PR #10014 was not complete as only handled policy claims partially.
Unified · Split
| @@ -213,15 +213,15 @@ func getClaimsFromToken(r *http.Request, token string) (map[string]interface{}, · 212 unmodified lines | ||
| 213 | 213 | |
| 214 | 214 | if globalPolicyOPA == nil { |
| 215 | 215 | // 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 { | |
| 217 | 217 | return claims.Map(), nil |
| 218 | 218 | } |
| 219 | 219 | |
| 220 | 220 | // If OPA is not set, session token should |
| 221 | 221 | // have a policy and its mandatory, reject |
| 222 | 222 | // requests without policy claim. |
| 223 | − _, pokOpenID := claims.Lookup(iamPolicyClaimNameOpenID()) | |
| 224 | − _, pokSA := claims.Lookup(iamPolicyClaimNameSA()) | |
| 223 | + _, pokOpenID := claims.MapClaims[iamPolicyClaimNameOpenID()] | |
| 224 | + _, pokSA := claims.MapClaims[iamPolicyClaimNameSA()] | |
| 225 | 225 | if !pokOpenID && !pokSA { |
| 226 | 226 | return nil, errAuthentication |
| 227 | 227 | } |
| @@ -360,6 +360,7 @@ func checkRequestAuthTypeToAccessKey(ctx context.Context, r *http.Request, actio · 132 unmodified lines | ||
| 360 | 360 | // Request is allowed return the appropriate access key. |
| 361 | 361 | return cred.AccessKey, owner, ErrNone |
| 362 | 362 | } |
| 363 | + | |
| 363 | 364 | return cred.AccessKey, owner, ErrAccessDenied |
| 364 | 365 | } |
| 365 | 366 | |
| @@ -8,31 +8,31 @@ Configure and install keycloak server by following [Keycloak Installation Guide] · 7 unmodified lines | ||
| 8 | 8 | |
| 9 | 9 | ### Configure Keycloak UI |
| 10 | 10 | - 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 | |
| 15 | 15 | |
| 16 | 16 | - 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 | |
| 20 | 20 | |
| 21 | 21 | - 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 | |
| 25 | 25 | |
| 26 | 26 | - Go to Clients |
| 27 | − -> Client on `account` | |
| 28 | − -> Mappers | |
| 29 | − -> Create | |
| 27 | + - Client on `account` | |
| 28 | + - Mappers | |
| 29 | + - Create | |
| 30 | 30 | - `Name` with any text |
| 31 | 31 | - `Mapper Type` is `User Attribute` |
| 32 | 32 | - `User Attribute` is `policy` |
| 33 | 33 | - `Token Claim Name` is `policy` |
| 34 | 34 | - `Claim JSON Type` is `string` |
| 35 | − -> Save | |
| 35 | + - Save | |
| 36 | 36 | |
| 37 | 37 | - Open http://localhost:8080/auth/realms/demo/.well-known/openid-configuration to verify OpenID discovery document, verify it has `authorization_endpoint` and `jwks_uri` |
| 38 | 38 | |
| @@ -31,7 +31,9 @@ const ( · 30 unmodified lines | ||
| 31 | 31 | // JWTJti JWT unique identifier claim substitution. |
| 32 | 32 | JWTJti Key = "jwt:jti" |
| 33 | 33 | |
| 34 | + JWTUpn Key = "jwt:upn" | |
| 34 | 35 | JWTName Key = "jwt:name" |
| 36 | + JWTGroups Key = "jwt:groups" | |
| 35 | 37 | JWTGivenName Key = "jwt:given_name" |
| 36 | 38 | JWTFamilyName Key = "jwt:family_name" |
| 37 | 39 | JWTMiddleName Key = "jwt:middle_name" |
| @@ -57,6 +59,8 @@ var JWTKeys = []Key{ · 19 unmodified lines | ||
| 57 | 59 | JWTAud, |
| 58 | 60 | JWTJti, |
| 59 | 61 | JWTName, |
| 62 | + JWTUpn, | |
| 63 | + JWTGroups, | |
| 60 | 64 | JWTGivenName, |
| 61 | 65 | JWTFamilyName, |
| 62 | 66 | JWTMiddleName, |
| @@ -47,23 +47,36 @@ func GetPoliciesFromClaims(claims map[string]interface{}, policyClaimName string · 46 unmodified lines | ||
| 47 | 47 | if !ok { |
| 48 | 48 | return s, false |
| 49 | 49 | } |
| 50 | − pnames, ok := pname.([]string) | |
| 50 | + pnames, ok := pname.([]interface{}) | |
| 51 | 51 | if !ok { |
| 52 | 52 | pnameStr, ok := pname.(string) |
| 53 | 53 | 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 | |
| 57 | 64 | } |
| 65 | + return s, false | |
| 58 | 66 | } |
| 59 | 67 | 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 | + } | |
| 65 | 79 | } |
| 66 | − s.Add(pname) | |
| 67 | 80 | } |
| 68 | 81 | return s, true |
| 69 | 82 | } |
| @@ -22,10 +22,55 @@ import ( · 21 unmodified lines | ||
| 22 | 22 | "reflect" |
| 23 | 23 | "testing" |
| 24 | 24 | |
| 25 | + "github.com/minio/minio-go/v6/pkg/set" | |
| 25 | 26 | "github.com/minio/minio/pkg/bucket/policy" |
| 26 | 27 | "github.com/minio/minio/pkg/bucket/policy/condition" |
| 27 | 28 | ) |
| 28 | 29 | |
| 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 | + | |
| 29 | 74 | func TestPolicyIsAllowed(t *testing.T) { |
| 30 | 75 | case1Policy := Policy{ |
| 31 | 76 | Version: DefaultVersion, |