Skip to content
This repository was archived by the owner on Feb 16, 2023. It is now read-only.

Commit b0900a4

Browse files
Merge pull request #239 from secrethub/release/v0.33.0
Release v0.33.0
2 parents 7b81fd9 + 848b061 commit b0900a4

6 files changed

Lines changed: 228 additions & 25 deletions

File tree

pkg/secrethub/account.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,25 @@ package secrethub
22

33
import (
44
"github.com/secrethub/secrethub-go/internals/api"
5+
"github.com/secrethub/secrethub-go/internals/api/uuid"
56
"github.com/secrethub/secrethub-go/internals/crypto"
67
"github.com/secrethub/secrethub-go/internals/errio"
78
"github.com/secrethub/secrethub-go/pkg/secrethub/credentials"
89
)
910

1011
// Errors
1112
var (
12-
ErrNoDecryptionKey = errClient.Code("no_decryption_key").Error("client is not initialized with a method to decrypt the account key")
13+
ErrNoDecryptionKey = errClient.Code("no_decryption_key").Error("client is not initialized with a method to decrypt the account key")
14+
ErrIncorrectAccountID = errClient.Code("incorrect_account_id").Error("the incorrect account ID was provided. To delete the currently authenticated account please provide its ID.")
1315
)
1416

1517
// AccountService handles operations on SecretHub accounts.
1618
type AccountService interface {
1719
// Me retrieves the authenticated account of the client.
1820
Me() (*api.Account, error)
21+
// Delete deletes the authenticated account of the client if it's ID is provided as a parameter.
22+
// Do not use this method. Account deletes should only be performed from the CLI.
23+
Delete(accountID uuid.UUID) error
1924
// Get retrieves an account by name.
2025
Get(name string) (*api.Account, error)
2126
// Keys returns an account key service.
@@ -37,6 +42,19 @@ func (s accountService) Me() (*api.Account, error) {
3742
return s.client.getMyAccount()
3843
}
3944

45+
// Delete deletes the authenticated account of the client if it's ID is provided as a parameter.
46+
// Do not use this method. Account deletes should only be performed from the CLI.
47+
func (s accountService) Delete(accountID uuid.UUID) error {
48+
account, err := s.client.getMyAccount()
49+
if err != nil {
50+
return err
51+
}
52+
if accountID != account.AccountID {
53+
return ErrIncorrectAccountID
54+
}
55+
return s.client.httpClient.DeleteMyAccount()
56+
}
57+
4058
// Get retrieves an account by name.
4159
func (s accountService) Get(name string) (*api.Account, error) {
4260
accountName, err := api.NewAccountName(name)

pkg/secrethub/acl.go

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
package secrethub
22

33
import (
4+
"context"
45
"fmt"
56

7+
"golang.org/x/sync/errgroup"
8+
9+
"github.com/secrethub/secrethub-go/internals/crypto"
10+
611
"github.com/secrethub/secrethub-go/internals/api"
712
"github.com/secrethub/secrethub-go/internals/api/uuid"
813
"github.com/secrethub/secrethub-go/internals/errio"
@@ -287,37 +292,49 @@ func (re *reencrypter) Add(blindName string) error {
287292
return err
288293
}
289294

295+
return re.reencrypt(encryptedTree, accountKey)
296+
}
297+
298+
func (re *reencrypter) reencrypt(encryptedTree *api.EncryptedTree, accountKey *crypto.RSAPrivateKey) error {
299+
errs, _ := errgroup.WithContext(context.Background())
300+
290301
for _, dir := range encryptedTree.Directories {
291-
_, ok := re.dirs[dir.DirID]
292-
if !ok {
293-
decrypted, err := dir.Decrypt(accountKey)
294-
if err != nil {
295-
return err
296-
}
297-
encrypted, err := re.client.encryptDirFor(decrypted, re.encryptFor)
298-
if err != nil {
299-
return err
302+
errs.Go(func() error {
303+
_, ok := re.dirs[dir.DirID]
304+
if !ok {
305+
decrypted, err := dir.Decrypt(accountKey)
306+
if err != nil {
307+
return err
308+
}
309+
encrypted, err := re.client.encryptDirFor(decrypted, re.encryptFor)
310+
if err != nil {
311+
return err
312+
}
313+
re.dirs[dir.DirID] = encrypted
300314
}
301-
re.dirs[dir.DirID] = encrypted
302-
}
315+
return nil
316+
})
303317
}
304318

305319
for _, secret := range encryptedTree.Secrets {
306-
_, ok := re.secrets[secret.SecretID]
307-
if !ok {
308-
decrypted, err := secret.Decrypt(accountKey)
309-
if err != nil {
310-
return err
320+
errs.Go(func() error {
321+
_, ok := re.secrets[secret.SecretID]
322+
if !ok {
323+
decrypted, err := secret.Decrypt(accountKey)
324+
if err != nil {
325+
return err
326+
}
327+
encrypted, err := re.client.encryptSecretFor(decrypted, re.encryptFor)
328+
if err != nil {
329+
return err
330+
}
331+
re.secrets[secret.SecretID] = encrypted
311332
}
312-
encrypted, err := re.client.encryptSecretFor(decrypted, re.encryptFor)
313-
if err != nil {
314-
return err
315-
}
316-
re.secrets[secret.SecretID] = encrypted
317-
}
333+
return nil
334+
})
318335
}
319336

320-
return nil
337+
return errs.Wait()
321338
}
322339

323340
func (re *reencrypter) Secrets() []api.SecretAccessRequest {

pkg/secrethub/acl_test.go

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
package secrethub
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/secrethub/secrethub-go/internals/api"
8+
"github.com/secrethub/secrethub-go/internals/api/uuid"
9+
"github.com/secrethub/secrethub-go/internals/assert"
10+
"github.com/secrethub/secrethub-go/internals/crypto"
11+
"github.com/secrethub/secrethub-go/pkg/secrethub/internals/http"
12+
)
13+
14+
func TestReencypter_reencrypt(t *testing.T) {
15+
fromPrivateKey, err := crypto.GenerateRSAPrivateKey(2048)
16+
assert.OK(t, err)
17+
18+
fromPublicKey := fromPrivateKey.Public()
19+
fromEncodedPublicKey, err := fromPublicKey.Encode()
20+
assert.OK(t, err)
21+
22+
forPrivateKey, err := crypto.GenerateRSAPrivateKey(2048)
23+
assert.OK(t, err)
24+
25+
forPublicKey := forPrivateKey.Public()
26+
forEncodedPublicKey, err := forPublicKey.Encode()
27+
assert.OK(t, err)
28+
29+
firstAccount := api.Account{
30+
AccountID: uuid.New(),
31+
Name: "first-user",
32+
PublicKey: fromEncodedPublicKey,
33+
AccountType: "",
34+
CreatedAt: time.Time{},
35+
}
36+
37+
secondAccount := api.Account{
38+
AccountID: uuid.New(),
39+
Name: "second-user",
40+
PublicKey: forEncodedPublicKey,
41+
AccountType: "",
42+
CreatedAt: time.Time{},
43+
}
44+
45+
fakeClient := Client{
46+
httpClient: &http.Client{},
47+
account: &secondAccount,
48+
accountKey: &forPrivateKey,
49+
}
50+
51+
cases := map[string]struct {
52+
dirs []*api.Dir
53+
secrets []*api.Secret
54+
}{
55+
"no directories": {
56+
dirs: nil,
57+
secrets: nil,
58+
},
59+
"one directory": {
60+
dirs: []*api.Dir{
61+
{
62+
DirID: uuid.New(),
63+
Name: "first-dir",
64+
},
65+
},
66+
secrets: nil,
67+
},
68+
"multiple directories": {
69+
dirs: []*api.Dir{
70+
{
71+
DirID: uuid.New(),
72+
Name: "first-dir",
73+
},
74+
{
75+
DirID: uuid.New(),
76+
Name: "second-dir",
77+
},
78+
{
79+
DirID: uuid.New(),
80+
Name: "third-dir",
81+
},
82+
{
83+
DirID: uuid.New(),
84+
Name: "fourth-dir",
85+
},
86+
{
87+
DirID: uuid.New(),
88+
Name: "fifth-dir",
89+
},
90+
},
91+
secrets: nil,
92+
},
93+
}
94+
for name, tc := range cases {
95+
t.Run(name, func(t *testing.T) {
96+
fakeReencrypter := reencrypter{
97+
dirs: make(map[uuid.UUID]api.EncryptedNameForNodeRequest),
98+
secrets: make(map[uuid.UUID]api.SecretAccessRequest),
99+
encryptFor: &secondAccount,
100+
client: &fakeClient,
101+
}
102+
103+
encryptedTree := createEncryptedTree(t, tc.dirs, tc.secrets, fakeReencrypter, firstAccount)
104+
err = fakeReencrypter.reencrypt(&encryptedTree, &fromPrivateKey)
105+
assert.OK(t, err)
106+
107+
count := 0
108+
for _, dir := range fakeReencrypter.dirs {
109+
assert.Equal(t, dir.AccountID, secondAccount.AccountID)
110+
decryptedDir, err := encryptedTree.Directories[tc.dirs[count].DirID].Decrypt(&fromPrivateKey)
111+
assert.OK(t, err)
112+
assert.Equal(t, tc.dirs[count].Name, decryptedDir.Name)
113+
count++
114+
}
115+
116+
//TODO: In order to test for secrets encryption and decryption, a refactoring of the Client is in order, as to be able to mock the HTTP Go client.
117+
count = 0
118+
for _, secret := range fakeReencrypter.secrets {
119+
assert.Equal(t, secret.Name.AccountID, secondAccount.AccountID)
120+
decryptedSecret, err := encryptedTree.Secrets[count].Decrypt(&fromPrivateKey)
121+
assert.OK(t, err)
122+
assert.Equal(t, tc.secrets[count].Name, decryptedSecret.Name)
123+
count++
124+
}
125+
})
126+
}
127+
}
128+
129+
func createEncryptedTree(t *testing.T, dirs []*api.Dir, secrets []*api.Secret, reencrypter reencrypter, account api.Account) api.EncryptedTree {
130+
encryptedTree := api.EncryptedTree{
131+
Directories: make(map[uuid.UUID]*api.EncryptedDir),
132+
Secrets: make([]*api.EncryptedSecret, len(secrets)),
133+
}
134+
for _, dir := range dirs {
135+
request, err := reencrypter.client.encryptDirFor(dir, &account)
136+
assert.OK(t, err)
137+
encryptedTree.Directories[dir.DirID] = &api.EncryptedDir{
138+
DirID: dir.DirID,
139+
EncryptedName: request.EncryptedName,
140+
}
141+
}
142+
143+
for i, secret := range secrets {
144+
request, err := reencrypter.client.encryptSecretFor(secret, &account)
145+
assert.OK(t, err)
146+
encryptedTree.Secrets[i] = &api.EncryptedSecret{
147+
DirID: secret.DirID,
148+
EncryptedName: request.Name.EncryptedName,
149+
}
150+
}
151+
152+
return encryptedTree
153+
}

pkg/secrethub/client_version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ package secrethub
22

33
// ClientVersion is the current version of the client
44
// Do not edit this unless you know what you're doing.
5-
const ClientVersion = "v0.32.0"
5+
const ClientVersion = "v0.33.0"

pkg/secrethub/fakeclient/account.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ package fakeclient
44

55
import (
66
"github.com/secrethub/secrethub-go/internals/api"
7+
"github.com/secrethub/secrethub-go/internals/api/uuid"
78
"github.com/secrethub/secrethub-go/pkg/secrethub"
89
)
910

1011
// AccountService is a mock of the AccountService interface.
1112
type AccountService struct {
1213
MeFunc func() (*api.Account, error)
14+
DeleteFunc func(accountID uuid.UUID) error
1315
GetFunc func(name string) (*api.Account, error)
1416
AccountKeyService secrethub.AccountKeyService
1517
}
@@ -23,6 +25,11 @@ func (s *AccountService) Get(name string) (*api.Account, error) {
2325
return s.GetFunc(name)
2426
}
2527

28+
// Delete implements the AccountService interface Delete function.
29+
func (s *AccountService) Delete(accountID uuid.UUID) error {
30+
return s.DeleteFunc(accountID)
31+
}
32+
2633
// Me implements the AccountService interface Me function.
2734
func (s *AccountService) Me() (*api.Account, error) {
2835
return s.MeFunc()

pkg/secrethub/internals/http/client.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const (
3737

3838
// Current account
3939
pathMeUser = "%s/me/user"
40+
pathMeAccount = "%s/me/account"
4041
pathMeRepos = "%s/me/repos"
4142
pathMeKey = "%s/me/key?key_version=v2"
4243
pathMeEmailVerification = "%s/me/user/verification-email"
@@ -173,6 +174,13 @@ func (c *Client) GetMyUser() (*api.User, error) {
173174
return out, errio.Error(err)
174175
}
175176

177+
// DeleteMyAccount
178+
func (c *Client) DeleteMyAccount() error {
179+
rawURL := fmt.Sprintf(pathMeAccount, c.base.String())
180+
err := c.delete(rawURL, true, nil)
181+
return errio.Error(err)
182+
}
183+
176184
// CreateCredential creates a new credential for the account.
177185
func (c *Client) CreateCredential(in *api.CreateCredentialRequest) (*api.Credential, error) {
178186
out := &api.Credential{}

0 commit comments

Comments
 (0)