From 8cfaceb3037957bfe2db453f8342384107c395a0 Mon Sep 17 00:00:00 2001 From: Johannes Kirchner <124351955+johannes-kirchner@users.noreply.github.com> Date: Wed, 2 Apr 2025 13:38:11 +0200 Subject: [PATCH] feat: read admin group from OIDC token claim (#445) --- config.yml | 4 ++++ docs/configuration/oauth-providers.md | 17 +++++++++++++- internal/config/config.go | 10 ++++---- internal/web/handlers/auth/oauth.go | 33 +++++++++++++++++++++++++-- templates/pages/admin_config.html | 2 ++ 5 files changed, 59 insertions(+), 7 deletions(-) diff --git a/config.yml b/config.yml index 0a891bb..5b47413 100644 --- a/config.yml +++ b/config.yml @@ -106,6 +106,10 @@ oidc.client-key: oidc.secret: # Discovery endpoint of the OpenID provider. Generally something like http://auth.example.com/.well-known/openid-configuration oidc.discovery-url: +# The name of the claim containing the groups +oidc.group-claim-name: +# The name of the group that should receive admin rights +oidc.admin-group: # Instance name # Set your own custom name to be displayed instead of 'Opengist' diff --git a/docs/configuration/oauth-providers.md b/docs/configuration/oauth-providers.md index dd45873..e34139c 100644 --- a/docs/configuration/oauth-providers.md +++ b/docs/configuration/oauth-providers.md @@ -76,4 +76,19 @@ Opengist can be configured to use OAuth to authenticate users, with GitHub, Gite # Discovery endpoint of the OpenID provider. Generally something like http://auth.example.com/.well-known/openid-configuration OG_OIDC_DISCOVERY_URL=http://auth.example.com/.well-known/openid-configuration ``` - \ No newline at end of file + +### OIDC Admin Group + +OpenGist supports automatic admin privilege assignment based on OIDC group claims. To configure this feature: +```yaml +oidc.group-claim-name: groups # Name of the claim containing the groups +oidc.admin-group: admin-group-name # Name of the group that should receive admin rights +``` +```shell +OG_OIDC_GROUP_CLAIM_NAME=groups +OG_OIDC_ADMIN_GROUP=admin-group-name +``` + +The `group-claim-name` must match the name of the claim in your JWT token that contains the groups. + +Users who are members of the configured `admin-group` will automatically receive admin privileges in OpenGist. These privileges are synchronized on every login. diff --git a/internal/config/config.go b/internal/config/config.go index b1aa187..fabac84 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -70,10 +70,12 @@ type config struct { GiteaUrl string `yaml:"gitea.url" env:"OG_GITEA_URL"` GiteaName string `yaml:"gitea.name" env:"OG_GITEA_NAME"` - OIDCProviderName string `yaml:"oidc.provider-name" env:"OG_OIDC_PROVIDER_NAME"` - OIDCClientKey string `yaml:"oidc.client-key" env:"OG_OIDC_CLIENT_KEY"` - OIDCSecret string `yaml:"oidc.secret" env:"OG_OIDC_SECRET"` - OIDCDiscoveryUrl string `yaml:"oidc.discovery-url" env:"OG_OIDC_DISCOVERY_URL"` + OIDCProviderName string `yaml:"oidc.provider-name" env:"OG_OIDC_PROVIDER_NAME"` + OIDCClientKey string `yaml:"oidc.client-key" env:"OG_OIDC_CLIENT_KEY"` + OIDCSecret string `yaml:"oidc.secret" env:"OG_OIDC_SECRET"` + OIDCDiscoveryUrl string `yaml:"oidc.discovery-url" env:"OG_OIDC_DISCOVERY_URL"` + OIDCGroupClaimName string `yaml:"oidc.group-claim-name" env:"OG_OIDC_GROUP_CLAIM_NAME"` + OIDCAdminGroup string `yaml:"oidc.admin-group" env:"OG_OIDC_ADMIN_GROUP"` MetricsEnabled bool `yaml:"metrics.enabled" env:"OG_METRICS_ENABLED"` diff --git a/internal/web/handlers/auth/oauth.go b/internal/web/handlers/auth/oauth.go index f846155..fd1831f 100644 --- a/internal/web/handlers/auth/oauth.go +++ b/internal/web/handlers/auth/oauth.go @@ -4,6 +4,9 @@ import ( "crypto/md5" "errors" "fmt" + "slices" + "strings" + "github.com/rs/zerolog/log" "github.com/thomiceli/opengist/internal/auth/oauth" "github.com/thomiceli/opengist/internal/config" @@ -12,7 +15,6 @@ import ( "golang.org/x/text/cases" "golang.org/x/text/language" "gorm.io/gorm" - "strings" ) func Oauth(ctx *context.Context) error { @@ -110,7 +112,8 @@ func OauthCallback(ctx *context.Context) error { return ctx.ErrorRes(500, "Cannot create user", err) } - if userDB.ID == 1 { + // if oidc admin group is not configured set first user as admin + if config.C.OIDCAdminGroup == "" && userDB.ID == 1 { if err = userDB.SetAdmin(); err != nil { return ctx.ErrorRes(500, "Cannot set user admin", err) } @@ -136,6 +139,32 @@ func OauthCallback(ctx *context.Context) error { } } + // update is admin status from oidc group + if config.C.OIDCAdminGroup != "" { + groupClaimName := config.C.OIDCGroupClaimName + if groupClaimName == "" { + log.Error().Msg("No OIDC group claim name configured") + } else if groups, ok := user.RawData[groupClaimName].([]interface{}); ok { + var groupNames []string + for _, group := range groups { + if groupName, ok := group.(string); ok { + groupNames = append(groupNames, groupName) + } + } + isOIDCAdmin := slices.Contains(groupNames, config.C.OIDCAdminGroup) + log.Debug().Bool("isOIDCAdmin", isOIDCAdmin).Str("user", user.Name).Msg("User is in admin group") + + if userDB.IsAdmin != isOIDCAdmin { + userDB.IsAdmin = isOIDCAdmin + if err = userDB.Update(); err != nil { + return ctx.ErrorRes(500, "Cannot set user admin", err) + } + } + } else { + log.Error().Msg("No groups found in user data") + } + } + sess := ctx.GetSession() sess.Values["user"] = userDB.ID ctx.SaveSession(sess) diff --git a/templates/pages/admin_config.html b/templates/pages/admin_config.html index c4311f3..5cfdc7d 100644 --- a/templates/pages/admin_config.html +++ b/templates/pages/admin_config.html @@ -69,6 +69,8 @@