Now server/auth is completely generic on the user type
This commit is contained in:
+26
-20
@@ -7,24 +7,30 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.phc.dm.unipi.it/aziis98/posti-dm/server/util"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoUserForSession = errors.New(`no user for session token`)
|
||||
)
|
||||
|
||||
type User[IdType any] interface {
|
||||
UID() IdType
|
||||
}
|
||||
|
||||
// Authenticator should be used by clients to provide authentication functions and mapping of session tokens to users
|
||||
type Authenticator interface {
|
||||
type Authenticator[UserID any, U User[UserID]] interface {
|
||||
// CheckUserPassword is called to login a user and create a corresponding session, see also "SessionTokenFromUser"
|
||||
CheckUserPassword(userID string, password string) error
|
||||
CheckUserPassword(user UserID, password string) error
|
||||
|
||||
// GetUserPermissions gets the list of permissions for this user
|
||||
UserPermissions(userID string) ([]string, error)
|
||||
UserPermissions(user UserID) ([]string, error)
|
||||
|
||||
// SessionTokenFromUser returns a (new) session token that represents this user
|
||||
SessionTokenFromUser(userID string) (string, error)
|
||||
SessionTokenFromUser(user UserID) (string, error)
|
||||
// UserFromSessionToken returns the corresponding user for this session token or "service.ErrNoUserForSession"
|
||||
UserFromSessionToken(session string) (string, error)
|
||||
UserFromSessionToken(session string) (U, error)
|
||||
|
||||
// AuthenticationFailed handles failed authentications
|
||||
AuthenticationFailed(error) http.Handler
|
||||
@@ -42,19 +48,19 @@ type MiddlewareConfig struct {
|
||||
}
|
||||
|
||||
// AuthSessionService given an Authenticator provides functions to login and logout users and an http.Handler middleware that accept users based on permissions and login status
|
||||
type AuthSessionService struct {
|
||||
type AuthSessionService[UserID any, U User[UserID]] struct {
|
||||
SessionCookieName string
|
||||
SessionCookiePath string
|
||||
SessionCookieDuration time.Duration
|
||||
|
||||
Authenticator
|
||||
Authenticator Authenticator[UserID, U]
|
||||
}
|
||||
|
||||
// NewAuthSessionService creates a new "*AuthSessionService" with a default session cookie name and path
|
||||
func NewAuthSessionService(auth Authenticator) *AuthSessionService {
|
||||
func NewAuthSessionService[UserID any, U User[UserID]](auth Authenticator[UserID, U]) *AuthSessionService[UserID, U] {
|
||||
oneWeek := 7 * 24 * time.Hour
|
||||
|
||||
return &AuthSessionService{
|
||||
return &AuthSessionService[UserID, U]{
|
||||
"session",
|
||||
"/",
|
||||
oneWeek,
|
||||
@@ -63,7 +69,7 @@ func NewAuthSessionService(auth Authenticator) *AuthSessionService {
|
||||
}
|
||||
|
||||
// Login tries to login a user given its id and password
|
||||
func (service *AuthSessionService) Login(w http.ResponseWriter, userID, password string) error {
|
||||
func (service *AuthSessionService[UserID, U]) Login(w http.ResponseWriter, userID UserID, password string) error {
|
||||
if err := service.Authenticator.CheckUserPassword(userID, password); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -84,7 +90,7 @@ func (service *AuthSessionService) Login(w http.ResponseWriter, userID, password
|
||||
}
|
||||
|
||||
// Logout clears the session cookie from a request effectively logging out the user for future requests
|
||||
func (service *AuthSessionService) Logout(w http.ResponseWriter) {
|
||||
func (service *AuthSessionService[UserID, U]) Logout(w http.ResponseWriter) {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: service.SessionCookieName,
|
||||
Path: service.SessionCookiePath,
|
||||
@@ -94,7 +100,7 @@ func (service *AuthSessionService) Logout(w http.ResponseWriter) {
|
||||
}
|
||||
|
||||
// Middleware returns an http middleware that accepts users based on login status and permissions
|
||||
func (service *AuthSessionService) Middleware(config *MiddlewareConfig) func(http.Handler) http.Handler {
|
||||
func (service *AuthSessionService[UserID, U]) Middleware(config *MiddlewareConfig) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
cookie, err := r.Cookie(service.SessionCookieName)
|
||||
@@ -112,7 +118,7 @@ func (service *AuthSessionService) Middleware(config *MiddlewareConfig) func(htt
|
||||
return
|
||||
}
|
||||
|
||||
userID, err := service.Authenticator.UserFromSessionToken(cookie.Value)
|
||||
user, err := service.Authenticator.UserFromSessionToken(cookie.Value)
|
||||
if err == ErrNoUserForSession {
|
||||
service.Logout(w)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
@@ -124,7 +130,7 @@ func (service *AuthSessionService) Middleware(config *MiddlewareConfig) func(htt
|
||||
}
|
||||
|
||||
if config.RequireLogged {
|
||||
userPerms, err := service.Authenticator.UserPermissions(userID)
|
||||
userPerms, err := service.Authenticator.UserPermissions(user.UID())
|
||||
if err != nil {
|
||||
service.Authenticator.OtherError(err).ServeHTTP(w, r)
|
||||
return
|
||||
@@ -168,7 +174,7 @@ func (service *AuthSessionService) Middleware(config *MiddlewareConfig) func(htt
|
||||
// Middleware(*AuthMiddlewareConfig)
|
||||
//
|
||||
// that only accepts logged in users, no special permissions are checked
|
||||
func (service *AuthSessionService) LoggedMiddleware() func(http.Handler) http.Handler {
|
||||
func (service *AuthSessionService[UserID, U]) LoggedMiddleware() func(http.Handler) http.Handler {
|
||||
return service.Middleware(&MiddlewareConfig{
|
||||
RequireLogged: true,
|
||||
NeedPermissions: []string{},
|
||||
@@ -181,16 +187,16 @@ func (service *AuthSessionService) LoggedMiddleware() func(http.Handler) http.Ha
|
||||
// func (auth *AuthService[U]) RequestUser(r *http.Request) *U
|
||||
//
|
||||
// and will just return nil if no user is present.
|
||||
func (service *AuthSessionService) RequestUser(r *http.Request) (string, error) {
|
||||
func (service *AuthSessionService[UserID, U]) RequestUser(r *http.Request) (U, error) {
|
||||
cookie, err := r.Cookie(service.SessionCookieName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return util.Zero[U](), err
|
||||
}
|
||||
|
||||
userID, err := service.Authenticator.UserFromSessionToken(cookie.Value)
|
||||
user, err := service.Authenticator.UserFromSessionToken(cookie.Value)
|
||||
if err == ErrNoUserForSession {
|
||||
return "", err
|
||||
return util.Zero[U](), err
|
||||
}
|
||||
|
||||
return userID, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user