make auth an interface

This commit is contained in:
Daniel 2024-07-29 00:58:32 -04:00
parent fac9e3fab2
commit 91099eb13e
5 changed files with 42 additions and 21 deletions

View file

@ -10,9 +10,13 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
// CheckAPIKey validates the provided key and returns the API key record. type apiKeyAuth interface {
// An error is returned if validation fails for any reason. // CheckAPIKey validates the provided key and returns the API key record.
func (a *Authenticator) CheckAPIKey(ctx context.Context, key string) (*database.ApiKey, error) { // An error is returned if validation fails for any reason.
CheckAPIKey(ctx context.Context, key string) (*database.ApiKey, error)
}
func (a *authenticator) CheckAPIKey(ctx context.Context, key string) (*database.ApiKey, error) {
keyUuid, err := uuid.Parse(key) keyUuid, err := uuid.Parse(key)
if err != nil { if err != nil {
log.Error().Str("apikey", key).Msg("cannot parse key") log.Error().Str("apikey", key).Msg("cannot parse key")

View file

@ -8,14 +8,19 @@ import (
) )
// Authenticator performs API key and user JWT authentication. // Authenticator performs API key and user JWT authentication.
type Authenticator struct { type Authenticator interface {
jwtAuth
apiKeyAuth
}
type authenticator struct {
domain string domain string
jwt *jwtauth.JWTAuth jwt *jwtauth.JWTAuth
} }
// NewAuthenticator creates a new Authenticator with the provided JWT secret and cookie domain. // NewAuthenticator creates a new Authenticator with the provided JWT secret and cookie domain.
func NewAuthenticator(jwtSecret string, domain string) *Authenticator { func NewAuthenticator(jwtSecret string, domain string) Authenticator {
return &Authenticator{ return &authenticator{
domain: domain, domain: domain,
jwt: jwtauth.New("HS256", []byte(jwtSecret), nil), jwt: jwtauth.New("HS256", []byte(jwtSecret), nil),
} }

View file

@ -15,27 +15,40 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
type jwtAuth interface {
// Authenticated returns whether the request is authenticated. It also returns the claims.
Authenticated(r *http.Request) (claims, bool)
// Login attempts to return a JWT for the provided user and password.
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)
// InstallAuthMiddleware installs the JWT authenticator middleware to the provided chi Router.
InstallAuthMiddleware(r chi.Router)
// InstallRoutes installs the auth route to the provided chi Router.
InstallRoutes(r chi.Router)
}
type claims map[string]interface{} type claims map[string]interface{}
// Authenticated returns whether the request is authenticated. It also returns the claims. func (a *authenticator) Authenticated(r *http.Request) (claims, bool) {
func (a *Authenticator) Authenticated(r *http.Request) (claims, bool) {
// TODO: check IP against ACL, or conf.Public, and against map of routes // TODO: check IP against ACL, or conf.Public, and against map of routes
tok, cl, err := jwtauth.FromContext(r.Context()) tok, cl, err := jwtauth.FromContext(r.Context())
return cl, err != nil && tok != nil return cl, err != nil && tok != nil
} }
// InstallVerifyMiddleware installs the JWT verifier middleware to the provided chi Router. func (a *authenticator) InstallVerifyMiddleware(r chi.Router) {
func (a *Authenticator) InstallVerifyMiddleware(r chi.Router) {
r.Use(jwtauth.Verifier(a.jwt)) r.Use(jwtauth.Verifier(a.jwt))
} }
// InstallVerifyMiddleware installs the JWT authenticator middleware to the provided chi Router. func (a *authenticator) InstallAuthMiddleware(r chi.Router) {
func (a *Authenticator) InstallAuthMiddleware(r chi.Router) {
r.Use(jwtauth.Authenticator(a.jwt)) r.Use(jwtauth.Authenticator(a.jwt))
} }
// Login attempts to return a JWT for the provided user and password. 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) {
q := database.New(database.FromCtx(ctx)) q := database.New(database.FromCtx(ctx))
users, err := q.GetUsers(ctx) users, err := q.GetUsers(ctx)
if err != nil { if err != nil {
@ -64,7 +77,7 @@ func (a *Authenticator) Login(ctx context.Context, username, password string) (t
return a.newToken(found.ID), nil return a.newToken(found.ID), nil
} }
func (a *Authenticator) newToken(uid int32) string { func (a *authenticator) newToken(uid int32) string {
claims := claims{ claims := claims{
"user_id": uid, "user_id": uid,
} }
@ -76,12 +89,11 @@ func (a *Authenticator) newToken(uid int32) string {
return tokenString return tokenString
} }
// InstallRoutes installs the auth route to the provided chi Router. func (a *authenticator) InstallRoutes(r chi.Router) {
func (a *Authenticator) InstallRoutes(r chi.Router) {
r.Post("/auth", a.routeAuth) r.Post("/auth", a.routeAuth)
} }
func (a *Authenticator) routeAuth(w http.ResponseWriter, r *http.Request) { func (a *authenticator) routeAuth(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm() err := r.ParseForm()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)

View file

@ -18,11 +18,11 @@ import (
// HTTPIngestor is an ingestor that accepts calls over HTTP. // HTTPIngestor is an ingestor that accepts calls over HTTP.
type HTTPIngestor struct { type HTTPIngestor struct {
auth *auth.Authenticator auth auth.Authenticator
} }
// NewHTTPIngestor creates a new HTTPIngestor. It requires an Authenticator. // NewHTTPIngestor creates a new HTTPIngestor. It requires an Authenticator.
func NewHTTPIngestor(auth *auth.Authenticator) *HTTPIngestor { func NewHTTPIngestor(auth auth.Authenticator) *HTTPIngestor {
return &HTTPIngestor{ return &HTTPIngestor{
auth: auth, auth: auth,
} }

View file

@ -12,7 +12,7 @@ import (
) )
type Server struct { type Server struct {
auth *auth.Authenticator auth auth.Authenticator
conf *config.Config conf *config.Config
db *database.DB db *database.DB
r *chi.Mux r *chi.Mux