diff --git a/.golangci.yml b/.golangci.yml index 88c5abe4..242ef1df 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -5,4 +5,22 @@ linters: - golint - staticcheck - vet + - unused + - gosimple + - stylecheck + - structcheck + - varcheck + - interfacer + - unconvert + - ineffassign + - deadcode + - gocyclo + - typecheck + - depguard + - unparam + - nakedret + - gofmt + - misspell + - prealloc + - goconst disable-all: true diff --git a/README.md b/README.md index a01eb61e..ba72281d 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ -# Go SecretHub +# Go client for SecretHub + [![GoDoc](http://img.shields.io/badge/godoc-reference-blue.svg)][godoc] [![Travis CI](https://travis-ci.org/secrethub/secrethub-go.svg?branch=master)][travis-ci] [![GolangCI](https://golangci.com/badges/github.com/secrethub/secrethub-go.svg)][golang-ci] [![Go Report Card](https://goreportcard.com/badge/github.com/secrethub/secrethub-go)][goreportcard] -The official [SecretHub][secrethub] Go client library. +[SecretHub][secrethub] is a developer tool to help you keep database passwords, API tokens, and other secrets out of IT automation scripts. -> SecretHub is a developer tool to help you keep database passwords, API tokens, and other secrets out of IT automation scripts. +`secrethub-go` provides a client for various SecretHub APIs. -Gopher +Gopher ## Installation diff --git a/internals/api/user.go b/internals/api/user.go index 23a39344..f6def74e 100644 --- a/internals/api/user.go +++ b/internals/api/user.go @@ -39,13 +39,14 @@ var ( // User represents a SecretHub user. type User struct { - AccountID *uuid.UUID `json:"account_id"` - PublicKey []byte `json:"public_key"` - Username string `json:"username"` - FullName string `json:"full_name"` - Email string `json:"user_email,omitempty"` // Optional, private information is only returned for yourself - CreatedAt *time.Time `json:"created_at,omitempty"` // Optional, private information is only returned for yourself - LastLoginAt *time.Time `json:"last_login_at,omitempty"` // Optional, private information is only returned for yourself + AccountID *uuid.UUID `json:"account_id"` + PublicKey []byte `json:"public_key"` + Username string `json:"username"` + FullName string `json:"full_name"` + Email string `json:"user_email,omitempty"` // Optional, private information is only returned for yourself + EmailVerified bool `json:"email_verified,omitempty"` // Optional, private information is only returned for yourself + CreatedAt *time.Time `json:"created_at,omitempty"` // Optional, private information is only returned for yourself + LastLoginAt *time.Time `json:"last_login_at,omitempty"` // Optional, private information is only returned for yourself } // PrettyName returns a printable string with the username and full name. diff --git a/internals/crypto/ciphertext.go b/internals/crypto/ciphertext.go index 21853c04..b952bb42 100644 --- a/internals/crypto/ciphertext.go +++ b/internals/crypto/ciphertext.go @@ -100,25 +100,26 @@ func (ec encodedCiphertext) metadata() (encodedCiphertextMetadata, error) { // newEncodedCiphertextMetadata creates a new encodedCiphertextMetadata from a map of metadata. // Input of {"param": "foo", "second": "bar"} outputs "param=foo,second=bar". -func newEncodedCiphertextMetadata(metadataList map[string]string) encodedCiphertextMetadata { - metadata := "" - +func newEncodedCiphertextMetadata(metadata map[string]string) encodedCiphertextMetadata { // Sort all the keys of the metadataList so that metadata is always in alphabetical order. - var keys []string - for k := range metadataList { - keys = append(keys, k) + keys := make([]string, len(metadata)) + i := 0 + for k := range metadata { + keys[i] = k + i++ } sort.Strings(keys) + res := "" for _, k := range keys { separator := "" - if len(metadata) > 0 { + if len(res) > 0 { separator = "," } - metadata = fmt.Sprintf("%s%s%s=%s", metadata, separator, k, metadataList[k]) + res = fmt.Sprintf("%s%s%s=%s", res, separator, k, metadata[k]) } - return encodedCiphertextMetadata(metadata) + return encodedCiphertextMetadata(res) } // getValue returns a value from metadata. diff --git a/internals/crypto/ciphertext_test.go b/internals/crypto/ciphertext_test.go index 8378c415..a455fc45 100644 --- a/internals/crypto/ciphertext_test.go +++ b/internals/crypto/ciphertext_test.go @@ -36,7 +36,7 @@ func TestRSAAES_Success(t *testing.T) { t.Fatal(err) } - if bytes.Equal(ciphertext.aes.Data, input) { + if bytes.Equal(ciphertext.AES.Data, input) { t.Error("encrypted data equals the original data") } diff --git a/internals/crypto/rsa.go b/internals/crypto/rsa.go index 4419ef81..0d79d335 100644 --- a/internals/crypto/rsa.go +++ b/internals/crypto/rsa.go @@ -73,8 +73,8 @@ func (pub RSAPublicKey) Encrypt(data []byte) (CiphertextRSAAES, error) { } return CiphertextRSAAES{ - aes: aesData, - rsa: rsaData, + AES: aesData, + RSA: rsaData, }, nil } @@ -229,12 +229,12 @@ func NewRSAPrivateKey(privateKey *rsa.PrivateKey) RSAPrivateKey { // then uses the decrypted symmetric key to decrypt the rest of the ciphertext // with the AES-GCM algorithm. func (prv RSAPrivateKey) Decrypt(ciphertext CiphertextRSAAES) ([]byte, error) { - aesKeyData, err := prv.Unwrap(ciphertext.rsa) + aesKeyData, err := prv.Unwrap(ciphertext.RSA) if err != nil { return nil, err } - return NewSymmetricKey(aesKeyData).Decrypt(ciphertext.aes) + return NewSymmetricKey(aesKeyData).Decrypt(ciphertext.AES) } // Unwrap uses the private key to decrypt a small ciphertext that has been encrypted @@ -354,77 +354,92 @@ func (prv RSAPrivateKey) ExportPrivateKeyWithPassphrase(pass string) ([]byte, er // CiphertextRSAAES represents data encrypted with AES-GCM, where the AES key is encrypted with RSA-OAEP. type CiphertextRSAAES struct { - aes CiphertextAES - rsa CiphertextRSA + AES CiphertextAES + RSA CiphertextRSA } -// MarshalJSON encodes the ciphertext in a string. -func (ct CiphertextRSAAES) MarshalJSON() ([]byte, error) { - data := base64.StdEncoding.EncodeToString(ct.aes.Data) +// EncodeToString encodes the ciphertext in a string. +func (ct CiphertextRSAAES) EncodeToString() string { + data := base64.StdEncoding.EncodeToString(ct.AES.Data) metadata := newEncodedCiphertextMetadata(map[string]string{ - "nonce": base64.StdEncoding.EncodeToString(ct.aes.Nonce), - "key": base64.StdEncoding.EncodeToString(ct.rsa.Data), + "nonce": base64.StdEncoding.EncodeToString(ct.AES.Nonce), + "key": base64.StdEncoding.EncodeToString(ct.RSA.Data), }) - return json.Marshal(fmt.Sprintf("%s$%s$%s", algorithmRSAAES, data, metadata)) + return fmt.Sprintf("%s$%s$%s", algorithmRSAAES, data, metadata) } -// UnmarshalJSON decodes a string into a ciphertext. -func (ct *CiphertextRSAAES) UnmarshalJSON(b []byte) error { - var s string - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - - if s == "" { - return nil - } +// MarshalJSON encodes the ciphertext in JSON. +func (ct CiphertextRSAAES) MarshalJSON() ([]byte, error) { + return json.Marshal(ct.EncodeToString()) +} +// DecodeCiphertextRSAAESFromString decodes an encoded ciphertext string to an CiphertextRSAAES. +func DecodeCiphertextRSAAESFromString(s string) (CiphertextRSAAES, error) { encoded, err := newEncodedCiphertext(s) if err != nil { - return err + return CiphertextRSAAES{}, err } algorithm, err := encoded.algorithm() if err != nil { - return errio.Error(err) + return CiphertextRSAAES{}, errio.Error(err) } if algorithm != algorithmRSAAES { - return ErrWrongAlgorithm + return CiphertextRSAAES{}, ErrWrongAlgorithm } encryptedData, err := encoded.data() if err != nil { - return errio.Error(err) + return CiphertextRSAAES{}, errio.Error(err) } metadata, err := encoded.metadata() if err != nil { - return errio.Error(err) + return CiphertextRSAAES{}, errio.Error(err) } aesNonce, err := metadata.getDecodedValue("nonce") if err != nil { - return errio.Error(err) + return CiphertextRSAAES{}, errio.Error(err) } aesKey, err := metadata.getDecodedValue("key") if err != nil { - return errio.Error(err) + return CiphertextRSAAES{}, errio.Error(err) } - ct.aes = CiphertextAES{ - Data: encryptedData, - Nonce: aesNonce, + return CiphertextRSAAES{ + AES: CiphertextAES{ + Data: encryptedData, + Nonce: aesNonce, + }, + RSA: CiphertextRSA{ + Data: aesKey, + }, + }, nil +} + +// UnmarshalJSON decodes JSON into a ciphertext. +func (ct *CiphertextRSAAES) UnmarshalJSON(b []byte) error { + if len(b) == 0 { + return nil } - ct.rsa = CiphertextRSA{ - Data: aesKey, + var s string + err := json.Unmarshal(b, &s) + if err != nil { + return err } + ciphertext, err := DecodeCiphertextRSAAESFromString(s) + if err != nil { + return err + } + + *ct = ciphertext return nil } @@ -433,45 +448,60 @@ type CiphertextRSA struct { Data []byte } -// MarshalJSON encodes the ciphertext in a string. -func (ct CiphertextRSA) MarshalJSON() ([]byte, error) { +// EncodeToString encodes the ciphertext in a string. +func (ct CiphertextRSA) EncodeToString() string { encodedKey := base64.StdEncoding.EncodeToString(ct.Data) - - return json.Marshal(fmt.Sprintf("%s$%s$", algorithmRSA, encodedKey)) + return fmt.Sprintf("%s$%s$", algorithmRSA, encodedKey) } -// UnmarshalJSON decodes a string into a ciphertext. -func (ct *CiphertextRSA) UnmarshalJSON(b []byte) error { - var s string - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - - if s == "" { - return nil - } +// MarshalJSON encodes the ciphertext in JSON. +func (ct CiphertextRSA) MarshalJSON() ([]byte, error) { + return json.Marshal(ct.EncodeToString()) +} +// DecodeCiphertextRSAFromString decodes an encoded ciphertext string to an CiphertextRSA. +func DecodeCiphertextRSAFromString(s string) (CiphertextRSA, error) { encoded, err := newEncodedCiphertext(s) if err != nil { - return err + return CiphertextRSA{}, err } algorithm, err := encoded.algorithm() if err != nil { - return errio.Error(err) + return CiphertextRSA{}, errio.Error(err) } if algorithm != algorithmRSA { - return ErrWrongAlgorithm + return CiphertextRSA{}, ErrWrongAlgorithm } encryptedData, err := encoded.data() if err != nil { - return errio.Error(err) + return CiphertextRSA{}, errio.Error(err) } - ct.Data = encryptedData + return CiphertextRSA{ + Data: encryptedData, + }, nil +} + +// UnmarshalJSON decodes JSON into a ciphertext. +func (ct *CiphertextRSA) UnmarshalJSON(b []byte) error { + if len(b) == 0 { + return nil + } + + var s string + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + ciphertext, err := DecodeCiphertextRSAFromString(s) + if err != nil { + return err + } + *ct = ciphertext return nil } diff --git a/internals/crypto/rsa_test.go b/internals/crypto/rsa_test.go index 2341d511..1b906610 100644 --- a/internals/crypto/rsa_test.go +++ b/internals/crypto/rsa_test.go @@ -233,7 +233,15 @@ func TestCiphertextRSA_MarshalJSON(t *testing.T) { } for name, tc := range cases { - t.Run(name, func(t *testing.T) { + t.Run(name+" encoded", func(t *testing.T) { + // Act + actual := tc.ciphertext.EncodeToString() + + // Assert + assert.Equal(t, actual, tc.expected) + }) + + t.Run(name+" json", func(t *testing.T) { // Act actual, err := tc.ciphertext.MarshalJSON() assert.OK(t, err) diff --git a/internals/crypto/scrypt.go b/internals/crypto/scrypt.go index abe8dd38..4e2e7897 100644 --- a/internals/crypto/scrypt.go +++ b/internals/crypto/scrypt.go @@ -33,7 +33,7 @@ const ( // Read more about the parameters, how they work and how to determine their values // in this blog post: https://blog.filippo.io/the-scrypt-parameters/ // - // Also, this the value recommented in the official GoDocs. + // Also, this the value recommended in the official GoDocs. DefaultScryptN = 1 << 15 // DefaultScryptR determines the sequential read size of the scrypt @@ -44,7 +44,7 @@ const ( // different memory characteristics. Use the N parameter instead to // increase or decrease work. // - // The value has been set to 8, which is the value recommented in + // The value has been set to 8, which is the value recommended in // the official GoDocs. DefaultScryptR = 8 @@ -54,7 +54,7 @@ const ( // be used to decrease the wall-clock-time of the key derivation // function. Use the N parameter for that. // - // The value has been set to 1, which is the value recommented in + // The value has been set to 1, which is the value recommended in // the official GoDocs. DefaultScryptP = 1 ) diff --git a/internals/crypto/scrypt_test.go b/internals/crypto/scrypt_test.go index dab7483a..bf02e37a 100644 --- a/internals/crypto/scrypt_test.go +++ b/internals/crypto/scrypt_test.go @@ -321,7 +321,7 @@ func TestIsPowerOfTwo(t *testing.T) { } } -// Below we test the assumption that increasing the salt lenght does +// Below we test the assumption that increasing the salt length does // not significantly increase the execution time of key derivation // function. The output of the benchmarks is documented below: // diff --git a/internals/crypto/symmetric.go b/internals/crypto/symmetric.go index 09772d4d..56536600 100644 --- a/internals/crypto/symmetric.go +++ b/internals/crypto/symmetric.go @@ -143,62 +143,79 @@ type CiphertextAES struct { Nonce []byte } -// MarshalJSON encodes the ciphertext in a string. -func (ct CiphertextAES) MarshalJSON() ([]byte, error) { +// EncodeToString encodes the ciphertext in a string. +func (ct CiphertextAES) EncodeToString() string { data := base64.StdEncoding.EncodeToString(ct.Data) metadata := newEncodedCiphertextMetadata(map[string]string{ "nonce": base64.StdEncoding.EncodeToString(ct.Nonce), }) - return json.Marshal(fmt.Sprintf("%s$%s$%s", algorithmAES, data, metadata)) + return fmt.Sprintf("%s$%s$%s", algorithmAES, data, metadata) } -// UnmarshalJSON decodes a string into a ciphertext. -func (ct *CiphertextAES) UnmarshalJSON(b []byte) error { - var s string - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - - if s == "" { - return nil - } +// MarshalJSON encodes the ciphertext in JSON. +func (ct CiphertextAES) MarshalJSON() ([]byte, error) { + return json.Marshal(ct.EncodeToString()) +} +// DecodeCiphertextAESFromString decodes an encoded ciphertext string to an CiphertextAES. +func DecodeCiphertextAESFromString(s string) (CiphertextAES, error) { encoded, err := newEncodedCiphertext(s) if err != nil { - return err + return CiphertextAES{}, err } algorithm, err := encoded.algorithm() if err != nil { - return errio.Error(err) + return CiphertextAES{}, errio.Error(err) } if algorithm != algorithmAES { - return ErrWrongAlgorithm + return CiphertextAES{}, ErrWrongAlgorithm } encryptedData, err := encoded.data() if err != nil { - return errio.Error(err) + return CiphertextAES{}, errio.Error(err) } metadata, err := encoded.metadata() if err != nil { - return errio.Error(err) + return CiphertextAES{}, errio.Error(err) } aesNonce, err := metadata.getDecodedValue("nonce") if err != nil { - return errio.Error(err) + return CiphertextAES{}, errio.Error(err) } - ct.Data = encryptedData - ct.Nonce = aesNonce + return CiphertextAES{ + Data: encryptedData, + Nonce: aesNonce, + }, nil +} +// UnmarshalJSON decodes JSON into a ciphertext. +func (ct *CiphertextAES) UnmarshalJSON(b []byte) error { + if len(b) == 0 { + return nil + } + + var s string + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + ciphertext, err := DecodeCiphertextAESFromString(s) + if err != nil { + return err + } + + *ct = ciphertext return nil + } // generateNonce generates a nonce of a given length. diff --git a/internals/crypto/symmetric_test.go b/internals/crypto/symmetric_test.go index b3ba4afc..7bd05883 100644 --- a/internals/crypto/symmetric_test.go +++ b/internals/crypto/symmetric_test.go @@ -52,7 +52,7 @@ func TestSymmetricKey_HMAC(t *testing.T) { } } -func TestCiphertextAES_MarshallJSON(t *testing.T) { +func TestCiphertextAES_MarshalJSON(t *testing.T) { cases := map[string]struct { ciphertext CiphertextAES expected string @@ -67,7 +67,15 @@ func TestCiphertextAES_MarshallJSON(t *testing.T) { } for name, tc := range cases { - t.Run(name, func(t *testing.T) { + t.Run(name+" encoded", func(t *testing.T) { + // Act + actual := tc.ciphertext.EncodeToString() + + // Assert + assert.Equal(t, actual, tc.expected) + }) + + t.Run(name+" json", func(t *testing.T) { // Act actual, err := tc.ciphertext.MarshalJSON() assert.OK(t, err) @@ -87,11 +95,11 @@ func TestCiphertextRSAAES_MarshalJSON(t *testing.T) { }{ "success": { ciphertext: CiphertextRSAAES{ - aes: CiphertextAES{ + AES: CiphertextAES{ Data: []byte("aes_data"), Nonce: []byte("nonce_data"), }, - rsa: CiphertextRSA{ + RSA: CiphertextRSA{ Data: []byte("rsa_data"), }, }, @@ -100,7 +108,15 @@ func TestCiphertextRSAAES_MarshalJSON(t *testing.T) { } for name, tc := range cases { - t.Run(name, func(t *testing.T) { + t.Run(name+" encoded", func(t *testing.T) { + // Act + actual := tc.ciphertext.EncodeToString() + + // Assert + assert.Equal(t, actual, tc.expected) + }) + + t.Run(name+" json", func(t *testing.T) { // Act actual, err := tc.ciphertext.MarshalJSON() assert.OK(t, err) diff --git a/pkg/secrethub/account.go b/pkg/secrethub/account.go index 6c0dc2bf..75280959 100644 --- a/pkg/secrethub/account.go +++ b/pkg/secrethub/account.go @@ -42,7 +42,7 @@ func (s accountService) Keys() AccountKeyService { // createAccountKey creates a new intermediate key wrapped in the supplied credential. // The public key of the intermediate key is returned. // The intermediate key is returned in an CreateAccountKeyRequest ready to be sent to the API. -// If an error has occured, it will be returned and the other result should be considered invalid. +// If an error has occurred, it will be returned and the other result should be considered invalid. func (c *client) createAccountKeyRequest(credential Credential, accountKey crypto.RSAPrivateKey) (*api.CreateAccountKeyRequest, error) { publicAccountKey, err := accountKey.Public().Export() if err != nil { diff --git a/pkg/secrethub/client.go b/pkg/secrethub/client.go index c55abd10..b918d91d 100644 --- a/pkg/secrethub/client.go +++ b/pkg/secrethub/client.go @@ -11,6 +11,7 @@ type Client interface { AccessRules() AccessRuleService Accounts() AccountService Dirs() DirService + Me() MeService Orgs() OrgService Repos() RepoService Secrets() SecretService @@ -45,6 +46,11 @@ func (c clientAdapter) Dirs() DirService { return newDirService(c.client) } +// Me returns a MeService. +func (c clientAdapter) Me() MeService { + return newMeService(c.client) +} + // Orgs returns an OrgService. func (c clientAdapter) Orgs() OrgService { return newOrgService(c.client) diff --git a/pkg/secrethub/fakeclient/client.go b/pkg/secrethub/fakeclient/client.go index 9994c660..e146e88a 100644 --- a/pkg/secrethub/fakeclient/client.go +++ b/pkg/secrethub/fakeclient/client.go @@ -29,6 +29,11 @@ func (c Client) Dirs() secrethub.DirService { return c.DirService } +// Me implements the secrethub.Client interface. +func (c Client) Me() secrethub.MeService { + return nil +} + // Orgs implements the secrethub.Client interface. func (c Client) Orgs() secrethub.OrgService { return c.OrgService diff --git a/pkg/secrethub/http.go b/pkg/secrethub/http.go index fda48df1..af2f8518 100644 --- a/pkg/secrethub/http.go +++ b/pkg/secrethub/http.go @@ -22,9 +22,10 @@ const ( baseURLPath = "/v1" // Current account - pathMeUser = "%s/me/user" - pathMeRepos = "%s/me/repos" - pathMeKey = "%s/me/key" + pathMeUser = "%s/me/user" + pathMeRepos = "%s/me/repos" + pathMeKey = "%s/me/key" + pathMeEmailVerification = "%s/me/user/verification-email" // Account pathAccount = "%s/account/%s" @@ -153,6 +154,13 @@ func (c *httpClient) GetMyUser() (*api.User, error) { return out, errio.Error(err) } +// SendVerificationEmail sends an email to the users registered email address for them to prove they +// own that email address. +func (c *httpClient) SendVerificationEmail() error { + rawURL := fmt.Sprintf(pathMeEmailVerification, c.base) + return c.post(rawURL, http.StatusCreated, nil, nil) +} + // Accounts // GetAccount returns the account for a name diff --git a/pkg/secrethub/me.go b/pkg/secrethub/me.go new file mode 100644 index 00000000..174cec06 --- /dev/null +++ b/pkg/secrethub/me.go @@ -0,0 +1,44 @@ +package secrethub + +import "github.com/secrethub/secrethub-go/internals/api" + +// MeService handles operations on the authenticated account. +type MeService interface { + // ListRepos retrieves all repositories of the current user. + ListRepos() ([]*api.Repo, error) + // GetUser retrieves the current users details. + GetUser() (*api.User, error) + // SendVerificationEmail sends an email to the authenticated user's registered email address + // for them to prove they own that email address. + SendVerificationEmail() error +} + +type meService struct { + client client + repoService RepoService + userService UserService +} + +func newMeService(client client) MeService { + return meService{ + client: client, + repoService: newRepoService(client), + userService: newUserService(client), + } +} + +// ListRepos retrieves all repositories of the current user. +func (ms meService) ListRepos() ([]*api.Repo, error) { + return ms.repoService.ListMine() +} + +// GetUser retrieves the current users details. +func (ms meService) GetUser() (*api.User, error) { + return ms.userService.Me() +} + +// SendVerificationEmail sends an email to the authenticated user's registered email address +// for them to prove they own that email address. +func (ms meService) SendVerificationEmail() error { + return ms.client.httpClient.SendVerificationEmail() +} diff --git a/pkg/secrethub/org_test.go b/pkg/secrethub/org_test.go index a592d2f0..86f107b8 100644 --- a/pkg/secrethub/org_test.go +++ b/pkg/secrethub/org_test.go @@ -38,7 +38,7 @@ func TestCreateOrg(t *testing.T) { Description: descr, CreatedAt: now, Members: []*api.OrgMember{ - &api.OrgMember{ + { OrgID: orgID, AccountID: accountID, Role: "admin", diff --git a/pkg/secrethub/user_test.go b/pkg/secrethub/user_test.go index 78ab7b12..7396ee61 100644 --- a/pkg/secrethub/user_test.go +++ b/pkg/secrethub/user_test.go @@ -15,6 +15,12 @@ import ( "github.com/secrethub/secrethub-go/internals/crypto" ) +const ( + username = "dev1" + fullName = "Developer Uno" + email = "dev1@testing.com" +) + func TestSignup(t *testing.T) { // Arrange @@ -25,10 +31,6 @@ func TestSignup(t *testing.T) { client: newClient(cred1, opts), } - username := "dev1" - fullName := "Developer Uno" - email := "dev1@testing.com" - expectedCreateUserRequest := api.CreateUserRequest{ Username: username, FullName: fullName, @@ -155,10 +157,6 @@ func TestGetUser(t *testing.T) { newClient(cred1, opts), ) - username := "dev1" - fullName := "Developer Uno" - email := "dev1@testing.com" - now := time.Now().UTC() expectedResponse := &api.User{ AccountID: uuid.New(), @@ -241,10 +239,6 @@ func TestGetMyUser(t *testing.T) { newClient(cred1, opts), ) - username := "dev1" - fullName := "Developer Uno" - email := "dev1@testing.com" - now := time.Now().UTC() expected := &api.User{ AccountID: uuid.New(),