Skip to content

Commit 7a3012c

Browse files
authored
[ARCH-342] Add Rename function. (#1694)
1 parent 8b842a2 commit 7a3012c

2 files changed

Lines changed: 141 additions & 0 deletions

File tree

keystore/admin.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ type SetMetadataUpdate struct {
9292

9393
type SetMetadataResponse struct{}
9494

95+
type RenameKeyRequest struct {
96+
OldName string
97+
NewName string
98+
}
99+
100+
type RenameKeyResponse struct{}
101+
95102
type Admin interface {
96103
// CreateKeys creates multiple keys in a single atomic operation.
97104
// The response preserves the order of the keys in the request.
@@ -118,6 +125,12 @@ type Admin interface {
118125
// SetMetadata updates metadata for multiple keys in a single atomic operation.
119126
// Returns ErrKeyNotFound if any key does not exist.
120127
SetMetadata(ctx context.Context, req SetMetadataRequest) (SetMetadataResponse, error)
128+
129+
// RenameKey renames an existing key.
130+
// Returns ErrKeyNotFound if the old key name does not exist,
131+
// ErrKeyAlreadyExists if the new key name exists,
132+
// ErrInvalidKeyName if the new key name is invalid.
133+
RenameKey(ctx context.Context, req RenameKeyRequest) (RenameKeyResponse, error)
121134
}
122135

123136
// UnimplementedAdmin returns ErrUnimplemented for all Admin methods.
@@ -143,6 +156,10 @@ func (UnimplementedAdmin) SetMetadata(ctx context.Context, req SetMetadataReques
143156
return SetMetadataResponse{}, fmt.Errorf("Admin.SetMetadata: %w", ErrUnimplemented)
144157
}
145158

159+
func (UnimplementedAdmin) RenameKey(ctx context.Context, req RenameKeyRequest) (RenameKeyResponse, error) {
160+
return RenameKeyResponse{}, fmt.Errorf("Admin.RenameKey: %w", ErrUnimplemented)
161+
}
162+
146163
func ValidKeyName(name string) error {
147164
if name == "" {
148165
return fmt.Errorf("key name cannot be empty")
@@ -364,3 +381,36 @@ func (ks *keystore) SetMetadata(ctx context.Context, req SetMetadataRequest) (Se
364381
ks.keystore = ksCopy
365382
return SetMetadataResponse{}, nil
366383
}
384+
385+
func (ks *keystore) RenameKey(ctx context.Context, req RenameKeyRequest) (RenameKeyResponse, error) {
386+
ks.mu.Lock()
387+
defer ks.mu.Unlock()
388+
389+
if req.NewName == req.OldName {
390+
return RenameKeyResponse{}, nil
391+
}
392+
393+
if err := ValidKeyName(req.NewName); err != nil {
394+
return RenameKeyResponse{}, fmt.Errorf("%w: %s", ErrInvalidKeyName, err)
395+
}
396+
397+
if _, ok := ks.keystore[req.NewName]; ok {
398+
return RenameKeyResponse{}, fmt.Errorf("%w: %s", ErrKeyAlreadyExists, req.NewName)
399+
}
400+
401+
k, ok := ks.keystore[req.OldName]
402+
if !ok {
403+
return RenameKeyResponse{}, fmt.Errorf("%w: %s", ErrKeyNotFound, req.OldName)
404+
}
405+
406+
ksCopy := maps.Clone(ks.keystore)
407+
ksCopy[req.NewName] = k
408+
delete(ksCopy, req.OldName)
409+
410+
if err := ks.save(ctx, ksCopy); err != nil {
411+
return RenameKeyResponse{}, fmt.Errorf("failed to save keystore: %w", err)
412+
}
413+
414+
ks.keystore = ksCopy
415+
return RenameKeyResponse{}, nil
416+
}

keystore/admin_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,3 +320,94 @@ func TestKeystore_SetMetadata(t *testing.T) {
320320
require.ErrorIs(t, err, keystore.ErrKeyNotFound)
321321
})
322322
}
323+
324+
func TestKeystore_RenameKey(t *testing.T) {
325+
ctx := t.Context()
326+
ks, err := keystore.LoadKeystore(ctx, keystore.NewMemoryStorage(), "ks")
327+
require.NoError(t, err)
328+
_, err = ks.CreateKeys(ctx, keystore.CreateKeysRequest{
329+
Keys: []keystore.CreateKeyRequest{
330+
{KeyName: "key1", KeyType: keystore.Ed25519},
331+
},
332+
})
333+
require.NoError(t, err)
334+
originalKey, err := ks.GetKeys(ctx, keystore.GetKeysRequest{
335+
KeyNames: []string{"key1"},
336+
})
337+
require.NoError(t, err)
338+
require.Len(t, originalKey.Keys, 1)
339+
340+
t.Run("rename non-existent key", func(t *testing.T) {
341+
_, err = ks.RenameKey(ctx, keystore.RenameKeyRequest{
342+
OldName: "key2",
343+
NewName: "new-name",
344+
})
345+
require.ErrorIs(t, err, keystore.ErrKeyNotFound)
346+
})
347+
348+
t.Run("rename to invalid name", func(t *testing.T) {
349+
_, err = ks.RenameKey(ctx, keystore.RenameKeyRequest{
350+
OldName: "key1",
351+
NewName: "", // Empty name is invalid
352+
})
353+
require.ErrorIs(t, err, keystore.ErrInvalidKeyName)
354+
})
355+
356+
t.Run("rename to existing name", func(t *testing.T) {
357+
// Create another key
358+
_, err = ks.CreateKeys(ctx, keystore.CreateKeysRequest{
359+
Keys: []keystore.CreateKeyRequest{
360+
{KeyName: "another", KeyType: keystore.Ed25519},
361+
},
362+
})
363+
require.NoError(t, err)
364+
365+
_, err = ks.RenameKey(ctx, keystore.RenameKeyRequest{
366+
OldName: "key1",
367+
NewName: "another", // Name already exists
368+
})
369+
require.ErrorIs(t, err, keystore.ErrKeyAlreadyExists)
370+
})
371+
372+
t.Run("rename to same name", func(t *testing.T) {
373+
_, err = ks.RenameKey(ctx, keystore.RenameKeyRequest{
374+
OldName: "key1",
375+
NewName: "key1",
376+
})
377+
require.NoError(t, err)
378+
379+
// Verify the key still exists
380+
resp, err := ks.GetKeys(ctx, keystore.GetKeysRequest{
381+
KeyNames: []string{"key1"},
382+
})
383+
require.NoError(t, err)
384+
require.Equal(t, resp, originalKey)
385+
})
386+
387+
t.Run("successful rename", func(t *testing.T) {
388+
// Rename the key
389+
_, err = ks.RenameKey(ctx, keystore.RenameKeyRequest{
390+
OldName: "key1",
391+
NewName: "renamed",
392+
})
393+
require.NoError(t, err)
394+
395+
// Verify the key exists under new name
396+
resp, err := ks.GetKeys(ctx, keystore.GetKeysRequest{
397+
KeyNames: []string{"renamed"},
398+
})
399+
require.NoError(t, err)
400+
require.Len(t, resp.Keys, 1)
401+
require.Equal(t, resp.Keys[0].KeyInfo.Name, "renamed")
402+
403+
// set name to the old one for easier comparison
404+
resp.Keys[0].KeyInfo.Name = "key1"
405+
assert.Equal(t, resp, originalKey)
406+
407+
// Verify the old name no longer exists
408+
resp, err = ks.GetKeys(ctx, keystore.GetKeysRequest{
409+
KeyNames: []string{"key1"},
410+
})
411+
require.EqualError(t, err, "key not found: key1")
412+
})
413+
}

0 commit comments

Comments
 (0)