1
0
mirror of https://github.com/oauth2-proxy/oauth2-proxy.git synced 2025-01-04 03:49:06 +02:00

Enable custom structure for group claim with default name group (#839)

* Allow complex structure for groups in group claim.

* Remove unused constant

* Update variable name

* Fix linting

* Use helper method

* Log error if not possible to append group value

* Add missing import

* Use own logger

* Fix imports

* Remove Dockerfile for testing

* Add Changelog entry

* Use formatGroup helper method and update tests

* Return string instead of string array

* Remove groups variable

* Return error in format method.

* Reorder imports

Co-authored-by: Nick Meves <nick.meves@greenhouse.io>
This commit is contained in:
Kevin Kreitner 2020-11-03 19:10:08 +01:00 committed by GitHub
parent cc6b808c17
commit 65016c8da1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 14 deletions

View File

@ -11,6 +11,7 @@
- Sessions from v6.0.0 or later had a graceful conversion to SHA256 that resulted in no reauthentication
- Upgrading from v5.1.1 or earlier will result in a reauthentication
- [#616](https://github.com/oauth2-proxy/oauth2-proxy/pull/616) Ensure you have configured oauth2-proxy to use the `groups` scope. The user may be logged out initially as they may not currently have the `groups` claim however after going back through login process wil be authenticated.
- [#839](https://github.com/oauth2-proxy/oauth2-proxy/pull/839) Enables complex data structures for group claim entries, which are output as Json by default.
## Breaking Changes

View File

@ -2,7 +2,9 @@ package providers
import (
"context"
"encoding/json"
"fmt"
"reflect"
"strings"
"time"
@ -10,6 +12,7 @@ import (
"golang.org/x/oauth2"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
)
@ -285,21 +288,36 @@ func (p *OIDCProvider) extractGroupsFromRawClaims(rawClaims map[string]interface
rawGroups, ok := rawClaims[p.GroupsClaim].([]interface{})
if rawGroups != nil && ok {
for _, rawGroup := range rawGroups {
group, ok := rawGroup.(string)
if ok {
groups = append(groups, group)
formattedGroup, err := formatGroup(rawGroup)
if err != nil {
logger.Errorf("unable to format group of type %s with error %s", reflect.TypeOf(rawGroup), err)
continue
}
groups = append(groups, formattedGroup)
}
}
return groups
}
func formatGroup(rawGroup interface{}) (string, error) {
group, ok := rawGroup.(string)
if !ok {
jsonGroup, err := json.Marshal(rawGroup)
if err != nil {
return "", err
}
group = string(jsonGroup)
}
return group, nil
}
type OIDCClaims struct {
rawClaims map[string]interface{}
UserID string
Subject string `json:"sub"`
Verified *bool `json:"email_verified"`
PreferredUsername string `json:"preferred_username"`
Groups []string
Subject string `json:"sub"`
Verified *bool `json:"email_verified"`
PreferredUsername string `json:"preferred_username"`
Groups []string `json:"-"`
}

View File

@ -29,12 +29,12 @@ const clientID = "https://test.myapp.com"
const secret = "secret"
type idTokenClaims struct {
Name string `json:"name,omitempty"`
Email string `json:"email,omitempty"`
Phone string `json:"phone_number,omitempty"`
Picture string `json:"picture,omitempty"`
Groups []string `json:"groups,omitempty"`
OtherGroups []string `json:"other_groups,omitempty"`
Name string `json:"name,omitempty"`
Email string `json:"email,omitempty"`
Phone string `json:"phone_number,omitempty"`
Picture string `json:"picture,omitempty"`
Groups interface{} `json:"groups,omitempty"`
OtherGroups interface{} `json:"other_groups,omitempty"`
jwt.StandardClaims
}
@ -64,6 +64,29 @@ var defaultIDToken idTokenClaims = idTokenClaims{
},
}
var customGroupClaimIDToken idTokenClaims = idTokenClaims{
"Jane Dobbs",
"janed@me.com",
"+4798765432",
"http://mugbook.com/janed/me.jpg",
[]map[string]interface{}{
{
"groupId": "Admin Group Id",
"roles": []string{"Admin"},
},
},
[]string{"test:c", "test:d"},
jwt.StandardClaims{
Audience: "https://test.myapp.com",
ExpiresAt: time.Now().Add(time.Duration(5) * time.Minute).Unix(),
Id: "id-some-id",
IssuedAt: time.Now().Unix(),
Issuer: "https://issuer.example.com",
NotBefore: 0,
Subject: "123456789",
},
}
var minimalIDToken idTokenClaims = idTokenClaims{
"",
"",
@ -283,7 +306,7 @@ func TestCreateSessionStateFromBearerToken(t *testing.T) {
GroupsClaim string
ExpectedUser string
ExpectedEmail string
ExpectedGroups []string
ExpectedGroups interface{}
}{
"Default IDToken": {
IDToken: defaultIDToken,
@ -306,6 +329,13 @@ func TestCreateSessionStateFromBearerToken(t *testing.T) {
ExpectedEmail: defaultIDToken.Email,
ExpectedGroups: []string{"test:c", "test:d"},
},
"Custom Groups Claim2": {
IDToken: customGroupClaimIDToken,
GroupsClaim: "groups",
ExpectedUser: customGroupClaimIDToken.Subject,
ExpectedEmail: customGroupClaimIDToken.Email,
ExpectedGroups: []string{"{\"groupId\":\"Admin Group Id\",\"roles\":[\"Admin\"]}"},
},
}
for testName, tc := range testCases {
t.Run(testName, func(t *testing.T) {
@ -373,3 +403,31 @@ func TestOIDCProvider_findVerifiedIdToken(t *testing.T) {
assert.Equal(t, nil, err)
assert.Equal(t, true, verifiedIDToken == nil)
}
func Test_formatGroup(t *testing.T) {
testCases := map[string]struct {
RawGroup interface{}
ExpectedFormattedGroupValue string
}{
"String Group": {
RawGroup: "group",
ExpectedFormattedGroupValue: "group",
},
"Map Group": {
RawGroup: map[string]string{"id": "1", "name": "Test"},
ExpectedFormattedGroupValue: "{\"id\":\"1\",\"name\":\"Test\"}",
},
"List Group": {
RawGroup: []string{"First", "Second"},
ExpectedFormattedGroupValue: "[\"First\",\"Second\"]",
},
}
for testName, tc := range testCases {
t.Run(testName, func(t *testing.T) {
formattedGroup, err := formatGroup(tc.RawGroup)
assert.Nil(t, err)
assert.Equal(t, tc.ExpectedFormattedGroupValue, formattedGroup)
})
}
}