Files
relata/internal/web/tokens_test.go
T

103 lines
3.0 KiB
Go

package web
import (
"context"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"time"
"github.com/andydixon/relata/internal/api"
"github.com/andydixon/relata/internal/auth"
"github.com/andydixon/relata/internal/config"
"github.com/andydixon/relata/internal/domain"
)
type tokenPageRepo struct {
domain.APITokenRepository
tokens []*domain.APIToken
}
func (r *tokenPageRepo) Create(_ context.Context, tok *domain.APIToken) error {
r.tokens = append(r.tokens, tok)
return nil
}
func (r *tokenPageRepo) ListForUser(_ context.Context, workspaceID, userID string) ([]*domain.APIToken, error) {
var out []*domain.APIToken
for _, tok := range r.tokens {
if tok.WorkspaceID == workspaceID && tok.UserID == userID {
out = append(out, tok)
}
}
return out, nil
}
type tokenPageRels struct{ domain.RelationshipRepository }
func (tokenPageRels) CountPendingReview(context.Context, string) (int64, error) { return 0, nil }
func newTokenPageHandlers(t *testing.T) (*Handlers, *domain.Principal) {
t.Helper()
renderer, err := NewRenderer()
if err != nil {
t.Fatal(err)
}
repo := &tokenPageRepo{}
tokens := auth.NewTokenService(repo, nil, auth.TokenConfig{
DefaultTTL: 24 * time.Hour,
MaxTTL: 30 * 24 * time.Hour,
})
cfg := &config.Config{}
cfg.Auth.OAuth.TokenDefaultTTL = 24 * time.Hour
cfg.Auth.OAuth.TokenMaxTTL = 30 * 24 * time.Hour
h := NewHandlers(Deps{Cfg: cfg, Renderer: renderer, Tokens: tokens, Rels: tokenPageRels{}})
p := &domain.Principal{
UserID: "user-1", Username: "admin", WorkspaceID: "workspace-1", Role: domain.RoleAdmin,
}
return h, p
}
func TestCreateTokenNeverPlacesSecretInRedirectURL(t *testing.T) {
h, p := newTokenPageHandlers(t)
form := url.Values{
"name": {"automation"},
"scopes": {string(domain.ScopeQuery)},
"ttl_days": {"7"},
}
r := httptest.NewRequest(http.MethodPost, "/settings/tokens", strings.NewReader(form.Encode()))
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
r = r.WithContext(api.WithPrincipal(r.Context(), p))
w := httptest.NewRecorder()
h.CreateToken(w, r)
if w.Code != http.StatusCreated {
t.Fatalf("status = %d, want 201", w.Code)
}
if location := w.Header().Get("Location"); location != "" {
t.Fatalf("token creation redirected to %q; the secret could enter URL logs", location)
}
if !strings.Contains(w.Body.String(), "rlt_v1_") {
t.Fatal("the one-time token was not rendered in the creation response")
}
if strings.Contains(w.Body.String(), "?secret=") {
t.Fatal("the rendered page still describes a secret-bearing URL")
}
}
func TestTokensPageIgnoresSecretQueryParameter(t *testing.T) {
h, p := newTokenPageHandlers(t)
r := httptest.NewRequest(http.MethodGet, "/settings/tokens?secret=rlt_v1_attacker_controlled", nil)
r = r.WithContext(api.WithPrincipal(r.Context(), p))
w := httptest.NewRecorder()
h.TokensPage(w, r)
if strings.Contains(w.Body.String(), "rlt_v1_attacker_controlled") {
t.Fatal("a secret query parameter was reflected into the token page")
}
}