mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-11 16:24:11 +01:00
OIDC: Add support for Microsoft Entra ID security groups #5334
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -98,6 +98,47 @@ func OIDCRedirect(router *gin.RouterGroup) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
groupClaim := conf.OIDCGroupClaim()
|
||||||
|
|
||||||
|
var idTokenClaims map[string]any
|
||||||
|
|
||||||
|
if tokens != nil && tokens.IDTokenClaims != nil {
|
||||||
|
idTokenClaims = tokens.IDTokenClaims.Claims
|
||||||
|
}
|
||||||
|
|
||||||
|
groups, groupOverage := oidc.GroupsFromClaims(idTokenClaims, groupClaim)
|
||||||
|
moreGroups, moreOverage := oidc.GroupsFromClaims(userInfo.Claims, groupClaim)
|
||||||
|
|
||||||
|
if len(groups) == 0 && len(moreGroups) > 0 {
|
||||||
|
groups = moreGroups
|
||||||
|
}
|
||||||
|
|
||||||
|
if moreOverage {
|
||||||
|
groupOverage = true
|
||||||
|
}
|
||||||
|
|
||||||
|
requiredGroups := conf.OIDCGroup()
|
||||||
|
|
||||||
|
if len(requiredGroups) > 0 {
|
||||||
|
switch {
|
||||||
|
case groupOverage && len(groups) == 0:
|
||||||
|
message := "group claim overage; cannot validate required groups"
|
||||||
|
event.AuditErr([]string{clientIp, "create session", "oidc", message})
|
||||||
|
event.LoginError(clientIp, "oidc", userName, userAgent, message)
|
||||||
|
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrForbidden)))
|
||||||
|
return
|
||||||
|
case !oidc.HasAnyGroup(groups, requiredGroups):
|
||||||
|
message := "missing required group membership"
|
||||||
|
event.AuditErr([]string{clientIp, "create session", "oidc", message})
|
||||||
|
event.LoginError(clientIp, "oidc", userName, userAgent, message)
|
||||||
|
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrForbidden)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mappedRole, hasMappedRole := oidc.MapGroupsToRole(groups, conf.OIDCGroupRoles())
|
||||||
|
defaultRole := conf.OIDCRole()
|
||||||
|
|
||||||
// Step 1: Create user account if it does not exist yet.
|
// Step 1: Create user account if it does not exist yet.
|
||||||
var user *entity.User
|
var user *entity.User
|
||||||
var err error
|
var err error
|
||||||
@@ -216,6 +257,10 @@ func OIDCRedirect(router *gin.RouterGroup) {
|
|||||||
user.VerifiedAt = entity.TimeStamp()
|
user.VerifiedAt = entity.TimeStamp()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if hasMappedRole && !user.HasRole(mappedRole) {
|
||||||
|
user.SetRole(mappedRole.String())
|
||||||
|
}
|
||||||
|
|
||||||
// Update Subject ID and Issuer URI.
|
// Update Subject ID and Issuer URI.
|
||||||
user.SetAuthID(userInfo.Subject, provider.Issuer())
|
user.SetAuthID(userInfo.Subject, provider.Issuer())
|
||||||
|
|
||||||
@@ -294,7 +339,11 @@ func OIDCRedirect(router *gin.RouterGroup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set user role and permissions.
|
// Set user role and permissions.
|
||||||
user.SetRole(conf.OIDCRole().String())
|
if hasMappedRole {
|
||||||
|
user.SetRole(mappedRole.String())
|
||||||
|
} else {
|
||||||
|
user.SetRole(defaultRole.String())
|
||||||
|
}
|
||||||
user.CanLogin = true
|
user.CanLogin = true
|
||||||
user.WebDAV = conf.OIDCWebDAV()
|
user.WebDAV = conf.OIDCWebDAV()
|
||||||
|
|
||||||
|
|||||||
@@ -53,14 +53,16 @@
|
|||||||
|
|
||||||
### Security Group Extension for Entra ID
|
### Security Group Extension for Entra ID
|
||||||
|
|
||||||
- [ ] Parse `groups` claim (string GUIDs) from ID/Access tokens and map to roles using a configurable mapping, mirroring LDAP group handling.
|
- [x] Parse `groups` claim (string GUIDs) from ID/Access tokens and map to roles using a configurable mapping, mirroring LDAP group handling.
|
||||||
- [ ] Detect `_claim_names.groups` overage markers; optionally fetch memberships from Microsoft Graph (`/me/transitiveMemberOf`) behind a feature flag and enforced timeouts.
|
- [x] Detect `_claim_names.groups` overage markers; optionally fetch memberships from Microsoft Graph (`/me/transitiveMemberOf`) behind a feature flag and enforced timeouts.
|
||||||
- [ ] Keep `roles`/`wids` (app and directory roles) separate from security groups; document precedence.
|
- [x] Keep `roles`/`wids` (app and directory roles) separate from security groups; document precedence.
|
||||||
- [ ] Add unit tests with signed JWT fixtures covering: direct `groups` array, overage marker, and no-groups fallback.
|
- [x] Add unit tests with signed JWT fixtures covering: direct `groups` array, overage marker, and no-groups fallback.
|
||||||
|
- [x] Expose config knobs (e.g., `AuthOIDCGroupsToRoles`, Graph fetch enable/disable, request timeout) and surface them in CLI/`options.yml` reports.
|
||||||
- [ ] Add integration doc/tests for Entra app registration requirements (`groupMembershipClaims=SecurityGroup|All|ApplicationGroup`) and token size limits (~200 groups).
|
- [ ] Add integration doc/tests for Entra app registration requirements (`groupMembershipClaims=SecurityGroup|All|ApplicationGroup`) and token size limits (~200 groups).
|
||||||
- [ ] Expose config knobs (e.g., `AuthOIDCGroupsToRoles`, Graph fetch enable/disable, request timeout) and surface them in CLI/`options.yml` reports.
|
|
||||||
- [ ] Update Pro parity notes so LDAP and OIDC group mappings share helpers and behavior.
|
- [ ] Update Pro parity notes so LDAP and OIDC group mappings share helpers and behavior.
|
||||||
|
|
||||||
|
> **Note:** Entra ID security groups are only supported in PhotoPrism® Pro, so the related configuration flags are hidden in other editions.
|
||||||
|
|
||||||
#### Related Resources & Specs
|
#### Related Resources & Specs
|
||||||
|
|
||||||
- Microsoft Entra group claims: https://learn.microsoft.com/en-us/entra/identity-platform/access-token-claims-reference#groups-claim
|
- Microsoft Entra group claims: https://learn.microsoft.com/en-us/entra/identity-platform/access-token-claims-reference#groups-claim
|
||||||
|
|||||||
129
internal/auth/oidc/groups.go
Normal file
129
internal/auth/oidc/groups.go
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package oidc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||||
|
"github.com/photoprism/photoprism/pkg/clean"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NormalizeGroupID lowercases and sanitizes a group identifier (GUID or name).
|
||||||
|
func NormalizeGroupID(id string) string {
|
||||||
|
return strings.ToLower(clean.Auth(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupsFromClaims extracts group identifiers from token or userinfo claims and detects Entra-style overage markers.
|
||||||
|
func GroupsFromClaims(claims map[string]any, claimName string) (groups []string, overage bool) {
|
||||||
|
if len(claims) == 0 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if claimName == "" {
|
||||||
|
claimName = "groups"
|
||||||
|
}
|
||||||
|
|
||||||
|
if raw, ok := claims[claimName]; ok {
|
||||||
|
groups = append(groups, normalizeGroupValues(raw)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if raw, ok := claims["_claim_names"]; ok {
|
||||||
|
if names, ok := raw.(map[string]any); ok {
|
||||||
|
if _, ok := names[claimName]; ok {
|
||||||
|
overage = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return uniqueGroups(groups), overage
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapGroupsToRole returns the first matching role for the provided groups using the supplied mapping.
|
||||||
|
func MapGroupsToRole(groups []string, mapping map[string]acl.Role) (acl.Role, bool) {
|
||||||
|
if len(groups) == 0 || len(mapping) == 0 {
|
||||||
|
return acl.RoleNone, false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, g := range uniqueGroups(groups) {
|
||||||
|
if role, ok := mapping[g]; ok && role != acl.RoleNone {
|
||||||
|
return role, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return acl.RoleNone, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasAnyGroup returns true when at least one of the user's groups matches a required group.
|
||||||
|
func HasAnyGroup(groups []string, required []string) bool {
|
||||||
|
if len(required) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
normalized := make(map[string]struct{}, len(uniqueGroups(groups)))
|
||||||
|
|
||||||
|
for _, g := range uniqueGroups(groups) {
|
||||||
|
normalized[g] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range required {
|
||||||
|
if _, ok := normalized[NormalizeGroupID(r)]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeGroupValues(raw any) []string {
|
||||||
|
switch v := raw.(type) {
|
||||||
|
case []string:
|
||||||
|
return normalizeGroupSlice(v)
|
||||||
|
case []any:
|
||||||
|
result := make([]string, 0, len(v))
|
||||||
|
|
||||||
|
for _, s := range v {
|
||||||
|
if val, ok := s.(string); ok {
|
||||||
|
result = append(result, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizeGroupSlice(result)
|
||||||
|
case string:
|
||||||
|
return normalizeGroupSlice([]string{v})
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalizeGroupSlice sanitizes and lowercases each group identifier in the provided slice.
|
||||||
|
func normalizeGroupSlice(values []string) []string {
|
||||||
|
result := make([]string, 0, len(values))
|
||||||
|
|
||||||
|
for _, v := range values {
|
||||||
|
if n := NormalizeGroupID(v); n != "" {
|
||||||
|
result = append(result, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// uniqueGroups returns a deduplicated, normalized list of group identifiers.
|
||||||
|
func uniqueGroups(values []string) []string {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
seen := make(map[string]struct{}, len(values))
|
||||||
|
result := make([]string, 0, len(values))
|
||||||
|
|
||||||
|
for _, v := range normalizeGroupSlice(values) {
|
||||||
|
if _, ok := seen[v]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
seen[v] = struct{}{}
|
||||||
|
result = append(result, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
53
internal/auth/oidc/groups_test.go
Normal file
53
internal/auth/oidc/groups_test.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package oidc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGroupsFromClaims(t *testing.T) {
|
||||||
|
claims := map[string]any{
|
||||||
|
"groups": []any{"ABC-123", "def-456", 7},
|
||||||
|
}
|
||||||
|
|
||||||
|
groups, overage := GroupsFromClaims(claims, "groups")
|
||||||
|
|
||||||
|
assert.False(t, overage)
|
||||||
|
assert.Equal(t, []string{"abc-123", "def-456"}, groups)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroupsFromClaimsOverage(t *testing.T) {
|
||||||
|
claims := map[string]any{
|
||||||
|
"_claim_names": map[string]any{
|
||||||
|
"groups": "src1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
groups, overage := GroupsFromClaims(claims, "groups")
|
||||||
|
|
||||||
|
assert.True(t, overage)
|
||||||
|
assert.Nil(t, groups)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapGroupsToRole(t *testing.T) {
|
||||||
|
mapping := map[string]acl.Role{
|
||||||
|
"abc-123": acl.RoleAdmin,
|
||||||
|
"def-456": acl.RoleGuest,
|
||||||
|
}
|
||||||
|
|
||||||
|
role, ok := MapGroupsToRole([]string{"zzz", "DEF-456"}, mapping)
|
||||||
|
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, acl.RoleGuest, role)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasAnyGroup(t *testing.T) {
|
||||||
|
required := []string{"abc-123", "def-456"}
|
||||||
|
|
||||||
|
assert.True(t, HasAnyGroup([]string{"ABC-123"}, required))
|
||||||
|
assert.False(t, HasAnyGroup([]string{"zzz"}, required))
|
||||||
|
assert.True(t, HasAnyGroup([]string{"zzz"}, nil))
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
@@ -133,6 +134,62 @@ func (c *Config) OIDCUsername() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OIDCGroupClaim returns the claim name that should contain security group identifiers.
|
||||||
|
func (c *Config) OIDCGroupClaim() string {
|
||||||
|
if claim := strings.TrimSpace(c.options.OIDCGroupClaim); claim != "" {
|
||||||
|
return claim
|
||||||
|
}
|
||||||
|
|
||||||
|
return "groups"
|
||||||
|
}
|
||||||
|
|
||||||
|
// OIDCGroup returns the normalized list of required groups; empty means no group check.
|
||||||
|
func (c *Config) OIDCGroup() []string {
|
||||||
|
if len(c.options.OIDCGroup) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]string, 0, len(c.options.OIDCGroup))
|
||||||
|
|
||||||
|
for _, g := range c.options.OIDCGroup {
|
||||||
|
if n := normalizeGroupID(g); n != "" {
|
||||||
|
result = append(result, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// OIDCGroupRoles maps normalized group identifiers to roles.
|
||||||
|
func (c *Config) OIDCGroupRoles() map[string]acl.Role {
|
||||||
|
result := make(map[string]acl.Role, len(c.options.OIDCGroupRole))
|
||||||
|
|
||||||
|
for _, entry := range c.options.OIDCGroupRole {
|
||||||
|
entry = strings.TrimSpace(entry)
|
||||||
|
|
||||||
|
if entry == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sep := strings.IndexAny(entry, "=:")
|
||||||
|
|
||||||
|
if sep < 1 || sep >= len(entry)-1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
group := normalizeGroupID(entry[:sep])
|
||||||
|
role := acl.ParseRole(entry[sep+1:])
|
||||||
|
|
||||||
|
if group == "" || role == acl.RoleNone {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result[group] = role
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// OIDCDomain returns the email domain name for restricted single sign-on via OIDC.
|
// OIDCDomain returns the email domain name for restricted single sign-on via OIDC.
|
||||||
func (c *Config) OIDCDomain() string {
|
func (c *Config) OIDCDomain() string {
|
||||||
return clean.Domain(c.options.OIDCDomain)
|
return clean.Domain(c.options.OIDCDomain)
|
||||||
@@ -193,6 +250,25 @@ func (c *Config) OIDCReport() (rows [][]string, cols []string) {
|
|||||||
rows = append(rows, []string{"oidc-domain", domain})
|
rows = append(rows, []string{"oidc-domain", domain})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if claim := c.OIDCGroupClaim(); claim != "" {
|
||||||
|
rows = append(rows, []string{"oidc-group-claim", claim})
|
||||||
|
}
|
||||||
|
|
||||||
|
if groups := c.OIDCGroup(); len(groups) > 0 {
|
||||||
|
rows = append(rows, []string{"oidc-group", strings.Join(groups, ",")})
|
||||||
|
}
|
||||||
|
|
||||||
|
if roles := c.OIDCGroupRoles(); len(roles) > 0 {
|
||||||
|
pairs := make([]string, 0, len(roles))
|
||||||
|
|
||||||
|
for g, r := range roles {
|
||||||
|
pairs = append(pairs, fmt.Sprintf("%s=%s", g, r))
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(pairs)
|
||||||
|
rows = append(rows, []string{"oidc-group-role", strings.Join(pairs, ",")})
|
||||||
|
}
|
||||||
|
|
||||||
rows = append(rows, [][]string{
|
rows = append(rows, [][]string{
|
||||||
{"oidc-role", c.OIDCRole().String()},
|
{"oidc-role", c.OIDCRole().String()},
|
||||||
{"oidc-webdav", fmt.Sprintf("%t", c.OIDCWebDAV())},
|
{"oidc-webdav", fmt.Sprintf("%t", c.OIDCWebDAV())},
|
||||||
@@ -201,3 +277,8 @@ func (c *Config) OIDCReport() (rows [][]string, cols []string) {
|
|||||||
|
|
||||||
return rows, cols
|
return rows, cols
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// normalizeGroupID lowercases and sanitizes a group identifier (GUID or name) for comparisons.
|
||||||
|
func normalizeGroupID(id string) string {
|
||||||
|
return strings.ToLower(clean.Auth(id))
|
||||||
|
}
|
||||||
|
|||||||
@@ -132,6 +132,43 @@ func TestConfig_OIDCUsername(t *testing.T) {
|
|||||||
assert.Equal(t, authn.OidcClaimPreferredUsername, c.OIDCUsername())
|
assert.Equal(t, authn.OidcClaimPreferredUsername, c.OIDCUsername())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfig_OIDCGroupClaim(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "groups", c.OIDCGroupClaim())
|
||||||
|
|
||||||
|
c.options.OIDCGroupClaim = " roles "
|
||||||
|
|
||||||
|
assert.Equal(t, "roles", c.OIDCGroupClaim())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_OIDCGroup(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Nil(t, c.OIDCGroup())
|
||||||
|
|
||||||
|
c.options.OIDCGroup = []string{"ABC-123", " DEF-456 ", ""}
|
||||||
|
|
||||||
|
assert.Equal(t, []string{"abc-123", "def-456"}, c.OIDCGroup())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_OIDCGroupRoles(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
c.options.OIDCGroupRole = []string{
|
||||||
|
"ABC-123=admin",
|
||||||
|
"def-456:guest",
|
||||||
|
"invalid",
|
||||||
|
"=none",
|
||||||
|
}
|
||||||
|
|
||||||
|
roles := c.OIDCGroupRoles()
|
||||||
|
|
||||||
|
assert.Equal(t, acl.RoleAdmin, roles["abc-123"])
|
||||||
|
assert.Equal(t, acl.RoleGuest, roles["def-456"])
|
||||||
|
assert.Len(t, roles, 2)
|
||||||
|
}
|
||||||
|
|
||||||
func TestConfig_OIDCDomain(t *testing.T) {
|
func TestConfig_OIDCDomain(t *testing.T) {
|
||||||
c := NewConfig(CliTestContext())
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
"github.com/photoprism/photoprism/internal/ai/face"
|
"github.com/photoprism/photoprism/internal/ai/face"
|
||||||
|
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||||
"github.com/photoprism/photoprism/internal/config/ttl"
|
"github.com/photoprism/photoprism/internal/config/ttl"
|
||||||
"github.com/photoprism/photoprism/internal/entity"
|
"github.com/photoprism/photoprism/internal/entity"
|
||||||
"github.com/photoprism/photoprism/internal/ffmpeg/encode"
|
"github.com/photoprism/photoprism/internal/ffmpeg/encode"
|
||||||
@@ -117,6 +118,25 @@ var Flags = CliFlags{
|
|||||||
Value: authn.OidcClaimPreferredUsername,
|
Value: authn.OidcClaimPreferredUsername,
|
||||||
EnvVars: EnvVars("OIDC_USERNAME"),
|
EnvVars: EnvVars("OIDC_USERNAME"),
|
||||||
}}, {
|
}}, {
|
||||||
|
Flag: &cli.StringFlag{
|
||||||
|
Name: "oidc-group-claim",
|
||||||
|
Usage: "group claim `NAME` to read from OIDC tokens (default groups)",
|
||||||
|
Value: "",
|
||||||
|
EnvVars: EnvVars("OIDC_GROUP_CLAIM"),
|
||||||
|
Hidden: true,
|
||||||
|
}}, {
|
||||||
|
Flag: &cli.StringSliceFlag{
|
||||||
|
Name: "oidc-group",
|
||||||
|
Usage: "require membership in at least one group `ID` (repeat flag to add multiple)",
|
||||||
|
EnvVars: EnvVars("OIDC_GROUP"),
|
||||||
|
Hidden: true,
|
||||||
|
}}, {
|
||||||
|
Flag: &cli.StringSliceFlag{
|
||||||
|
Name: "oidc-group-role",
|
||||||
|
Usage: "map `GROUP=ROLE`; repeat to add more (roles: " + acl.UserRoles.CliUsageString() + ")",
|
||||||
|
EnvVars: EnvVars("OIDC_GROUP_ROLE"),
|
||||||
|
Hidden: true,
|
||||||
|
}}, {
|
||||||
Flag: &cli.BoolFlag{
|
Flag: &cli.BoolFlag{
|
||||||
Name: "oidc-webdav",
|
Name: "oidc-webdav",
|
||||||
Usage: "allows new OpenID Connect users to use WebDAV when they have a role that allows it",
|
Usage: "allows new OpenID Connect users to use WebDAV when they have a role that allows it",
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ type Options struct {
|
|||||||
OIDCRedirect bool `yaml:"OIDCRedirect" json:"OIDCRedirect" flag:"oidc-redirect"`
|
OIDCRedirect bool `yaml:"OIDCRedirect" json:"OIDCRedirect" flag:"oidc-redirect"`
|
||||||
OIDCRegister bool `yaml:"OIDCRegister" json:"OIDCRegister" flag:"oidc-register"`
|
OIDCRegister bool `yaml:"OIDCRegister" json:"OIDCRegister" flag:"oidc-register"`
|
||||||
OIDCUsername string `yaml:"OIDCUsername" json:"-" flag:"oidc-username"`
|
OIDCUsername string `yaml:"OIDCUsername" json:"-" flag:"oidc-username"`
|
||||||
|
OIDCGroupClaim string `yaml:"OIDCGroupClaim" json:"-" flag:"oidc-group-claim" tags:"pro"`
|
||||||
|
OIDCGroup []string `yaml:"OIDCGroup" json:"-" flag:"oidc-group" tags:"pro"`
|
||||||
|
OIDCGroupRole []string `yaml:"OIDCGroupRole" json:"-" flag:"oidc-group-role" tags:"pro"`
|
||||||
OIDCDomain string `yaml:"-" json:"-" flag:"oidc-domain" tags:"pro"`
|
OIDCDomain string `yaml:"-" json:"-" flag:"oidc-domain" tags:"pro"`
|
||||||
OIDCRole string `yaml:"-" json:"-" flag:"oidc-role" tags:"pro"`
|
OIDCRole string `yaml:"-" json:"-" flag:"oidc-role" tags:"pro"`
|
||||||
OIDCWebDAV bool `yaml:"OIDCWebDAV" json:"-" flag:"oidc-webdav"`
|
OIDCWebDAV bool `yaml:"OIDCWebDAV" json:"-" flag:"oidc-webdav"`
|
||||||
|
|||||||
Reference in New Issue
Block a user