This commit is contained in:
Tom 2023-06-22 13:30:44 +02:00
parent 4b93d7dace
commit effa79aacd
10 changed files with 56 additions and 20 deletions

4
API.md
View file

@ -17,7 +17,7 @@ Typically each request takes only a second to execute.
NOTE: These routes will be documented using a Swagger / OpenAPI definition in the future. NOTE: These routes will be documented using a Swagger / OpenAPI definition in the future.
All routes can be found under `/api/v1/http/` All routes can be found under `/api/v1/http/`
- `/api/v1/auth`: Returns user information - `/api/v1/auth`: Returns api session information
- `/api/v1/systems`: Returns a (publically visible) list of systems - `/api/v1/systems`: Returns a (publically visible) list of systems
- `/api/v1/news`: Returns JSON containing all news items - `/api/v1/news`: Returns JSON containing all news items
@ -54,4 +54,4 @@ Each input is sent directly to the underlying process.
### Supported Websocket Calls ### Supported Websocket Calls
(to be documented) (to be documented)

View file

@ -44,7 +44,7 @@ func (a *API) HandleRoute(ctx context.Context, path string) (http.Handler, error
Handler: func(s string, r *http.Request) (ai AuthInfo, err error) { Handler: func(s string, r *http.Request) (ai AuthInfo, err error) {
var user *auth.AuthUser var user *auth.AuthUser
user, ai.Token, err = a.Dependencies.Auth.UserOf(r) user, err = a.Dependencies.Auth.SessionOf(r)
if user != nil { if user != nil {
ai.User = user.User.User ai.User = user.User.User
} }

View file

@ -80,7 +80,7 @@ func (next *Next) HandleRoute(ctx context.Context, path string) (http.Handler, e
} }
// get the user // get the user
user, _, err := next.Dependencies.Auth.UserOf(r) user, _, err := next.Dependencies.Auth.SessionOf(r)
if err != nil { if err != nil {
return "", 0, err return "", 0, err
} }

View file

@ -23,7 +23,7 @@ func (auth *Auth) Protect(handler http.Handler, AllowToken bool, scope component
// load the user in the session // load the user in the session
// TODO<tokens>: Check if API access is allowed // TODO<tokens>: Check if API access is allowed
user, token, err := auth.UserOf(r) user, token, err := auth.SessionOf(r)
if err != nil { if err != nil {
goto err goto err
} }

View file

@ -32,6 +32,6 @@ func (*AdminLoggedIn) Scope() component.ScopeInfo {
} }
func (al *AdminLoggedIn) HasScope(param string, r *http.Request) (bool, error) { func (al *AdminLoggedIn) HasScope(param string, r *http.Request) (bool, error) {
user, _, err := al.Dependencies.Auth.UserOf(r) user, _, err := al.Dependencies.Auth.SessionOf(r)
return user != nil && user.IsAdmin() && user.IsTOTPEnabled(), err return user != nil && user.IsAdmin() && user.IsTOTPEnabled(), err
} }

View file

@ -31,6 +31,6 @@ func (*UserLoggedIn) Scope() component.ScopeInfo {
} }
func (iu *UserLoggedIn) HasScope(param string, r *http.Request) (bool, error) { func (iu *UserLoggedIn) HasScope(param string, r *http.Request) (bool, error) {
user, _, err := iu.Dependencies.Auth.UserOf(r) user, _, err := iu.Dependencies.Auth.SessionOf(r)
return user != nil, err return user != nil, err
} }

View file

@ -18,27 +18,30 @@ import (
_ "embed" _ "embed"
) )
// UserOf returns the user logged into the provided request. // SessionOf returns the session and user logged into the provided request.
// token indicates if the user used a token to authenticate, or a browser session was used. // token indicates if the user used a token to authenticate, or a browser session was used.
// A token takes priority over a user in a session. // A token takes priority over a user in a session.
// //
// If there is no user associated with the given request, user and error are nil, and token is false. // If there is no user associated with the given request, user and error are nil, and token is false.
// An invalid session, expired token, or disabled user all result in user = nil. // An invalid session, expired token, or disabled user all result in user = nil.
// //
// When no UserOf exists in the given session returns nil. // When no SessionOf exists in the given session returns nil.
func (auth *Auth) UserOf(r *http.Request) (user *AuthUser, token bool, err error) { func (auth *Auth) SessionOf(r *http.Request) (session component.SessionInfo, user *AuthUser, err error) {
// check the user from the token first // check the user from the token first
{ {
user, err := auth.UserOfToken(r) user, err := auth.UserOfToken(r)
if user != nil && err == nil { if user != nil && err == nil {
return user, true, nil return component.SessionInfo{User: &user.User, Token: true}, user, nil
} }
} }
// fallback to using session // fallback to using session
{ {
user, err := auth.UserOfSession(r) user, err := auth.UserOfSession(r)
return user, false, err if err != nil {
return component.SessionInfo{}, nil, err
}
return component.SessionInfo{User: &user.User, Token: false}, user, nil
} }
} }

