feature: Better guest handling (#472)

* feature: Better guest handling

* bugfix: Better handling of requiring guests to login
This commit is contained in:
James Read 2024-11-09 23:27:09 +00:00 committed by GitHub
parent 344df3739d
commit 13e48dded2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 47 additions and 20 deletions

View File

@ -33,6 +33,10 @@ type AuthenticatedUser struct {
acls []string acls []string
} }
func (u *AuthenticatedUser) IsGuest() bool {
return u.Username == "guest" && u.Provider == "system"
}
func logAclNotMatched(cfg *config.Config, aclFunction string, user *AuthenticatedUser, action *config.Action, acl *config.AccessControlList) { func logAclNotMatched(cfg *config.Config, aclFunction string, user *AuthenticatedUser, action *config.Action, acl *config.AccessControlList) {
if cfg.LogDebugOptions.AclNotMatched { if cfg.LogDebugOptions.AclNotMatched {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
@ -162,6 +166,7 @@ func UserFromContext(ctx context.Context, cfg *config.Config) *AuthenticatedUser
"username": ret.Username, "username": ret.Username,
"usergroup": ret.Usergroup, "usergroup": ret.Usergroup,
"provider": ret.Provider, "provider": ret.Provider,
"acls": ret.acls,
}).Debugf("UserFromContext") }).Debugf("UserFromContext")
return ret return ret

View File

