diff --git a/pkg/gordio/auth/apikey.go b/pkg/gordio/auth/apikey.go index 019c920..876b35a 100644 --- a/pkg/gordio/auth/apikey.go +++ b/pkg/gordio/auth/apikey.go @@ -2,6 +2,8 @@ package auth import ( "context" + "crypto/sha256" + "encoding/base64" "time" "dynatron.me/x/stillbox/pkg/gordio/database" @@ -24,7 +26,9 @@ func (a *authenticator) CheckAPIKey(ctx context.Context, key string) (*UserID, e } db := database.FromCtx(ctx) - apik, err := db.GetAPIKey(ctx, keyUuid) + hash := sha256.Sum256([]byte(keyUuid.String())) + b64hash := base64.StdEncoding.EncodeToString(hash[:]) + apik, err := db.GetAPIKey(ctx, b64hash) if err != nil { if database.IsNoRows(err) { log.Error().Str("apikey", keyUuid.String()).Msg("no such key") @@ -36,7 +40,7 @@ func (a *authenticator) CheckAPIKey(ctx context.Context, key string) (*UserID, e } if (apik.Disabled != nil && *apik.Disabled) || (apik.Expires.Valid && time.Now().After(apik.Expires.Time)) { - log.Error().Str("key", apik.ApiKey.String()).Msg("key disabled") + log.Error().Str("key", apik.ApiKey).Msg("key disabled") return nil, ErrUnauthorized } diff --git a/pkg/gordio/auth/jwt.go b/pkg/gordio/auth/jwt.go index 65ac83e..0ea4087 100644 --- a/pkg/gordio/auth/jwt.go +++ b/pkg/gordio/auth/jwt.go @@ -23,13 +23,13 @@ type jwtAuth interface { Login(ctx context.Context, username, password string) (token string, err error) // InstallVerifyMiddleware installs the JWT verifier middleware to the provided chi Router. - InstallVerifyMiddleware(r chi.Router) + VerifyMiddleware() func(http.Handler) http.Handler // InstallAuthMiddleware installs the JWT authenticator middleware to the provided chi Router. - InstallAuthMiddleware(r chi.Router) + AuthMiddleware() func(http.Handler) http.Handler // InstallRoutes installs the auth route to the provided chi Router. - InstallRoutes(r chi.Router) + Routes() chi.Router } type claims map[string]interface{} @@ -40,12 +40,12 @@ func (a *authenticator) Authenticated(r *http.Request) (claims, bool) { return cl, err != nil && tok != nil } -func (a *authenticator) InstallVerifyMiddleware(r chi.Router) { - r.Use(jwtauth.Verifier(a.jwt)) +func (a *authenticator) VerifyMiddleware() func(http.Handler) http.Handler { + return jwtauth.Verifier(a.jwt) } -func (a *authenticator) InstallAuthMiddleware(r chi.Router) { - r.Use(jwtauth.Authenticator(a.jwt)) +func (a *authenticator) AuthMiddleware() func(http.Handler) http.Handler { + return jwtauth.Authenticator(a.jwt) } func (a *authenticator) Login(ctx context.Context, username, password string) (token string, err error) { @@ -89,8 +89,11 @@ func (a *authenticator) newToken(uid int32) string { return tokenString } -func (a *authenticator) InstallRoutes(r chi.Router) { +func (a *authenticator) Routes() chi.Router { + r := chi.NewRouter() r.Post("/auth", a.routeAuth) + + return r } func (a *authenticator) routeAuth(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/gordio/database/models.go b/pkg/gordio/database/models.go index bf210e6..6aafd8d 100644 --- a/pkg/gordio/database/models.go +++ b/pkg/gordio/database/models.go @@ -17,7 +17,7 @@ type ApiKey struct { CreatedAt time.Time `json:"created_at"` Expires pgtype.Timestamp `json:"expires"` Disabled *bool `json:"disabled"` - ApiKey uuid.UUID `json:"api_key"` + ApiKey string `json:"api_key"` } type Call struct { diff --git a/pkg/gordio/database/querier.go b/pkg/gordio/database/querier.go index a15d01d..ab6d283 100644 --- a/pkg/gordio/database/querier.go +++ b/pkg/gordio/database/querier.go @@ -15,9 +15,9 @@ type Querier interface { AddCall(ctx context.Context, arg AddCallParams) (uuid.UUID, error) CreateAPIKey(ctx context.Context, owner int, expires pgtype.Timestamp, disabled *bool) (ApiKey, error) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) - DeleteAPIKey(ctx context.Context, apiKey uuid.UUID) error + DeleteAPIKey(ctx context.Context, apiKey string) error DeleteUser(ctx context.Context, username string) error - GetAPIKey(ctx context.Context, apiKey uuid.UUID) (ApiKey, error) + GetAPIKey(ctx context.Context, apiKey string) (ApiKey, error) GetTalkgroupTags(ctx context.Context, systemID int, tgid int) ([]string, error) GetTalkgroupsWithAllTags(ctx context.Context, tags []string) ([]Talkgroup, error) GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) ([]Talkgroup, error) diff --git a/pkg/gordio/database/users.sql.go b/pkg/gordio/database/users.sql.go index 7cd45a6..ea9eb0a 100644 --- a/pkg/gordio/database/users.sql.go +++ b/pkg/gordio/database/users.sql.go @@ -8,7 +8,6 @@ package database import ( "context" - "github.com/google/uuid" "github.com/jackc/pgx/v5/pgtype" ) @@ -77,7 +76,7 @@ const deleteAPIKey = `-- name: DeleteAPIKey :exec DELETE FROM api_keys WHERE api_key = $1 ` -func (q *Queries) DeleteAPIKey(ctx context.Context, apiKey uuid.UUID) error { +func (q *Queries) DeleteAPIKey(ctx context.Context, apiKey string) error { _, err := q.db.Exec(ctx, deleteAPIKey, apiKey) return err } @@ -95,7 +94,7 @@ const getAPIKey = `-- name: GetAPIKey :one SELECT id, owner, created_at, expires, disabled, api_key FROM api_keys WHERE api_key = $1 ` -func (q *Queries) GetAPIKey(ctx context.Context, apiKey uuid.UUID) (ApiKey, error) { +func (q *Queries) GetAPIKey(ctx context.Context, apiKey string) (ApiKey, error) { row := q.db.QueryRow(ctx, getAPIKey, apiKey) var i ApiKey err := row.Scan( diff --git a/pkg/gordio/server/routes.go b/pkg/gordio/server/routes.go index eef102f..71d059c 100644 --- a/pkg/gordio/server/routes.go +++ b/pkg/gordio/server/routes.go @@ -17,21 +17,19 @@ func (s *Server) setupRoutes() { r.Group(func(r chi.Router) { // authenticated routes - s.auth.InstallVerifyMiddleware(r) - s.auth.InstallAuthMiddleware(r) + r.Use(s.auth.AuthMiddleware(), s.auth.VerifyMiddleware()) }) r.Group(func(r chi.Router) { r.Use(rateLimiter()) r.Use(render.SetContentType(render.ContentTypeJSON)) // public routes - s.auth.InstallRoutes(r) - s.sources.InstallPublicRoutes(r) + r.Mount("/", s.auth.Routes()) + r.Mount("/", s.sources.PublicRoutes()) }) r.Group(func(r chi.Router) { - r.Use(rateLimiter()) - s.auth.InstallVerifyMiddleware(r) + r.Use(rateLimiter(), s.auth.VerifyMiddleware()) // optional auth routes diff --git a/pkg/gordio/sources/source.go b/pkg/gordio/sources/source.go index 08362b0..63d4dae 100644 --- a/pkg/gordio/sources/source.go +++ b/pkg/gordio/sources/source.go @@ -2,6 +2,7 @@ package sources import ( "context" + "dynatron.me/x/stillbox/pkg/gordio/calls" "github.com/go-chi/chi/v5" @@ -25,12 +26,15 @@ func (s *Sources) Register(name string, src Source) { }) } -func (s *Sources) InstallPublicRoutes(r chi.Router) { +func (s *Sources) PublicRoutes() chi.Router { + r := chi.NewRouter() for _, si := range *s { if rs, ok := si.Source.(PublicRouteSource); ok { rs.InstallPublicRoutes(r) } } + + return r } type Ingestor interface { diff --git a/pkg/gordio/ws/session.go b/pkg/gordio/ws/session.go new file mode 100644 index 0000000..1750483 --- /dev/null +++ b/pkg/gordio/ws/session.go @@ -0,0 +1,13 @@ +package ws + +import ( + "github.com/go-chi/chi/v5" + // "github.com/gorilla/websocket" +) + +type SessionManager struct { +} + +func (s *SessionManager) InstallPrivateRoutes(r chi.Router) { + +} diff --git a/sql/postgres/migrations/001_initial.up.sql b/sql/postgres/migrations/001_initial.up.sql index 0c62020..ee5853b 100644 --- a/sql/postgres/migrations/001_initial.up.sql +++ b/sql/postgres/migrations/001_initial.up.sql @@ -15,7 +15,7 @@ CREATE TABLE IF NOT EXISTS api_keys( created_at TIMESTAMP NOT NULL, expires TIMESTAMP, disabled BOOLEAN, - api_key UUID UNIQUE NOT NULL + api_key TEXT UNIQUE NOT NULL ); CREATE TABLE IF NOT EXISTS systems(