Fix routing, hash api keys, start ws

This commit is contained in:
Daniel 2024-08-03 00:05:02 -04:00
parent c670c76d76
commit 22c4afadb6
9 changed files with 45 additions and 24 deletions

View file

@ -2,6 +2,8 @@ package auth
import ( import (
"context" "context"
"crypto/sha256"
"encoding/base64"
"time" "time"
"dynatron.me/x/stillbox/pkg/gordio/database" "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) 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 err != nil {
if database.IsNoRows(err) { if database.IsNoRows(err) {
log.Error().Str("apikey", keyUuid.String()).Msg("no such key") 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)) { 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 return nil, ErrUnauthorized
} }

View file

@ -23,13 +23,13 @@ type jwtAuth interface {
Login(ctx context.Context, username, password string) (token string, err error) Login(ctx context.Context, username, password string) (token string, err error)
// InstallVerifyMiddleware installs the JWT verifier middleware to the provided chi Router. // 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 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 installs the auth route to the provided chi Router.
InstallRoutes(r chi.Router) Routes() chi.Router
} }
type claims map[string]interface{} type claims map[string]interface{}
@ -40,12 +40,12 @@ func (a *authenticator) Authenticated(r *http.Request) (claims, bool) {
return cl, err != nil && tok != nil return cl, err != nil && tok != nil
} }
func (a *authenticator) InstallVerifyMiddleware(r chi.Router) { func (a *authenticator) VerifyMiddleware() func(http.Handler) http.Handler {
r.Use(jwtauth.Verifier(a.jwt)) return jwtauth.Verifier(a.jwt)
} }
func (a *authenticator) InstallAuthMiddleware(r chi.Router) { func (a *authenticator) AuthMiddleware() func(http.Handler) http.Handler {
r.Use(jwtauth.Authenticator(a.jwt)) return jwtauth.Authenticator(a.jwt)
} }
func (a *authenticator) Login(ctx context.Context, username, password string) (token string, err error) { 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 return tokenString
} }
func (a *authenticator) InstallRoutes(r chi.Router) { func (a *authenticator) Routes() chi.Router {
r := chi.NewRouter()
r.Post("/auth", a.routeAuth) r.Post("/auth", a.routeAuth)
return r
} }
func (a *authenticator) routeAuth(w http.ResponseWriter, r *http.Request) { func (a *authenticator) routeAuth(w http.ResponseWriter, r *http.Request) {

View file

@ -17,7 +17,7 @@ type ApiKey struct {
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
Expires pgtype.Timestamp `json:"expires"` Expires pgtype.Timestamp `json:"expires"`
Disabled *bool `json:"disabled"` Disabled *bool `json:"disabled"`
ApiKey uuid.UUID `json:"api_key"` ApiKey string `json:"api_key"`
} }
type Call struct { type Call struct {

View file

@ -15,9 +15,9 @@ type Querier interface {
AddCall(ctx context.Context, arg AddCallParams) (uuid.UUID, error) AddCall(ctx context.Context, arg AddCallParams) (uuid.UUID, error)
CreateAPIKey(ctx context.Context, owner int, expires pgtype.Timestamp, disabled *bool) (ApiKey, error) CreateAPIKey(ctx context.Context, owner int, expires pgtype.Timestamp, disabled *bool) (ApiKey, error)
CreateUser(ctx context.Context, arg CreateUserParams) (User, 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 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) GetTalkgroupTags(ctx context.Context, systemID int, tgid int) ([]string, error)
GetTalkgroupsWithAllTags(ctx context.Context, tags []string) ([]Talkgroup, error) GetTalkgroupsWithAllTags(ctx context.Context, tags []string) ([]Talkgroup, error)
GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) ([]Talkgroup, error) GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) ([]Talkgroup, error)

View file

@ -8,7 +8,6 @@ package database
import ( import (
"context" "context"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
) )
@ -77,7 +76,7 @@ const deleteAPIKey = `-- name: DeleteAPIKey :exec
DELETE FROM api_keys WHERE api_key = $1 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) _, err := q.db.Exec(ctx, deleteAPIKey, apiKey)
return err 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 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) row := q.db.QueryRow(ctx, getAPIKey, apiKey)
var i ApiKey var i ApiKey
err := row.Scan( err := row.Scan(

View file

@ -17,21 +17,19 @@ func (s *Server) setupRoutes() {
r.Group(func(r chi.Router) { r.Group(func(r chi.Router) {
// authenticated routes // authenticated routes
s.auth.InstallVerifyMiddleware(r) r.Use(s.auth.AuthMiddleware(), s.auth.VerifyMiddleware())
s.auth.InstallAuthMiddleware(r)
}) })
r.Group(func(r chi.Router) { r.Group(func(r chi.Router) {
r.Use(rateLimiter()) r.Use(rateLimiter())
r.Use(render.SetContentType(render.ContentTypeJSON)) r.Use(render.SetContentType(render.ContentTypeJSON))
// public routes // public routes
s.auth.InstallRoutes(r) r.Mount("/", s.auth.Routes())
s.sources.InstallPublicRoutes(r) r.Mount("/", s.sources.PublicRoutes())
}) })
r.Group(func(r chi.Router) { r.Group(func(r chi.Router) {
r.Use(rateLimiter()) r.Use(rateLimiter(), s.auth.VerifyMiddleware())
s.auth.InstallVerifyMiddleware(r)
// optional auth routes // optional auth routes

View file

@ -2,6 +2,7 @@ package sources
import ( import (
"context" "context"
"dynatron.me/x/stillbox/pkg/gordio/calls" "dynatron.me/x/stillbox/pkg/gordio/calls"
"github.com/go-chi/chi/v5" "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 { for _, si := range *s {
if rs, ok := si.Source.(PublicRouteSource); ok { if rs, ok := si.Source.(PublicRouteSource); ok {
rs.InstallPublicRoutes(r) rs.InstallPublicRoutes(r)
} }
} }
return r
} }
type Ingestor interface { type Ingestor interface {

13
pkg/gordio/ws/session.go Normal file
View file

@ -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) {
}

View file

@ -15,7 +15,7 @@ CREATE TABLE IF NOT EXISTS api_keys(
created_at TIMESTAMP NOT NULL, created_at TIMESTAMP NOT NULL,
expires TIMESTAMP, expires TIMESTAMP,
disabled BOOLEAN, disabled BOOLEAN,
api_key UUID UNIQUE NOT NULL api_key TEXT UNIQUE NOT NULL
); );
CREATE TABLE IF NOT EXISTS systems( CREATE TABLE IF NOT EXISTS systems(