History

fix: handle array policies in JWT claim (#10041)

PR #10014 was not complete as only handled policy claims partially.

Harshavardhana committed 6 years ago

Commit 369a8761 parent 778e9c85 files · +90 −27Browse files

Unified · Split

5 files
  1. cmd/auth-handler.go +4 −3
  2. docs/sts/keycloak.md +14 −14
  3. pkg/bucket/policy/condition/jwt.go +4 −0
  4. pkg/iam/policy/policy.go +23 −10
  5. pkg/iam/policy/policy_test.go +45 −0
cmd/auth-handler.go+4−3
@@ -213,15 +213,15 @@ func getClaimsFromToken(r *http.Request, token string) (map[string]interface{}, · 212 unmodified lines
213213
214214 if globalPolicyOPA == nil {
215215 // If OPA is not set and if ldap claim key is set, allow the claim.
216− if _, ok := claims.Lookup(ldapUser); ok {
216+ if _, ok := claims.MapClaims[ldapUser]; ok {
217217 return claims.Map(), nil
218218 }
219219
220220 // If OPA is not set, session token should
221221 // have a policy and its mandatory, reject
222222 // requests without policy claim.
223− _, pokOpenID := claims.Lookup(iamPolicyClaimNameOpenID())
224− _, pokSA := claims.Lookup(iamPolicyClaimNameSA())
223+ _, pokOpenID := claims.MapClaims[iamPolicyClaimNameOpenID()]
224+ _, pokSA := claims.MapClaims[iamPolicyClaimNameSA()]
225225 if !pokOpenID && !pokSA {
226226 return nil, errAuthentication
227227 }
@@ -360,6 +360,7 @@ func checkRequestAuthTypeToAccessKey(ctx context.Context, r *http.Request, actio · 132 unmodified lines
360360 // Request is allowed return the appropriate access key.
361361 return cred.AccessKey, owner, ErrNone
362362 }
363+
363364 return cred.AccessKey, owner, ErrAccessDenied
364365 }
365366
docs/sts/keycloak.md+14−14
@@ -8,31 +8,31 @@ Configure and install keycloak server by following [Keycloak Installation Guide] · 7 unmodified lines
88
99 ### Configure Keycloak UI
1010 - Go to Clients
11− -> Click on account
12− -> Settings
13− -> Enable `Implicit Flow`
14− -> Save
11+ - Click on account
12+ - Settings
13+ - Enable `Implicit Flow`
14+ - Save
1515
1616 - Go to Users
17− -> Click on the user
18− -> Attribute, add a new attribute `Key` is `policy`, `Value` is name of the `policy` on MinIO (ex: `readwrite`)
19− -> Add and Save
17+ - Click on the user
18+ - Attribute, add a new attribute `Key` is `policy`, `Value` is name of the `policy` on MinIO (ex: `readwrite`)
19+ - Add and Save
2020
2121 - Go to Clients
22− -> Click on `account`
23− -> Settings, set `Valid Redirect URIs` to `*`, expand `Advanced Settings` and set `Access Token Lifespan` to `1 Hours`
24− -> Save
22+ - Click on `account`
23+ - Settings, set `Valid Redirect URIs` to `*`, expand `Advanced Settings` and set `Access Token Lifespan` to `1 Hours`
24+ - Save
2525
2626 - Go to Clients
27− -> Client on `account`
28− -> Mappers
29− -> Create
27+ - Client on `account`
28+ - Mappers
29+ - Create
3030 - `Name` with any text
3131 - `Mapper Type` is `User Attribute`
3232 - `User Attribute` is `policy`
3333 - `Token Claim Name` is `policy`
3434 - `Claim JSON Type` is `string`
35− -> Save
35+ - Save
3636
3737 - Open http://localhost:8080/auth/realms/demo/.well-known/openid-configuration to verify OpenID discovery document, verify it has `authorization_endpoint` and `jwks_uri`
3838
pkg/bucket/policy/condition/jwt.go+4−0
@@ -31,7 +31,9 @@ const ( · 30 unmodified lines
3131 // JWTJti JWT unique identifier claim substitution.
3232 JWTJti Key = "jwt:jti"
3333
34+ JWTUpn Key = "jwt:upn"
3435 JWTName Key = "jwt:name"
36+ JWTGroups Key = "jwt:groups"
3537 JWTGivenName Key = "jwt:given_name"
3638 JWTFamilyName Key = "jwt:family_name"
3739 JWTMiddleName Key = "jwt:middle_name"
@@ -57,6 +59,8 @@ var JWTKeys = []Key{ · 19 unmodified lines
5759 JWTAud,
5860 JWTJti,
5961 JWTName,
62+ JWTUpn,
63+ JWTGroups,
6064 JWTGivenName,
6165 JWTFamilyName,
6266 JWTMiddleName,
pkg/iam/policy/policy.go+23−10
@@ -47,23 +47,36 @@ func GetPoliciesFromClaims(claims map[string]interface{}, policyClaimName string · 46 unmodified lines
4747 if !ok {
4848 return s, false
4949 }
50− pnames, ok := pname.([]string)
50+ pnames, ok := pname.([]interface{})
5151 if !ok {
5252 pnameStr, ok := pname.(string)
5353 if ok {
54− pnames = strings.Split(pnameStr, ",")
55− } else {
56− return s, false
54+ for _, pname := range strings.Split(pnameStr, ",") {
55+ pname = strings.TrimSpace(pname)
56+ if pname == "" {
57+ // ignore any empty strings, considerate
58+ // towards some user errors.
59+ continue
60+ }
61+ s.Add(pname)
62+ }
63+ return s, true
5764 }
65+ return s, false
5866 }
5967 for _, pname := range pnames {
60− pname = strings.TrimSpace(pname)
61− if pname == "" {
62− // ignore any empty strings, considerate
63− // towards some user errors.
64− continue
68+ pnameStr, ok := pname.(string)
69+ if ok {
70+ for _, pnameStr := range strings.Split(pnameStr, ",") {
71+ pnameStr = strings.TrimSpace(pnameStr)
72+ if pnameStr == "" {
73+ // ignore any empty strings, considerate
74+ // towards some user errors.
75+ continue
76+ }
77+ s.Add(pnameStr)
78+ }
6579 }
66− s.Add(pname)
6780 }
6881 return s, true
6982 }
pkg/iam/policy/policy_test.go+45−0
@@ -22,10 +22,55 @@ import ( · 21 unmodified lines
2222 "reflect"
2323 "testing"
2424
25+ "github.com/minio/minio-go/v6/pkg/set"
2526 "github.com/minio/minio/pkg/bucket/policy"
2627 "github.com/minio/minio/pkg/bucket/policy/condition"
2728 )
2829
30+func TestGetPoliciesFromClaims(t *testing.T) {
31+ attributesArray := `{
32+ "exp": 1594690452,
33+ "iat": 1594689552,
34+ "auth_time": 1594689552,
35+ "jti": "18ed05c9-2c69-45d5-a33f-8c94aca99ad5",
36+ "iss": "http://localhost:8080/auth/realms/minio",
37+ "aud": "account",
38+ "sub": "7e5e2f30-1c97-4616-8623-2eae14dee9b1",
39+ "typ": "ID",
40+ "azp": "account",
41+ "nonce": "66ZoLzwJbjdkiedI",
42+ "session_state": "3df7b526-5310-4038-9f35-50ecd295a31d",
43+ "acr": "1",
44+ "upn": "harsha",
45+ "address": {},
46+ "email_verified": false,
47+ "groups": [
48+ "offline_access"
49+ ],
50+ "preferred_username": "harsha",
51+ "policy": [
52+ "readwrite",
53+ "readwrite,readonly",
54+ " readonly",
55+ ""
56+ ]}`
57+ var m = make(map[string]interface{})
58+ if err := json.Unmarshal([]byte(attributesArray), &m); err != nil {
59+ t.Fatal(err)
60+ }
61+ var expectedSet = set.CreateStringSet("readwrite", "readonly")
62+ gotSet, ok := GetPoliciesFromClaims(m, "policy")
63+ if !ok {
64+ t.Fatal("no policy claim was found")
65+ }
66+ if gotSet.IsEmpty() {
67+ t.Fatal("no policies were found in policy claim")
68+ }
69+ if !gotSet.Equals(expectedSet) {
70+ t.Fatalf("Expected %v got %v", expectedSet, gotSet)
71+ }
72+}
73+
2974 func TestPolicyIsAllowed(t *testing.T) {
3075 case1Policy := Policy{
3176 Version: DefaultVersion,

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t