OIDC: allow to get the role field from a sub-struct

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino
2022-08-10 21:42:58 +02:00
parent 78f75cdcb9
commit a61211d32c
5 changed files with 97 additions and 13 deletions

View File

@@ -225,12 +225,7 @@ func (t *oidcToken) parseClaims(claims map[string]any, usernameField, roleField
if forcedRole != "" {
t.Role = forcedRole
} else {
if roleField != "" {
role, ok := claims[roleField]
if ok {
t.Role = role
}
}
t.getRoleFromField(claims, roleField)
}
t.CustomFields = nil
if len(customFields) > 0 {
@@ -254,6 +249,41 @@ func (t *oidcToken) parseClaims(claims map[string]any, usernameField, roleField
return nil
}
func (t *oidcToken) getRoleFromField(claims map[string]any, roleField string) {
if roleField != "" {
role, ok := claims[roleField]
if ok {
t.Role = role
return
}
if !strings.Contains(roleField, ".") {
return
}
getStructValue := func(outer any, field string) (any, bool) {
switch val := outer.(type) {
case map[string]any:
res, ok := val[field]
return res, ok
}
return nil, false
}
for idx, field := range strings.Split(roleField, ".") {
if idx == 0 {
role, ok = getStructValue(claims, field)
} else {
role, ok = getStructValue(role, field)
}
if !ok {
return
}
}
t.Role = role
}
}
func (t *oidcToken) isAdmin() bool {
switch v := t.Role.(type) {
case string:

View File

@@ -1156,6 +1156,7 @@ func TestOIDCIsAdmin(t *testing.T) {
{input: append(emptySlice, 1), want: false},
{input: 1, want: false},
{input: nil, want: false},
{input: map[string]string{"admin": "admin"}, want: false},
}
for _, tc := range tests {
token := oidcToken{
@@ -1165,6 +1166,59 @@ func TestOIDCIsAdmin(t *testing.T) {
}
}
func TestParseAdminRole(t *testing.T) {
claims := make(map[string]any)
rawClaims := []byte(`{
"sub": "35666371",
"email": "example@example.com",
"preferred_username": "Sally",
"name": "Sally Tyler",
"updated_at": "2018-04-13T22:08:45Z",
"given_name": "Sally",
"family_name": "Tyler",
"params": {
"sftpgo_role": "admin",
"subparams": {
"sftpgo_role": "admin",
"inner": {
"sftpgo_role": ["user","admin"]
}
}
},
"at_hash": "lPLhxI2wjEndc-WfyroDZA",
"rt_hash": "mCmxPtA04N-55AxlEUbq-A",
"aud": "78d1d040-20c9-0136-5146-067351775fae92920",
"exp": 1523664997,
"iat": 1523657797
}`)
err := json.Unmarshal(rawClaims, &claims)
assert.NoError(t, err)
type test struct {
input string
want bool
}
tests := []test{
{input: "sftpgo_role", want: false},
{input: "params.sftpgo_role", want: true},
{input: "params.subparams.sftpgo_role", want: true},
{input: "params.subparams.inner.sftpgo_role", want: true},
{input: "email", want: false},
{input: "missing", want: false},
{input: "params.email", want: false},
{input: "missing.sftpgo_role", want: false},
{input: "params", want: false},
{input: "params.subparams.inner.sftpgo_role.missing", want: false},
}
for _, tc := range tests {
token := oidcToken{}
token.getRoleFromField(claims, tc.input)
assert.Equal(t, tc.want, token.isAdmin(), "%q should return %t", tc.input, tc.want)
}
}
func TestOIDCWithLoginFormsDisabled(t *testing.T) {
oidcMgr, ok := oidcMgr.(*memoryOIDCManager)
require.True(t, ok)