-
-
Notifications
You must be signed in to change notification settings - Fork 0
Use secret key for token middleware signatures #84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
983317a
942f3c5
912884c
cb53b29
311b9ab
4bc51b4
d4f46b2
dd2bec4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package auth | ||
|
|
||
| import ( | ||
| "reflect" | ||
| "testing" | ||
| ) | ||
|
|
||
| func TestSchemaConstants(t *testing.T) { | ||
| tests := []struct { | ||
| name string | ||
| got any | ||
| want any | ||
| }{ | ||
| {"PublicKeyPrefix", PublicKeyPrefix, "pk_"}, | ||
| {"SecretKeyPrefix", SecretKeyPrefix, "sk_"}, | ||
| {"TokenMinLength", TokenMinLength, 16}, | ||
| {"AccountNameMinLength", AccountNameMinLength, 5}, | ||
| {"EncryptionKeyLength", EncryptionKeyLength, 32}, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| tt := tt | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if !reflect.DeepEqual(tt.got, tt.want) { | ||
| t.Errorf("%s = %v, want %v", tt.name, tt.got, tt.want) | ||
| } | ||
| }) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package cli | ||
|
|
||
| import ( | ||
| "reflect" | ||
| "testing" | ||
| ) | ||
|
|
||
| func TestColourConstants(t *testing.T) { | ||
| tests := []struct { | ||
| name string | ||
| got any | ||
| want any | ||
| }{ | ||
| {"Reset", Reset, "\033[0m"}, | ||
| {"RedColour", RedColour, "\033[31m"}, | ||
| {"GreenColour", GreenColour, "\033[32m"}, | ||
| {"YellowColour", YellowColour, "\033[33m"}, | ||
| {"BlueColour", BlueColour, "\033[34m"}, | ||
| {"MagentaColour", MagentaColour, "\033[35m"}, | ||
| {"CyanColour", CyanColour, "\033[36m"}, | ||
| {"GrayColour", GrayColour, "\033[37m"}, | ||
| {"WhiteColour", WhiteColour, "\033[97m"}, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| tt := tt | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if !reflect.DeepEqual(tt.got, tt.want) { | ||
| t.Errorf("%s = %q, want %q", tt.name, tt.got, tt.want) | ||
| } | ||
| }) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -97,7 +97,7 @@ func TestTokenMiddleware_PublicTokenMismatch(t *testing.T) { | |||||
| next := func(w http.ResponseWriter, r *http.Request) *pkgHttp.ApiError { return nil } | ||||||
| handler := tm.Handle(next) | ||||||
|
|
||||||
| req := makeSignedRequest(t, http.MethodGet, "https://api.test.local/v1/x", "", seed.AccountName, "wrong-"+seed.PublicKey, seed.SecretKey, time.Now(), "nonce-mm", "req-mm") | ||||||
| req := makeSignedRequest(t, http.MethodGet, "https://api.test.local/v1/x", "", seed.AccountName, "wrong-"+seed.PublicKey, seed.SecretKey, time.Now(), "nonce-mm", "req-mm") | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line is indented with spaces instead of tabs. Please run
Suggested change
|
||||||
| req.Header.Set("X-Forwarded-For", "1.1.1.1") | ||||||
| rec := httptest.NewRecorder() | ||||||
| if err := handler(rec, req); err == nil || err.Status != http.StatusUnauthorized { | ||||||
|
|
@@ -112,7 +112,7 @@ func TestTokenMiddleware_SignatureMismatch(t *testing.T) { | |||||
| next := func(w http.ResponseWriter, r *http.Request) *pkgHttp.ApiError { return nil } | ||||||
| handler := tm.Handle(next) | ||||||
|
|
||||||
| req := makeSignedRequest(t, http.MethodPost, "https://api.test.local/v1/x", "body", seed.AccountName, seed.PublicKey, seed.SecretKey, time.Now(), "nonce-sig", "req-sig") | ||||||
| req := makeSignedRequest(t, http.MethodPost, "https://api.test.local/v1/x", "body", seed.AccountName, seed.PublicKey, seed.SecretKey, time.Now(), "nonce-sig", "req-sig") | ||||||
| req.Header.Set("X-Forwarded-For", "1.1.1.1") | ||||||
| req.Header.Set("X-API-Signature", req.Header.Get("X-API-Signature")+"tamper") | ||||||
| rec := httptest.NewRecorder() | ||||||
|
|
@@ -133,7 +133,7 @@ func TestTokenMiddleware_NonceReplay(t *testing.T) { | |||||
| } | ||||||
| handler := tm.Handle(next) | ||||||
|
|
||||||
| req := makeSignedRequest(t, http.MethodPost, "https://api.test.local/v1/x", "{}", seed.AccountName, seed.PublicKey, seed.SecretKey, time.Now(), "nonce-rp", "req-rp") | ||||||
| req := makeSignedRequest(t, http.MethodPost, "https://api.test.local/v1/x", "{}", seed.AccountName, seed.PublicKey, seed.SecretKey, time.Now(), "nonce-rp", "req-rp") | ||||||
| req.Header.Set("X-Forwarded-For", "1.1.1.1") | ||||||
| rec := httptest.NewRecorder() | ||||||
| if err := handler(rec, req); err != nil { | ||||||
|
|
@@ -161,22 +161,22 @@ func TestTokenMiddleware_RateLimiter(t *testing.T) { | |||||
|
|
||||||
| // Pre-warm limiter by sending invalid signature requests up to the limit | ||||||
| for i := 0; i < tm.maxFailPerScope; i++ { | ||||||
| req := makeSignedRequest( | ||||||
| t, http.MethodGet, "https://api.test.local/v1/rl", "", | ||||||
| seed.AccountName, seed.PublicKey, "wrong-secret", time.Now(), | ||||||
| fmt.Sprintf("nonce-rl-%d", i), fmt.Sprintf("req-rl-%d", i), | ||||||
| ) | ||||||
| req := makeSignedRequest( | ||||||
| t, http.MethodGet, "https://api.test.local/v1/rl", "", | ||||||
| seed.AccountName, seed.PublicKey, "wrong-secret", time.Now(), | ||||||
| fmt.Sprintf("nonce-rl-%d", i), fmt.Sprintf("req-rl-%d", i), | ||||||
| ) | ||||||
| req.Header.Set("X-Forwarded-For", "9.9.9.9") | ||||||
| rec := httptest.NewRecorder() | ||||||
| _ = handler(rec, req) // ignore errors while warming | ||||||
| } | ||||||
|
|
||||||
| // Next request with valid signature should be rate limited | ||||||
| req := makeSignedRequest( | ||||||
| t, http.MethodGet, "https://api.test.local/v1/rl", "", | ||||||
| seed.AccountName, seed.PublicKey, seed.SecretKey, time.Now(), | ||||||
| "nonce-rl-final", "req-rl-final", | ||||||
| ) | ||||||
| req := makeSignedRequest( | ||||||
| t, http.MethodGet, "https://api.test.local/v1/rl", "", | ||||||
| seed.AccountName, seed.PublicKey, seed.SecretKey, time.Now(), | ||||||
| "nonce-rl-final", "req-rl-final", | ||||||
| ) | ||||||
| req.Header.Set("X-Forwarded-For", "9.9.9.9") | ||||||
| rec := httptest.NewRecorder() | ||||||
| err := handler(rec, req) | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -209,7 +209,8 @@ func generate32(t *testing.T) []byte { | |||||||||
| return buf | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // makeSignedRequest builds a request with required headers and a valid HMAC signature over the canonical string. | ||||||||||
| // makeSignedRequest builds a request with required headers and a valid HMAC | ||||||||||
| // signature over the canonical string using the account's secret key. | ||||||||||
| func makeSignedRequest(t *testing.T, method, rawURL, body, account, public, secret string, ts time.Time, nonce, reqID string) *http.Request { | ||||||||||
| t.Helper() | ||||||||||
| var bodyBuf *bytes.Buffer | ||||||||||
|
|
@@ -226,8 +227,8 @@ func makeSignedRequest(t *testing.T, method, rawURL, body, account, public, secr | |||||||||
| req.Header.Set("X-API-Nonce", nonce) | ||||||||||
|
|
||||||||||
| bodyHash := portal.Sha256Hex([]byte(body)) | ||||||||||
| canonical := portal.BuildCanonical(method, req.URL, account, public, req.Header.Get("X-API-Timestamp"), nonce, bodyHash) | ||||||||||
| sig := auth.CreateSignatureFrom(canonical, secret) | ||||||||||
| canonical := portal.BuildCanonical(method, req.URL, account, public, req.Header.Get("X-API-Timestamp"), nonce, bodyHash) | ||||||||||
| sig := auth.CreateSignatureFrom(canonical, secret) | ||||||||||
|
Comment on lines
+230
to
+231
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These lines are indented with spaces instead of tabs. Please run
Suggested change
|
||||||||||
| req.Header.Set("X-API-Signature", sig) | ||||||||||
| return req | ||||||||||
| } | ||||||||||
|
|
@@ -273,12 +274,12 @@ func TestTokenMiddleware_DB_Integration(t *testing.T) { | |||||||||
| http.MethodPost, | ||||||||||
| "https://api.test.local/v1/posts?z=9&a=1", | ||||||||||
| "{\"title\":\"ok\"}", | ||||||||||
| seed.AccountName, | ||||||||||
| seed.PublicKey, | ||||||||||
| seed.SecretKey, | ||||||||||
| now, | ||||||||||
| "nonce-1", | ||||||||||
| "req-001", | ||||||||||
| seed.AccountName, | ||||||||||
| seed.PublicKey, | ||||||||||
| seed.SecretKey, | ||||||||||
| now, | ||||||||||
| "nonce-1", | ||||||||||
| "req-001", | ||||||||||
| ) | ||||||||||
| rec := httptest.NewRecorder() | ||||||||||
| if err := handler(rec, req); err != nil { | ||||||||||
|
|
@@ -294,12 +295,12 @@ func TestTokenMiddleware_DB_Integration(t *testing.T) { | |||||||||
| http.MethodGet, | ||||||||||
| "https://api.test.local/v1/ping", | ||||||||||
| "", | ||||||||||
| "no-such-user", | ||||||||||
| seed.PublicKey, | ||||||||||
| seed.SecretKey, | ||||||||||
| now, | ||||||||||
| "nonce-2", | ||||||||||
| "req-002", | ||||||||||
| "no-such-user", | ||||||||||
| seed.PublicKey, | ||||||||||
| seed.SecretKey, | ||||||||||
| now, | ||||||||||
| "nonce-2", | ||||||||||
| "req-002", | ||||||||||
| ) | ||||||||||
| rec = httptest.NewRecorder() | ||||||||||
| if err := handler(rec, reqUnknown); err == nil || err.Status != http.StatusUnauthorized { | ||||||||||
|
|
@@ -350,12 +351,12 @@ func TestTokenMiddleware_DB_Integration_HappyPath(t *testing.T) { | |||||||||
| http.MethodPost, | ||||||||||
| "https://api.test.local/v1/resource?b=2&a=1", | ||||||||||
| "{\"x\":123}", | ||||||||||
| seed.AccountName, | ||||||||||
| seed.PublicKey, | ||||||||||
| seed.SecretKey, | ||||||||||
| time.Now(), | ||||||||||
| "n-happy-1", | ||||||||||
| "rid-happy-1", | ||||||||||
| seed.AccountName, | ||||||||||
| seed.PublicKey, | ||||||||||
| seed.SecretKey, | ||||||||||
| time.Now(), | ||||||||||
| "n-happy-1", | ||||||||||
| "rid-happy-1", | ||||||||||
| ) | ||||||||||
| rec := httptest.NewRecorder() | ||||||||||
| if err := handler(rec, req); err != nil { | ||||||||||
|
|
@@ -416,12 +417,12 @@ func TestTokenMiddleware_RejectsFutureTimestamps(t *testing.T) { | |||||||||
| http.MethodGet, | ||||||||||
| "https://api.test.local/v1/test", | ||||||||||
| "", | ||||||||||
| seed.AccountName, | ||||||||||
| seed.PublicKey, | ||||||||||
| seed.SecretKey, | ||||||||||
| futureTime, | ||||||||||
| "n-future-1", | ||||||||||
| "rid-future-1", | ||||||||||
| seed.AccountName, | ||||||||||
| seed.PublicKey, | ||||||||||
| seed.SecretKey, | ||||||||||
| futureTime, | ||||||||||
| "n-future-1", | ||||||||||
| "rid-future-1", | ||||||||||
| ) | ||||||||||
| rec := httptest.NewRecorder() | ||||||||||
|
|
||||||||||
|
|
||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The indentation for these new lines appears to be using spaces instead of tabs. The standard Go format (
gofmt) uses tabs for indentation. Please rungofmton the file to fix the formatting for consistency.