View file

@ -1,8 +1,11 @@
package component package component
import ( import (
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"github.com/FAU-CDI/wisski-distillery/internal/models"
) )
// Scope represents a single permit by a session to perform some action. // Scope represents a single permit by a session to perform some action.
@ -58,6 +61,36 @@ type ScopeProvider interface {
// Scopes returns information about the scope // Scopes returns information about the scope
Scope() ScopeInfo Scope() ScopeInfo
// Check checks if the given session has access to the given scope // Check checks if the given session has access to the given scope.
HasScope(param string, r *http.Request) (bool, error) HasScope(param string, r *http.Request) (bool, error)
// TODO: move this to a session
}
// SessionInfo provides information about the current session.
type SessionInfo struct {
// User is the current user associated with the session.
User *models.User
// Token indicates if the user was authenticated with a Token
Token bool
}
func (si SessionInfo) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
User string `json:"user"`
Token bool `json:"token"`
}{User: si.Username(), Token: si.Token})
}
// Username reports the username associated with this session
func (si SessionInfo) Username() string {
if si.User == nil {
return ""
}
return si.User.User
}
// Anonymous reports if this Session is associated with a user account
func (si SessionInfo) Anonymous() bool {
return si.Username() != ""
} }

View file

@ -42,7 +42,7 @@ func (li *ListInstances) ShouldShowList(r *http.Request) bool {
return allowPrivate return allowPrivate
} }
user, _, _ := li.Dependencies.Auth.UserOf(r) user, _, _ := li.Dependencies.Auth.SessionOf(r)
if user == nil { if user == nil {
return allowPublic return allowPublic
} else { } else {

View file

@ -7,14 +7,14 @@ const UserTable = "users"
type User struct { type User struct {
Pk uint `gorm:"column:pk;primaryKey"` Pk uint `gorm:"column:pk;primaryKey"`
User string `gorm:"column:user;not null;unique"` // name of the user User string `gorm:"column:user;not null;unique"` // name of the user
PasswordHash []byte `gorm:"column:password"` // password of the user, hashed
TOTPEnabled *bool `gorm:"column:totpenabled"` // is totp enabled for the user PasswordHash []byte `gorm:"column:password" json:"-"` // password of the user, hashed
TOTPURL string `gorm:"column:totp"` // the totp of the user TOTPEnabled *bool `gorm:"column:totpenabled" json:"-"` // is totp enabled for the user
TOTPURL string `gorm:"column:totp" json:"-"` // the totp of the user
Enabled *bool `gorm:"enabled;not null"` Enabled *bool `gorm:"enabled;not null" json:"enabled"`
Admin *bool `gorm:"column:admin;not null"` Admin *bool `gorm:"column:admin;not null" json:"admin"`
} }
func (user *User) HasPassword() bool { func (user *User) HasPassword() bool {