mirror of https://github.com/portainer/portainer
fix(docs): convert APIKey to string EE-6199 (#10943)
parent
d750389c67
commit
488fcc7cc5
|
@ -6,11 +6,11 @@ import (
|
|||
|
||||
// APIKeyService represents a service for managing API keys.
|
||||
type APIKeyService interface {
|
||||
HashRaw(rawKey string) []byte
|
||||
HashRaw(rawKey string) string
|
||||
GenerateApiKey(user portainer.User, description string) (string, *portainer.APIKey, error)
|
||||
GetAPIKey(apiKeyID portainer.APIKeyID) (*portainer.APIKey, error)
|
||||
GetAPIKeys(userID portainer.UserID) ([]portainer.APIKey, error)
|
||||
GetDigestUserAndKey(digest []byte) (portainer.User, portainer.APIKey, error)
|
||||
GetDigestUserAndKey(digest string) (portainer.User, portainer.APIKey, error)
|
||||
UpdateAPIKey(apiKey *portainer.APIKey) error
|
||||
DeleteAPIKey(apiKeyID portainer.APIKeyID) error
|
||||
InvalidateUserKeyCache(userId portainer.UserID) bool
|
||||
|
|
|
@ -33,8 +33,8 @@ func NewAPIKeyCache(cacheSize int) *apiKeyCache {
|
|||
// Get returns the user/key associated to an api-key's digest
|
||||
// This is required because HTTP requests will contain the digest of the API key in header,
|
||||
// the digest value must be mapped to a portainer user.
|
||||
func (c *apiKeyCache) Get(digest []byte) (portainer.User, portainer.APIKey, bool) {
|
||||
val, ok := c.cache.Get(string(digest))
|
||||
func (c *apiKeyCache) Get(digest string) (portainer.User, portainer.APIKey, bool) {
|
||||
val, ok := c.cache.Get(digest)
|
||||
if !ok {
|
||||
return portainer.User{}, portainer.APIKey{}, false
|
||||
}
|
||||
|
@ -44,23 +44,23 @@ func (c *apiKeyCache) Get(digest []byte) (portainer.User, portainer.APIKey, bool
|
|||
}
|
||||
|
||||
// Set persists a user/key entry to the cache
|
||||
func (c *apiKeyCache) Set(digest []byte, user portainer.User, apiKey portainer.APIKey) {
|
||||
c.cache.Add(string(digest), entry{
|
||||
func (c *apiKeyCache) Set(digest string, user portainer.User, apiKey portainer.APIKey) {
|
||||
c.cache.Add(digest, entry{
|
||||
user: user,
|
||||
apiKey: apiKey,
|
||||
})
|
||||
}
|
||||
|
||||
// Delete evicts a digest's user/key entry key from the cache
|
||||
func (c *apiKeyCache) Delete(digest []byte) {
|
||||
c.cache.Remove(string(digest))
|
||||
func (c *apiKeyCache) Delete(digest string) {
|
||||
c.cache.Remove(digest)
|
||||
}
|
||||
|
||||
// InvalidateUserKeyCache loops through all the api-keys associated to a user and removes them from the cache
|
||||
func (c *apiKeyCache) InvalidateUserKeyCache(userId portainer.UserID) bool {
|
||||
present := false
|
||||
for _, k := range c.cache.Keys() {
|
||||
user, _, _ := c.Get([]byte(k.(string)))
|
||||
user, _, _ := c.Get(k.(string))
|
||||
if user.ID == userId {
|
||||
present = c.cache.Remove(k)
|
||||
}
|
||||
|
|
|
@ -17,19 +17,19 @@ func Test_apiKeyCacheGet(t *testing.T) {
|
|||
keyCache.cache.Add(string(""), entry{user: portainer.User{}, apiKey: portainer.APIKey{}})
|
||||
|
||||
tests := []struct {
|
||||
digest []byte
|
||||
digest string
|
||||
found bool
|
||||
}{
|
||||
{
|
||||
digest: []byte("foo"),
|
||||
digest: "foo",
|
||||
found: true,
|
||||
},
|
||||
{
|
||||
digest: []byte(""),
|
||||
digest: "",
|
||||
found: true,
|
||||
},
|
||||
{
|
||||
digest: []byte("bar"),
|
||||
digest: "bar",
|
||||
found: false,
|
||||
},
|
||||
}
|
||||
|
@ -48,11 +48,11 @@ func Test_apiKeyCacheSet(t *testing.T) {
|
|||
keyCache := NewAPIKeyCache(10)
|
||||
|
||||
// pre-populate cache
|
||||
keyCache.Set([]byte("bar"), portainer.User{ID: 2}, portainer.APIKey{})
|
||||
keyCache.Set([]byte("foo"), portainer.User{ID: 1}, portainer.APIKey{})
|
||||
keyCache.Set("bar", portainer.User{ID: 2}, portainer.APIKey{})
|
||||
keyCache.Set("foo", portainer.User{ID: 1}, portainer.APIKey{})
|
||||
|
||||
// overwrite existing entry
|
||||
keyCache.Set([]byte("foo"), portainer.User{ID: 3}, portainer.APIKey{})
|
||||
keyCache.Set("foo", portainer.User{ID: 3}, portainer.APIKey{})
|
||||
|
||||
val, ok := keyCache.cache.Get(string("bar"))
|
||||
is.True(ok)
|
||||
|
@ -74,14 +74,14 @@ func Test_apiKeyCacheDelete(t *testing.T) {
|
|||
|
||||
t.Run("Delete an existing entry", func(t *testing.T) {
|
||||
keyCache.cache.Add(string("foo"), entry{user: portainer.User{ID: 1}, apiKey: portainer.APIKey{}})
|
||||
keyCache.Delete([]byte("foo"))
|
||||
keyCache.Delete("foo")
|
||||
|
||||
_, ok := keyCache.cache.Get(string("foo"))
|
||||
is.False(ok)
|
||||
})
|
||||
|
||||
t.Run("Delete a non-existing entry", func(t *testing.T) {
|
||||
nonPanicFunc := func() { keyCache.Delete([]byte("non-existent-key")) }
|
||||
nonPanicFunc := func() { keyCache.Delete("non-existent-key") }
|
||||
is.NotPanics(nonPanicFunc)
|
||||
})
|
||||
}
|
||||
|
@ -131,16 +131,16 @@ func Test_apiKeyCacheLRU(t *testing.T) {
|
|||
keyCache := NewAPIKeyCache(test.cacheLen)
|
||||
|
||||
for _, key := range test.key {
|
||||
keyCache.Set([]byte(key), portainer.User{ID: 1}, portainer.APIKey{})
|
||||
keyCache.Set(key, portainer.User{ID: 1}, portainer.APIKey{})
|
||||
}
|
||||
|
||||
for _, key := range test.foundKeys {
|
||||
_, _, found := keyCache.Get([]byte(key))
|
||||
_, _, found := keyCache.Get(key)
|
||||
is.True(found, "Key %s not found", key)
|
||||
}
|
||||
|
||||
for _, key := range test.evictedKeys {
|
||||
_, _, found := keyCache.Get([]byte(key))
|
||||
_, _, found := keyCache.Get(key)
|
||||
is.False(found, "key %s should have been evicted", key)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -32,9 +32,9 @@ func NewAPIKeyService(apiKeyRepository dataservices.APIKeyRepository, userReposi
|
|||
}
|
||||
|
||||
// HashRaw computes a hash digest of provided raw API key.
|
||||
func (a *apiKeyService) HashRaw(rawKey string) []byte {
|
||||
func (a *apiKeyService) HashRaw(rawKey string) string {
|
||||
hashDigest := sha256.Sum256([]byte(rawKey))
|
||||
return hashDigest[:]
|
||||
return base64.StdEncoding.EncodeToString(hashDigest[:])
|
||||
}
|
||||
|
||||
// GenerateApiKey generates a raw API key for a user (for one-time display).
|
||||
|
@ -77,7 +77,7 @@ func (a *apiKeyService) GetAPIKeys(userID portainer.UserID) ([]portainer.APIKey,
|
|||
|
||||
// GetDigestUserAndKey returns the user and api-key associated to a specified hash digest.
|
||||
// A cache lookup is performed first; if the user/api-key is not found in the cache, respective database lookups are performed.
|
||||
func (a *apiKeyService) GetDigestUserAndKey(digest []byte) (portainer.User, portainer.APIKey, error) {
|
||||
func (a *apiKeyService) GetDigestUserAndKey(digest string) (portainer.User, portainer.APIKey, error) {
|
||||
// get api key from cache if possible
|
||||
cachedUser, cachedKey, ok := a.cache.Get(digest)
|
||||
if ok {
|
||||
|
|
|
@ -2,6 +2,7 @@ package apikey
|
|||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -68,7 +69,7 @@ func Test_GenerateApiKey(t *testing.T) {
|
|||
|
||||
generatedDigest := sha256.Sum256([]byte(rawKey))
|
||||
|
||||
is.Equal(apiKey.Digest, generatedDigest[:])
|
||||
is.Equal(apiKey.Digest, base64.StdEncoding.EncodeToString(generatedDigest[:]))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package apikeyrepository
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
|
@ -37,7 +36,7 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
|||
|
||||
// GetAPIKeysByUserID returns a slice containing all the APIKeys a user has access to.
|
||||
func (service *Service) GetAPIKeysByUserID(userID portainer.UserID) ([]portainer.APIKey, error) {
|
||||
var result = make([]portainer.APIKey, 0)
|
||||
result := make([]portainer.APIKey, 0)
|
||||
|
||||
err := service.Connection.GetAll(
|
||||
BucketName,
|
||||
|
@ -61,7 +60,7 @@ func (service *Service) GetAPIKeysByUserID(userID portainer.UserID) ([]portainer
|
|||
|
||||
// GetAPIKeyByDigest returns the API key for the associated digest.
|
||||
// Note: there is a 1-to-1 mapping of api-key and digest
|
||||
func (service *Service) GetAPIKeyByDigest(digest []byte) (*portainer.APIKey, error) {
|
||||
func (service *Service) GetAPIKeyByDigest(digest string) (*portainer.APIKey, error) {
|
||||
var k *portainer.APIKey
|
||||
stop := fmt.Errorf("ok")
|
||||
err := service.Connection.GetAll(
|
||||
|
@ -73,7 +72,7 @@ func (service *Service) GetAPIKeyByDigest(digest []byte) (*portainer.APIKey, err
|
|||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to APIKey object")
|
||||
return nil, fmt.Errorf("failed to convert to APIKey object: %s", obj)
|
||||
}
|
||||
if bytes.Equal(key.Digest, digest) {
|
||||
if key.Digest == digest {
|
||||
k = key
|
||||
return nil, stop
|
||||
}
|
||||
|
|
|
@ -152,7 +152,7 @@ type (
|
|||
APIKeyRepository interface {
|
||||
BaseCRUD[portainer.APIKey, portainer.APIKeyID]
|
||||
GetAPIKeysByUserID(userID portainer.UserID) ([]portainer.APIKey, error)
|
||||
GetAPIKeyByDigest(digest []byte) (*portainer.APIKey, error)
|
||||
GetAPIKeyByDigest(digest string) (*portainer.APIKey, error)
|
||||
}
|
||||
|
||||
// SettingsService represents a service for managing application settings
|
||||
|
|
|
@ -64,5 +64,5 @@ func (handler *Handler) userGetAccessTokens(w http.ResponseWriter, r *http.Reque
|
|||
|
||||
// hideAPIKeyFields remove the digest from the API key (it is not needed in the response)
|
||||
func hideAPIKeyFields(apiKey *portainer.APIKey) {
|
||||
apiKey.Digest = nil
|
||||
apiKey.Digest = ""
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ func Test_userGetAccessTokens(t *testing.T) {
|
|||
|
||||
is.Len(resp, 1)
|
||||
if len(resp) == 1 {
|
||||
is.Nil(resp[0].Digest)
|
||||
is.Equal(resp[0].Digest, "")
|
||||
is.Equal(apiKey.ID, resp[0].ID)
|
||||
is.Equal(apiKey.UserID, resp[0].UserID)
|
||||
is.Equal(apiKey.Prefix, resp[0].Prefix)
|
||||
|
@ -129,10 +129,10 @@ func Test_hideAPIKeyFields(t *testing.T) {
|
|||
UserID: 2,
|
||||
Prefix: "abc",
|
||||
Description: "test",
|
||||
Digest: nil,
|
||||
Digest: "",
|
||||
}
|
||||
|
||||
hideAPIKeyFields(apiKey)
|
||||
|
||||
is.Nil(apiKey.Digest, "digest should be cleared when hiding api key fields")
|
||||
is.Equal(apiKey.Digest, "", "digest should be cleared when hiding api key fields")
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ type (
|
|||
// Authorizations represents a set of authorizations associated to a role
|
||||
Authorizations map[Authorization]bool
|
||||
|
||||
//AutoUpdateSettings represents the git auto sync config for stack deployment
|
||||
// AutoUpdateSettings represents the git auto sync config for stack deployment
|
||||
AutoUpdateSettings struct {
|
||||
// Auto update interval
|
||||
Interval string `example:"1m30s"`
|
||||
|
@ -311,7 +311,7 @@ type (
|
|||
ConfigHash string `json:"ConfigHash"`
|
||||
}
|
||||
|
||||
//EdgeStack represents an edge stack
|
||||
// EdgeStack represents an edge stack
|
||||
EdgeStack struct {
|
||||
// EdgeStack Identifier
|
||||
ID EdgeStackID `json:"Id" example:"1"`
|
||||
|
@ -335,7 +335,7 @@ type (
|
|||
|
||||
EdgeStackDeploymentType int
|
||||
|
||||
//EdgeStackID represents an edge stack id
|
||||
// EdgeStackID represents an edge stack id
|
||||
EdgeStackID int
|
||||
|
||||
EdgeStackStatusDetails struct {
|
||||
|
@ -348,7 +348,7 @@ type (
|
|||
ImagesPulled bool
|
||||
}
|
||||
|
||||
//EdgeStackStatus represents an edge stack status
|
||||
// EdgeStackStatus represents an edge stack status
|
||||
EdgeStackStatus struct {
|
||||
Status []EdgeStackDeploymentStatus
|
||||
EndpointID EndpointID
|
||||
|
@ -372,7 +372,7 @@ type (
|
|||
RollbackTo *int
|
||||
}
|
||||
|
||||
//EdgeStackStatusType represents an edge stack status type
|
||||
// EdgeStackStatusType represents an edge stack status type
|
||||
EdgeStackStatusType int
|
||||
|
||||
PendingActionsID int
|
||||
|
@ -905,7 +905,7 @@ type (
|
|||
Prefix string `json:"prefix"` // API key identifier (7 char prefix)
|
||||
DateCreated int64 `json:"dateCreated"` // Unix timestamp (UTC) when the API key was created
|
||||
LastUsed int64 `json:"lastUsed"` // Unix timestamp (UTC) when the API key was last used
|
||||
Digest []byte `json:"digest,omitempty"` // Digest represents SHA256 hash of the raw API key
|
||||
Digest string `json:"digest,omitempty"` // Digest represents SHA256 hash of the raw API key
|
||||
}
|
||||
|
||||
// Schedule represents a scheduled job.
|
||||
|
@ -1657,7 +1657,7 @@ const (
|
|||
AuthenticationInternal
|
||||
// AuthenticationLDAP represents the LDAP authentication method (authentication against a LDAP server)
|
||||
AuthenticationLDAP
|
||||
//AuthenticationOAuth represents the OAuth authentication method (authentication against a authorization server)
|
||||
// AuthenticationOAuth represents the OAuth authentication method (authentication against a authorization server)
|
||||
AuthenticationOAuth
|
||||
)
|
||||
|
||||
|
@ -1697,13 +1697,13 @@ const (
|
|||
const (
|
||||
// EdgeStackStatusPending represents a pending edge stack
|
||||
EdgeStackStatusPending EdgeStackStatusType = iota
|
||||
//EdgeStackStatusDeploymentReceived represents an edge environment which received the edge stack deployment
|
||||
// EdgeStackStatusDeploymentReceived represents an edge environment which received the edge stack deployment
|
||||
EdgeStackStatusDeploymentReceived
|
||||
//EdgeStackStatusError represents an edge environment which failed to deploy its edge stack
|
||||
// EdgeStackStatusError represents an edge environment which failed to deploy its edge stack
|
||||
EdgeStackStatusError
|
||||
//EdgeStackStatusAcknowledged represents an acknowledged edge stack
|
||||
// EdgeStackStatusAcknowledged represents an acknowledged edge stack
|
||||
EdgeStackStatusAcknowledged
|
||||
//EdgeStackStatusRemoved represents a removed edge stack
|
||||
// EdgeStackStatusRemoved represents a removed edge stack
|
||||
EdgeStackStatusRemoved
|
||||
// StatusRemoteUpdateSuccess represents a successfully updated edge stack
|
||||
EdgeStackStatusRemoteUpdateSuccess
|
||||
|
|
Loading…
Reference in New Issue