Improvements for tokens

This commit is contained in:
Tom 2023-06-29 08:59:19 +02:00
parent effa79aacd
commit 8ccd490bed
15 changed files with 34 additions and 30 deletions

1
API.md
View file

@ -21,6 +21,7 @@ All routes can be found under `/api/v1/http/`
- `/api/v1/systems`: Returns a (publically visible) list of systems
- `/api/v1/news`: Returns JSON containing all news items
## Interactive Websocket API
Some API calls require interactivity or provide streaming content to clients.

2
go.mod
View file

@ -16,7 +16,7 @@ require (
github.com/pquerna/otp v1.4.0
github.com/rs/zerolog v1.29.1
github.com/tkw1536/goprogram v0.3.5
github.com/tkw1536/pkglib v0.0.0-20230530085130-4f049f64e420
github.com/tkw1536/pkglib v0.0.0-20230629065114-9b97337c75a0
github.com/yuin/goldmark v1.5.4
github.com/yuin/goldmark-meta v1.1.0
golang.org/x/crypto v0.8.0

4
go.sum
View file

@ -114,8 +114,8 @@ github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtp
github.com/tdewolff/test v1.0.7 h1:8Vs0142DmPFW/bQeHRP3MV19m1gvndjUb1sn8yy74LM=
github.com/tkw1536/goprogram v0.3.5 h1:S0axKo3R/vGa4zhYqYDKAZEPhAfwUSSeMtVwnAu4sNY=
github.com/tkw1536/goprogram v0.3.5/go.mod h1:pYr4dMHOSVurbPQ4KTR0ett8XWNISbsRS6zlh9Nsxa8=
github.com/tkw1536/pkglib v0.0.0-20230530085130-4f049f64e420 h1:ZZ7Hhf4cMKXWbOfh6A1FWmhOgD8US0Tz9Ky2fPiaZ3I=
github.com/tkw1536/pkglib v0.0.0-20230530085130-4f049f64e420/go.mod h1:0A1B9Cc5+yJXR3eeB14CqD4dFSbEjjWRo5Pr9M3XYuI=
github.com/tkw1536/pkglib v0.0.0-20230629065114-9b97337c75a0 h1:M1iZRhxJtw9SZSGjowf1IpZx96U5fQK/c3uhK9vZOxk=
github.com/tkw1536/pkglib v0.0.0-20230629065114-9b97337c75a0/go.mod h1:0A1B9Cc5+yJXR3eeB14CqD4dFSbEjjWRo5Pr9M3XYuI=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=

View file

@ -43,11 +43,9 @@ func (a *API) HandleRoute(ctx context.Context, path string) (http.Handler, error
Methods: []string{"GET"},
Handler: func(s string, r *http.Request) (ai AuthInfo, err error) {
var user *auth.AuthUser
user, err = a.Dependencies.Auth.SessionOf(r)
if user != nil {
ai.User = user.User.User
}
session, _, err := a.Dependencies.Auth.SessionOf(r)
ai.User = session.Username()
ai.Token = session.Token
return
},
}, nil

View file

@ -31,7 +31,7 @@ var (
func (next *Next) Routes() component.Routes {
return component.Routes{
Prefix: "/next/",
Decorator: next.Dependencies.Auth.Require(true, scopes.ScopeUserLoggedIn, nil),
Decorator: next.Dependencies.Auth.Require(true, scopes.ScopeUserValid, nil),
}
}

View file

@ -42,7 +42,7 @@ func (panel *UserPanel) Routes() component.Routes {
return component.Routes{
Prefix: "/user/",
CSRF: true,
Decorator: panel.Dependencies.Auth.Require(false, scopes.ScopeUserLoggedIn, nil),
Decorator: panel.Dependencies.Auth.Require(false, scopes.ScopeUserValid, nil),
}
}
@ -137,7 +137,7 @@ func (panel *UserPanel) HandleRoute(ctx context.Context, route string) (http.Han
}
// ensure that the user is logged in!
return panel.Dependencies.Auth.Protect(router, false, scopes.ScopeUserLoggedIn, nil), nil
return panel.Dependencies.Auth.Protect(router, false, scopes.ScopeUserValid, nil), nil
}
type userFormContext struct {

View file

@ -62,7 +62,7 @@ func (panel *UserPanel) routeUser(ctx context.Context) http.Handler {
return uc, nil, err
}
uc.ShowAdminURLs = panel.Dependencies.Auth.CheckScope("", scopes.ScopeAdminLoggedIn, r) == nil
uc.ShowAdminURLs = panel.Dependencies.Auth.CheckScope("", scopes.ScopeUserAdmin, r) == nil
// replace the totp action in the menu
var totpAction component.MenuItem

View file

@ -22,14 +22,14 @@ func (auth *Auth) Protect(handler http.Handler, AllowToken bool, scope component
var paramValue string
// load the user in the session
// TODO<tokens>: Check if API access is allowed
user, token, err := auth.SessionOf(r)
// TODO: In a future version of sessions, check if token has the permitted scope.
session, user, err := auth.SessionOf(r)
if err != nil {
goto err
}
// token was set, but not allowed!
if token && !AllowToken {
if session.Token && !AllowToken {
goto forbidden
}

View file

@ -19,19 +19,19 @@ var (
)
const (
ScopeAdminLoggedIn Scope = "login.admin"
ScopeUserAdmin Scope = "user.admin"
)
func (*AdminLoggedIn) Scope() component.ScopeInfo {
return component.ScopeInfo{
Scope: ScopeAdminLoggedIn,
Description: "session has a signed in admin",
DeniedMessage: "user must be signed into an admin account with TOTP enabled",
Scope: ScopeUserAdmin,
Description: "session must have a valid admin",
DeniedMessage: "user must have an admin account with TOTP enabled",
TakesParam: false,
}
}
func (al *AdminLoggedIn) HasScope(param string, r *http.Request) (bool, error) {
user, _, err := al.Dependencies.Auth.SessionOf(r)
_, user, err := al.Dependencies.Auth.SessionOf(r)
return user != nil && user.IsAdmin() && user.IsTOTPEnabled(), err
}

View file

@ -19,18 +19,18 @@ var (
)
const (
ScopeUserLoggedIn Scope = "login.user"
ScopeUserValid Scope = "user.valid"
)
func (*UserLoggedIn) Scope() component.ScopeInfo {
return component.ScopeInfo{
Scope: ScopeUserLoggedIn,
Description: "session has an associated user",
Scope: ScopeUserValid,
Description: "session must have a valid user",
TakesParam: false,
}
}
func (iu *UserLoggedIn) HasScope(param string, r *http.Request) (bool, error) {
user, _, err := iu.Dependencies.Auth.SessionOf(r)
_, user, err := iu.Dependencies.Auth.SessionOf(r)
return user != nil, err
}

View file

@ -41,11 +41,15 @@ func (auth *Auth) SessionOf(r *http.Request) (session component.SessionInfo, use
if err != nil {
return component.SessionInfo{}, nil, err
}
if user == nil {
return component.SessionInfo{}, nil, nil
}
return component.SessionInfo{User: &user.User, Token: false}, user, nil
}
}
// UserOfToken returns the user associated with the token in request.
// To check the user of a token or session, use SessionOf.
func (auth *Auth) UserOfToken(r *http.Request) (user *AuthUser, err error) {
// get the token object
token, err := auth.Dependencies.Tokens.TokenOf(r)

View file

@ -103,7 +103,7 @@ func (resolver *Resolver) HandleRoute(ctx context.Context, route string) (http.H
IndexContext: context,
}
if resolver.Dependencies.Auth.CheckScope("", scopes.ScopeUserLoggedIn, r) != nil {
if resolver.Dependencies.Auth.CheckScope("", scopes.ScopeUserValid, r) != nil {
ctx.IndexContext.Prefixes = nil
}
httpx.WriteHTML(tpl.Context(r, ctx), nil, t, "", w, r)

View file

@ -47,12 +47,12 @@ func (admin *Admin) Routes() component.Routes {
return component.Routes{
Prefix: "/admin/",
CSRF: true,
Decorator: admin.Dependencies.Auth.Require(false, scopes.ScopeAdminLoggedIn, nil),
Decorator: admin.Dependencies.Auth.Require(false, scopes.ScopeUserAdmin, nil),
}
}
func (admin *Admin) Menu(r *http.Request) []component.MenuItem {
if admin.Dependencies.Auth.CheckScope("", scopes.ScopeAdminLoggedIn, r) != nil {
if admin.Dependencies.Auth.CheckScope("", scopes.ScopeUserAdmin, r) != nil {
return nil
}
return []component.MenuItem{

View file

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

View file

@ -45,10 +45,11 @@ func (server *Server) Server(ctx context.Context, progress io.Writer) (public ht
DefaultDomain: slug == "" && ok,
}
}
publicM.Panic = func(panic any, w http.ResponseWriter, r *http.Request) {
publicM.Panic = func(p any, stack []byte, w http.ResponseWriter, r *http.Request) {
// log the panic
logger.Error().
Str("panic", fmt.Sprint(panic)).
Str("panic", fmt.Sprint(p)).
Str("stack", string(stack)).
Str("path", r.URL.Path).
Msg("panic serving handler")