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.
-
+
## 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(),