@ -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 ( user ID string ) ( [ ] string , error )
UserPermissions ( user UserID ) ( [ ] string , error )
// SessionTokenFromUser returns a (new) session token that represents this user
SessionTokenFromUser ( user ID 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 , u serID, password string ) error {
func ( service * AuthSessionService [UserID , U ] ) Login ( w http . ResponseWriter , u serID U serID, 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
}
user ID , 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 ( user ID)
userPerms , err := service . Authenticator . UserPermissions ( user . U ID( ) )
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
}
user ID , err := service . Authenticator . UserFromSessionToken ( cookie . Value )
user , err := service . Authenticator . UserFromSessionToken ( cookie . Value )
if err == ErrNoUserForSession {
return "" , err
return util . Zero [ U ] ( ) , err
}
return user ID , nil
return user , nil
}