Skip to content

Commit

Permalink
Merge pull request #149 from SkynetLabs/ivo/fix_api_keys_lower_case
Browse files Browse the repository at this point in the history
Handle API keys passed in their lower case form.
  • Loading branch information
ro-tex committed Mar 9, 2022
2 parents 1c13813 + a713328 commit 0763389
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 7 deletions.
10 changes: 4 additions & 6 deletions api/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ var (
// ErrNoAPIKey is an error returned when we expect an API key but we don't
// find one.
ErrNoAPIKey = errors.New("no api key found")
// ErrInvalidAPIKey is an error returned when the given API key is invalid.
ErrInvalidAPIKey = errors.New("invalid api key")
// ErrNoToken is returned when we expected a JWT token to be provided but it
// was not.
ErrNoToken = errors.New("no authorisation token found")
Expand Down Expand Up @@ -169,11 +167,11 @@ func apiKeyFromRequest(r *http.Request) (database.APIKey, error) {
if akStr == "" {
return "", ErrNoAPIKey
}
ak := database.APIKey(akStr)
if !ak.IsValid() {
return "", ErrInvalidAPIKey
ak, err := database.NewAPIKeyFromString(akStr)
if err != nil {
return "", err
}
return ak, nil
return *ak, nil
}

// tokenFromRequest extracts the JWT token from the request and returns it.
Expand Down
13 changes: 12 additions & 1 deletion database/apikeys.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ var (
// ErrMaxNumAPIKeysExceeded is returned when a user tries to create a new
// API key after already having the maximum allowed number.
ErrMaxNumAPIKeysExceeded = errors.New("maximum number of api keys exceeded")
// ErrInvalidAPIKey is an error returned when the given API key is invalid.
ErrInvalidAPIKey = errors.New("invalid api key")
)

type (
Expand All @@ -50,9 +52,18 @@ func NewAPIKey() APIKey {
return APIKey(base32.HexEncoding.WithPadding(base32.NoPadding).EncodeToString(fastrand.Bytes(PubKeySize)))
}

// NewAPIKeyFromString creates an APIKey struct from a string and validates it.
func NewAPIKeyFromString(s string) (*APIKey, error) {
ak := APIKey(strings.ToUpper(s))
if !ak.IsValid() {
return nil, ErrInvalidAPIKey
}
return &ak, nil
}

// Bytes returns the raw representation of an API key.
func (ak APIKey) Bytes() ([]byte, error) {
return base32.HexEncoding.WithPadding(base32.NoPadding).DecodeString(strings.ToUpper(string(ak)))
return base32.HexEncoding.WithPadding(base32.NoPadding).DecodeString(string(ak))
}

// IsValid checks whether the underlying string satisfies the type's requirement
Expand Down
29 changes: 29 additions & 0 deletions database/apikeys_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package database

import "testing"

// TestNewAPIKeyFromString validates that NewAPIKeyFromString properly handles
// valid API keys, upper case or lower case.
func TestNewAPIKeyFromString(t *testing.T) {
tests := []struct {
name string
in string
valid bool
}{
{name: "empty", in: "", valid: false},
{name: "valid upper case", in: "6TAOK0RVVKKK25PIA33FHDBD1G04DLO015DAAD6OM2J33KCD5CL0", valid: true},
{name: "valid lower case", in: "6taok0rvvkkk25pia33fhdbd1g04dlo015daad6om2j33kcd5cl0", valid: true},
{name: "too short upper case", in: "6TAOK0RVVKKK25PIA33FHDBD1G04DLO015DAAD6OM2J33KCD5CL", valid: false},
{name: "too short lower case", in: "6taok0rvvkkk25pia33fhdbd1g04dlo015daad6om2j33kcd5cl", valid: false},
{name: "too long upper case", in: "6TAOK0RVVKKK25PIA33FHDBD1G04DLO015DAAD6OM2J33KCD5CL01", valid: false},
{name: "too long lower case", in: "6taok0rvvkkk25pia33fhdbd1g04dlo015daad6om2j33kcd5cl01", valid: false},
{name: "invalid alphabet", in: "!TAOK0RVVKKK25PIA33FHDBD1G04DLO015DAAD6OM2J33KCD5CL0", valid: false},
}

for _, tt := range tests {
_, err := NewAPIKeyFromString(tt.in)
if (tt.valid && err != nil) || (!tt.valid && err == nil) {
t.Errorf("Test '%s' failed.", tt.name)
}
}
}

0 comments on commit 0763389

Please sign in to comment.