forked from mirrors/gotosocial
[feature] Make OIDC admin groups configurable (#1555)
This removes the current default of checking for membership of the admin or admins group and makes it required to explicitly configure which groups should grant admin access, if any. Relying on the implicit default of admin or admins is potentially dangerous as that group may contain a different subset of people that we may wish to grant admin access to GtS. This is probably not an issue for a single-person instance, but for a community instance different admin groups may exist in an OIDC provider for different applications. I'm explicitly opting for not defaulting the value of oidc-admin-groups to admin,admins because I think it's better for those things to be explicitly configured.
This commit is contained in:
parent
c27b4d7ed0
commit
9cfb69f75d
6 changed files with 49 additions and 6 deletions
|
@ -60,7 +60,7 @@ oidc-client-secret: ""
|
||||||
# Array of string. Scopes to request from the OIDC provider. The returned values will be used to
|
# Array of string. Scopes to request from the OIDC provider. The returned values will be used to
|
||||||
# populate users created in GtS as a result of the authentication flow. 'openid' and 'email' are required.
|
# populate users created in GtS as a result of the authentication flow. 'openid' and 'email' are required.
|
||||||
# 'profile' is used to extract a username for the newly created user.
|
# 'profile' is used to extract a username for the newly created user.
|
||||||
# 'groups' is optional and can be used to determine if a user is an admin (if they're in the group 'admin' or 'admins').
|
# 'groups' is optional and can be used to determine if a user is an admin based on oidc-admin-groups.
|
||||||
# Examples: See eg., https://auth0.com/docs/scopes/openid-connect-scopes
|
# Examples: See eg., https://auth0.com/docs/scopes/openid-connect-scopes
|
||||||
# Default: ["openid", "email", "profile", "groups"]
|
# Default: ["openid", "email", "profile", "groups"]
|
||||||
oidc-scopes:
|
oidc-scopes:
|
||||||
|
@ -75,6 +75,12 @@ oidc-scopes:
|
||||||
# Options: [true, false]
|
# Options: [true, false]
|
||||||
# Default: false
|
# Default: false
|
||||||
oidc-link-existing: false
|
oidc-link-existing: false
|
||||||
|
|
||||||
|
# Array of string. If the returned ID token contains a 'groups' claim that
|
||||||
|
# matches one of the groups in oidc-admin-groups, then this user will be granted
|
||||||
|
# admin rights on the GtS instance
|
||||||
|
# Default: []
|
||||||
|
oidc-admin-groups: []
|
||||||
```
|
```
|
||||||
|
|
||||||
## Behavior
|
## Behavior
|
||||||
|
@ -101,7 +107,7 @@ access to your GtS account.
|
||||||
|
|
||||||
Most OIDC providers allow for the concept of groups and group memberships in returned claims. GoToSocial can use group membership to determine whether or not a user returned from an OIDC flow should be created as an admin account or not.
|
Most OIDC providers allow for the concept of groups and group memberships in returned claims. GoToSocial can use group membership to determine whether or not a user returned from an OIDC flow should be created as an admin account or not.
|
||||||
|
|
||||||
If the returned OIDC groups information for a user contains membership of the groups `admin` or `admins`, then that user will be created/signed in as though they are an admin.
|
If the returned OIDC groups information for a user contains membership of the groups configured in `oidc-admin-groups`, then that user will be created/signed in as though they are an admin.
|
||||||
|
|
||||||
## Migrating from old versions
|
## Migrating from old versions
|
||||||
|
|
||||||
|
|
|
@ -616,7 +616,7 @@ oidc-client-secret: ""
|
||||||
# Array of string. Scopes to request from the OIDC provider. The returned values will be used to
|
# Array of string. Scopes to request from the OIDC provider. The returned values will be used to
|
||||||
# populate users created in GtS as a result of the authentication flow. 'openid' and 'email' are required.
|
# populate users created in GtS as a result of the authentication flow. 'openid' and 'email' are required.
|
||||||
# 'profile' is used to extract a username for the newly created user.
|
# 'profile' is used to extract a username for the newly created user.
|
||||||
# 'groups' is optional and can be used to determine if a user is an admin (if they're in the group 'admin' or 'admins').
|
# 'groups' is optional and can be used to determine if a user is an admin based on oidc-admin-groups.
|
||||||
# Examples: See eg., https://auth0.com/docs/scopes/openid-connect-scopes
|
# Examples: See eg., https://auth0.com/docs/scopes/openid-connect-scopes
|
||||||
# Default: ["openid", "email", "profile", "groups"]
|
# Default: ["openid", "email", "profile", "groups"]
|
||||||
oidc-scopes:
|
oidc-scopes:
|
||||||
|
@ -632,6 +632,11 @@ oidc-scopes:
|
||||||
# Default: false
|
# Default: false
|
||||||
oidc-link-existing: false
|
oidc-link-existing: false
|
||||||
|
|
||||||
|
# Array of string. If the returned ID token contains a 'groups' claim that matches one of the
|
||||||
|
# groups in oidc-admin-groups, then this user will be granted admin rights on the GtS instance
|
||||||
|
# Default: []
|
||||||
|
oidc-admin-groups: []
|
||||||
|
|
||||||
#######################
|
#######################
|
||||||
##### SMTP CONFIG #####
|
##### SMTP CONFIG #####
|
||||||
#######################
|
#######################
|
||||||
|
|
|
@ -284,10 +284,15 @@ func (m *Module) createUserFromOIDC(ctx context.Context, claims *oidc.Claims, ex
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the user is in any recognised admin groups
|
// check if the user is in any recognised admin groups
|
||||||
|
adminGroups := config.GetOIDCAdminGroups()
|
||||||
var admin bool
|
var admin bool
|
||||||
|
LOOP:
|
||||||
for _, g := range claims.Groups {
|
for _, g := range claims.Groups {
|
||||||
if strings.EqualFold(g, "admin") || strings.EqualFold(g, "admins") {
|
for _, ag := range adminGroups {
|
||||||
|
if strings.EqualFold(g, ag) {
|
||||||
admin = true
|
admin = true
|
||||||
|
break LOOP
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,6 +122,7 @@ type Configuration struct {
|
||||||
OIDCClientSecret string `name:"oidc-client-secret" usage:"ClientSecret of GoToSocial, as registered with the OIDC provider."`
|
OIDCClientSecret string `name:"oidc-client-secret" usage:"ClientSecret of GoToSocial, as registered with the OIDC provider."`
|
||||||
OIDCScopes []string `name:"oidc-scopes" usage:"OIDC scopes."`
|
OIDCScopes []string `name:"oidc-scopes" usage:"OIDC scopes."`
|
||||||
OIDCLinkExisting bool `name:"oidc-link-existing" usage:"link existing user accounts to OIDC logins based on the stored email value"`
|
OIDCLinkExisting bool `name:"oidc-link-existing" usage:"link existing user accounts to OIDC logins based on the stored email value"`
|
||||||
|
OIDCAdminGroups []string `name:"oidc-admin-groups" usage:"Membership of one of the listed groups makes someone a GtS admin"`
|
||||||
|
|
||||||
SMTPHost string `name:"smtp-host" usage:"Host of the smtp server. Eg., 'smtp.eu.mailgun.org'"`
|
SMTPHost string `name:"smtp-host" usage:"Host of the smtp server. Eg., 'smtp.eu.mailgun.org'"`
|
||||||
SMTPPort int `name:"smtp-port" usage:"Port of the smtp server. Eg., 587"`
|
SMTPPort int `name:"smtp-port" usage:"Port of the smtp server. Eg., 587"`
|
||||||
|
|
|
@ -1724,6 +1724,31 @@ func GetOIDCLinkExisting() bool { return global.GetOIDCLinkExisting() }
|
||||||
// SetOIDCLinkExisting safely sets the value for global configuration 'OIDCLinkExisting' field
|
// SetOIDCLinkExisting safely sets the value for global configuration 'OIDCLinkExisting' field
|
||||||
func SetOIDCLinkExisting(v bool) { global.SetOIDCLinkExisting(v) }
|
func SetOIDCLinkExisting(v bool) { global.SetOIDCLinkExisting(v) }
|
||||||
|
|
||||||
|
// GetOIDCAdminGroups safely fetches the Configuration value for state's 'OIDCAdminGroups' field
|
||||||
|
func (st *ConfigState) GetOIDCAdminGroups() (v []string) {
|
||||||
|
st.mutex.Lock()
|
||||||
|
v = st.config.OIDCAdminGroups
|
||||||
|
st.mutex.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOIDCAdminGroups safely sets the Configuration value for state's 'OIDCAdminGroups' field
|
||||||
|
func (st *ConfigState) SetOIDCAdminGroups(v []string) {
|
||||||
|
st.mutex.Lock()
|
||||||
|
defer st.mutex.Unlock()
|
||||||
|
st.config.OIDCAdminGroups = v
|
||||||
|
st.reloadToViper()
|
||||||
|
}
|
||||||
|
|
||||||
|
// OIDCAdminGroupsFlag returns the flag name for the 'OIDCAdminGroups' field
|
||||||
|
func OIDCAdminGroupsFlag() string { return "oidc-admin-groups" }
|
||||||
|
|
||||||
|
// GetOIDCAdminGroups safely fetches the value for global configuration 'OIDCAdminGroups' field
|
||||||
|
func GetOIDCAdminGroups() []string { return global.GetOIDCAdminGroups() }
|
||||||
|
|
||||||
|
// SetOIDCAdminGroups safely sets the value for global configuration 'OIDCAdminGroups' field
|
||||||
|
func SetOIDCAdminGroups(v []string) { global.SetOIDCAdminGroups(v) }
|
||||||
|
|
||||||
// GetSMTPHost safely fetches the Configuration value for state's 'SMTPHost' field
|
// GetSMTPHost safely fetches the Configuration value for state's 'SMTPHost' field
|
||||||
func (st *ConfigState) GetSMTPHost() (v string) {
|
func (st *ConfigState) GetSMTPHost() (v string) {
|
||||||
st.mutex.Lock()
|
st.mutex.Lock()
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
EXPECT='{"account-domain":"peepee","accounts-allow-custom-css":true,"accounts-approval-required":false,"accounts-reason-required":false,"accounts-registration-open":true,"advanced-cookies-samesite":"strict","advanced-rate-limit-requests":6969,"advanced-throttling-multiplier":-1,"advanced-throttling-retry-after":10000000000,"application-name":"gts","bind-address":"127.0.0.1","cache":{"gts":{"account-max-size":99,"account-sweep-freq":1000000000,"account-ttl":10800000000000,"block-max-size":100,"block-sweep-freq":10000000000,"block-ttl":300000000000,"domain-block-max-size":1000,"domain-block-sweep-freq":60000000000,"domain-block-ttl":86400000000000,"emoji-category-max-size":100,"emoji-category-sweep-freq":10000000000,"emoji-category-ttl":300000000000,"emoji-max-size":500,"emoji-sweep-freq":10000000000,"emoji-ttl":300000000000,"mention-max-size":500,"mention-sweep-freq":10000000000,"mention-ttl":300000000000,"notification-max-size":500,"notification-sweep-freq":10000000000,"notification-ttl":300000000000,"report-max-size":100,"report-sweep-freq":10000000000,"report-ttl":300000000000,"status-max-size":500,"status-sweep-freq":10000000000,"status-ttl":300000000000,"tombstone-max-size":100,"tombstone-sweep-freq":10000000000,"tombstone-ttl":300000000000,"user-max-size":100,"user-sweep-freq":10000000000,"user-ttl":300000000000}},"config-path":"internal/config/testdata/test.yaml","db-address":":memory:","db-database":"gotosocial_prod","db-max-open-conns-multiplier":3,"db-password":"hunter2","db-port":6969,"db-sqlite-busy-timeout":1000000000,"db-sqlite-cache-size":0,"db-sqlite-journal-mode":"DELETE","db-sqlite-synchronous":"FULL","db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"sqlite","db-user":"sex-haver","dry-run":true,"email":"","host":"example.com","instance-deliver-to-shared-inboxes":false,"instance-expose-peers":true,"instance-expose-public-timeline":true,"instance-expose-suspended":true,"instance-expose-suspended-web":true,"landing-page-user":"admin","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-db-queries":true,"log-level":"info","media-description-max-chars":5000,"media-description-min-chars":69,"media-emoji-local-max-size":420,"media-emoji-remote-max-size":420,"media-image-max-size":420,"media-remote-cache-days":30,"media-video-max-size":420,"oidc-client-id":"1234","oidc-client-secret":"shhhh its a secret","oidc-enabled":true,"oidc-idp-name":"sex-haver","oidc-issuer":"whoknows","oidc-link-existing":true,"oidc-scopes":["read","write"],"oidc-skip-verification":true,"password":"","path":"","port":6969,"protocol":"http","request-id-header":"X-Trace-Id","smtp-from":"queen.rip.in.piss@terfisland.org","smtp-host":"example.com","smtp-password":"hunter2","smtp-port":4269,"smtp-username":"sex-haver","software-version":"","statuses-cw-max-chars":420,"statuses-max-chars":69,"statuses-media-max-files":1,"statuses-poll-max-options":1,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-local-base-path":"/root/store","storage-s3-access-key":"minio","storage-s3-bucket":"gts","storage-s3-endpoint":"localhost:9000","storage-s3-proxy":true,"storage-s3-secret-key":"miniostorage","storage-s3-use-ssl":false,"syslog-address":"127.0.0.1:6969","syslog-enabled":true,"syslog-protocol":"udp","trusted-proxies":["127.0.0.1/32","docker.host.local"],"username":"","web-asset-base-dir":"/root","web-template-base-dir":"/root"}'
|
EXPECT='{"account-domain":"peepee","accounts-allow-custom-css":true,"accounts-approval-required":false,"accounts-reason-required":false,"accounts-registration-open":true,"advanced-cookies-samesite":"strict","advanced-rate-limit-requests":6969,"advanced-throttling-multiplier":-1,"advanced-throttling-retry-after":10000000000,"application-name":"gts","bind-address":"127.0.0.1","cache":{"gts":{"account-max-size":99,"account-sweep-freq":1000000000,"account-ttl":10800000000000,"block-max-size":100,"block-sweep-freq":10000000000,"block-ttl":300000000000,"domain-block-max-size":1000,"domain-block-sweep-freq":60000000000,"domain-block-ttl":86400000000000,"emoji-category-max-size":100,"emoji-category-sweep-freq":10000000000,"emoji-category-ttl":300000000000,"emoji-max-size":500,"emoji-sweep-freq":10000000000,"emoji-ttl":300000000000,"mention-max-size":500,"mention-sweep-freq":10000000000,"mention-ttl":300000000000,"notification-max-size":500,"notification-sweep-freq":10000000000,"notification-ttl":300000000000,"report-max-size":100,"report-sweep-freq":10000000000,"report-ttl":300000000000,"status-max-size":500,"status-sweep-freq":10000000000,"status-ttl":300000000000,"tombstone-max-size":100,"tombstone-sweep-freq":10000000000,"tombstone-ttl":300000000000,"user-max-size":100,"user-sweep-freq":10000000000,"user-ttl":300000000000}},"config-path":"internal/config/testdata/test.yaml","db-address":":memory:","db-database":"gotosocial_prod","db-max-open-conns-multiplier":3,"db-password":"hunter2","db-port":6969,"db-sqlite-busy-timeout":1000000000,"db-sqlite-cache-size":0,"db-sqlite-journal-mode":"DELETE","db-sqlite-synchronous":"FULL","db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"sqlite","db-user":"sex-haver","dry-run":true,"email":"","host":"example.com","instance-deliver-to-shared-inboxes":false,"instance-expose-peers":true,"instance-expose-public-timeline":true,"instance-expose-suspended":true,"instance-expose-suspended-web":true,"landing-page-user":"admin","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-db-queries":true,"log-level":"info","media-description-max-chars":5000,"media-description-min-chars":69,"media-emoji-local-max-size":420,"media-emoji-remote-max-size":420,"media-image-max-size":420,"media-remote-cache-days":30,"media-video-max-size":420,"oidc-admin-groups":["steamy"],"oidc-client-id":"1234","oidc-client-secret":"shhhh its a secret","oidc-enabled":true,"oidc-idp-name":"sex-haver","oidc-issuer":"whoknows","oidc-link-existing":true,"oidc-scopes":["read","write"],"oidc-skip-verification":true,"password":"","path":"","port":6969,"protocol":"http","request-id-header":"X-Trace-Id","smtp-from":"queen.rip.in.piss@terfisland.org","smtp-host":"example.com","smtp-password":"hunter2","smtp-port":4269,"smtp-username":"sex-haver","software-version":"","statuses-cw-max-chars":420,"statuses-max-chars":69,"statuses-media-max-files":1,"statuses-poll-max-options":1,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-local-base-path":"/root/store","storage-s3-access-key":"minio","storage-s3-bucket":"gts","storage-s3-endpoint":"localhost:9000","storage-s3-proxy":true,"storage-s3-secret-key":"miniostorage","storage-s3-use-ssl":false,"syslog-address":"127.0.0.1:6969","syslog-enabled":true,"syslog-protocol":"udp","trusted-proxies":["127.0.0.1/32","docker.host.local"],"username":"","web-asset-base-dir":"/root","web-template-base-dir":"/root"}'
|
||||||
|
|
||||||
# Set all the environment variables to
|
# Set all the environment variables to
|
||||||
# ensure that these are parsed without panic
|
# ensure that these are parsed without panic
|
||||||
|
@ -72,6 +72,7 @@ GTS_OIDC_CLIENT_ID='1234' \
|
||||||
GTS_OIDC_CLIENT_SECRET='shhhh its a secret' \
|
GTS_OIDC_CLIENT_SECRET='shhhh its a secret' \
|
||||||
GTS_OIDC_SCOPES='read,write' \
|
GTS_OIDC_SCOPES='read,write' \
|
||||||
GTS_OIDC_LINK_EXISTING=true \
|
GTS_OIDC_LINK_EXISTING=true \
|
||||||
|
GTS_OIDC_ADMIN_GROUPS='steamy' \
|
||||||
GTS_SMTP_HOST='example.com' \
|
GTS_SMTP_HOST='example.com' \
|
||||||
GTS_SMTP_PORT=4269 \
|
GTS_SMTP_PORT=4269 \
|
||||||
GTS_SMTP_USERNAME='sex-haver' \
|
GTS_SMTP_USERNAME='sex-haver' \
|
||||||
|
|
Loading…
Reference in a new issue