@ -117,7 +117,7 @@ type Config struct {
AuthHttpHeaderUserGroup string AuthHttpHeaderUserGroup string
AuthLocalUsers AuthLocalUsersConfig AuthLocalUsers AuthLocalUsersConfig
AuthLoginUrl string AuthLoginUrl string
AuthAllowGuest bool AuthRequireGuestsToLogin bool
AuthOAuth2RedirectURL string AuthOAuth2RedirectURL string
AuthOAuth2Providers map[string]*OAuth2Provider AuthOAuth2Providers map[string]*OAuth2Provider
DefaultPermissions PermissionsList DefaultPermissions PermissionsList
@ -214,7 +214,7 @@ func DefaultConfigWithBasePort(basePort int) *Config {
config.DefaultPermissions.Logs = true config.DefaultPermissions.Logs = true
config.AuthJwtClaimUsername = "name" config.AuthJwtClaimUsername = "name"
config.AuthJwtClaimUserGroup = "group" config.AuthJwtClaimUserGroup = "group"
config.AuthAllowGuest = true config.AuthRequireGuestsToLogin = false
config.WebUIDir = "./webui" config.WebUIDir = "./webui"
config.CronSupportForSeconds = false config.CronSupportForSeconds = false
config.SectionNavigationStyle = "sidebar" config.SectionNavigationStyle = "sidebar"

View File

@ -36,6 +36,14 @@ func Reload(cfg *Config) {
os.Exit(1) os.Exit(1)
} }
if cfg.AuthRequireGuestsToLogin {
log.Infof("AuthRequireGuestsToLogin is enabled. All defaultPermissions will be set to false")
cfg.DefaultPermissions.View = false
cfg.DefaultPermissions.Exec = false
cfg.DefaultPermissions.Logs = false
}
metricConfigReloadedCount.Inc() metricConfigReloadedCount.Inc()
metricConfigActionCount.Set(float64(len(cfg.Actions))) metricConfigActionCount.Set(float64(len(cfg.Actions)))

View File

@ -296,6 +296,10 @@ func (api *oliveTinAPI) Logout(ctx ctx.Context, req *pb.LogoutRequest) (*httpbod
func (api *oliveTinAPI) GetDashboardComponents(ctx ctx.Context, req *pb.GetDashboardComponentsRequest) (*pb.GetDashboardComponentsResponse, error) { func (api *oliveTinAPI) GetDashboardComponents(ctx ctx.Context, req *pb.GetDashboardComponentsRequest) (*pb.GetDashboardComponentsResponse, error) {
user := acl.UserFromContext(ctx, cfg) user := acl.UserFromContext(ctx, cfg)
if user.IsGuest() && cfg.AuthRequireGuestsToLogin {
return nil, status.Errorf(codes.PermissionDenied, "Guests are not allowed to access the dashboard.")
}
res := buildDashboardResponse(api.executor, cfg, user) res := buildDashboardResponse(api.executor, cfg, user)
if len(res.Actions) == 0 { if len(res.Actions) == 0 {
@ -306,13 +310,6 @@ func (api *oliveTinAPI) GetDashboardComponents(ctx ctx.Context, req *pb.GetDashb
dashboardCfgToPb(res, cfg.Dashboards, cfg) dashboardCfgToPb(res, cfg.Dashboards, cfg)
res.AuthenticatedUser = user.Username
res.AuthenticatedUserProvider = user.Provider
if res.AuthenticatedUser == "guest" && !cfg.AuthAllowGuest {
return nil, status.Errorf(codes.PermissionDenied, "Unauthenticated")
}
return res, nil return res, nil
} }

View File

@ -10,7 +10,10 @@ import (
) )
func buildDashboardResponse(ex *executor.Executor, cfg *config.Config, user *acl.AuthenticatedUser) *pb.GetDashboardComponentsResponse { func buildDashboardResponse(ex *executor.Executor, cfg *config.Config, user *acl.AuthenticatedUser) *pb.GetDashboardComponentsResponse {
res := &pb.GetDashboardComponentsResponse{} res := &pb.GetDashboardComponentsResponse{
AuthenticatedUser: user.Username,
AuthenticatedUserProvider: user.Provider,
}
ex.MapActionIdToBindingLock.RLock() ex.MapActionIdToBindingLock.RLock()

View File

@ -164,7 +164,7 @@
<section id = "content-login" title = "Login" hidden> <section id = "content-login" title = "Login" hidden>
<div class = "flex-col"> <div class = "flex-col">
<form class = "box-shadow padded-content border-radius" id = "local-user-login"> <form class = "box-shadow padded-content border-radius" id = "local-user-login">
<p class = "login-disabled">Login with OAuth or Local users is not enabled on this server.</p> <p class = "login-disabled">This server is not configured with either OAuth, or local users, so you cannot login.</p>
<div class = "login-oauth2" hidden> <div class = "login-oauth2" hidden>
<h2>OAuth Login</h2> <h2>OAuth Login</h2>

View File

@ -65,25 +65,38 @@ export function initMarshaller () {
window.addEventListener('EventOutputChunk', onOutputChunk) window.addEventListener('EventOutputChunk', onOutputChunk)
} }
export function marshalDashboardComponentsJsonToHtml (json) { function setUsername (username, provider) {
marshalActionsJsonToHtml(json) document.getElementById('username').innerText = username
marshalDashboardStructureToHtml(json) document.getElementById('username').setAttribute('title', provider)
document.getElementById('username').innerText = json.authenticatedUser
if (window.settings.AuthLocalLogin || window.settings.AuthOAuth2Providers !== null) { if (window.settings.AuthLocalLogin || window.settings.AuthOAuth2Providers !== null) {
if (json.authenticatedUser === 'guest') { if (username === 'guest') {
document.getElementById('link-login').hidden = false document.getElementById('link-login').hidden = false
document.getElementById('link-logout').hidden = true document.getElementById('link-logout').hidden = true
} else { } else {
document.getElementById('link-login').hidden = true document.getElementById('link-login').hidden = true
if (json.authenticatedUserProvider === 'local' || json.authenticatedUserProvider === 'oauth2') { if (provider === 'local' || provider === 'oauth2') {
document.getElementById('link-logout').hidden = false document.getElementById('link-logout').hidden = false
} }
} }
}
}
document.getElementById('username').setAttribute('title', json.authenticatedUserProvider) export function marshalDashboardComponentsJsonToHtml (json) {
if (json == null) { // eg: HTTP 403
setUsername('guest', 'system')
if (window.settings.AuthLoginUrl !== '') {
window.location = window.settings.AuthLoginUrl
} else {
showSection('/login')
}
} else {
setUsername(json.authenticatedUser, json.authenticatedUserProvider)
marshalActionsJsonToHtml(json)
marshalDashboardStructureToHtml(json)
} }
document.body.setAttribute('initial-marshal-complete', 'true') document.body.setAttribute('initial-marshal-complete', 'true')

View File

@ -61,8 +61,9 @@ function fetchGetDashboardComponents () {
cors: 'cors' cors: 'cors'
}).then(res => { }).then(res => {
if (!res.ok && res.status === 403) { if (!res.ok && res.status === 403) {
window.location.href = window.settings.AuthLoginUrl return null
} }
return res.json() return res.json()
}).then(res => { }).then(res => {
if (!window.restAvailable) { if (!window.restAvailable) {