Skip to content

Commit

Permalink
Merge pull request #128 from pirm-in/master
Browse files Browse the repository at this point in the history
feat: support for scope list in JWT token for scope validation
  • Loading branch information
kpacha authored Jan 24, 2024
2 parents d8cf84a + 889cb9c commit 9d87c6f
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 16 deletions.
67 changes: 51 additions & 16 deletions jose.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,16 +155,11 @@ func ScopesAllMatcher(scopesKey string, claims map[string]interface{}, requiredS
if !ok {
return false
}
scopeClaim, ok := tmp.(string)
if !ok {
return false
}

presentScopes := strings.Split(scopeClaim, " ")
if len(presentScopes) > 0 {
for _, rScope := range requiredScopes {
matchAll := func(required []string, given []string) bool {
for _, rScope := range required {
matched := false
for _, pScope := range presentScopes {
for _, pScope := range given {
if rScope == pScope {
matched = true
}
Expand All @@ -177,6 +172,23 @@ func ScopesAllMatcher(scopesKey string, claims map[string]interface{}, requiredS
return true
}

scopes, ok := tmp.([]interface{})
if ok {
if len(scopes) > 0 {
return matchAll(requiredScopes, convertToStringSlice(scopes))
}
}

scopeString, ok := tmp.(string)
if !ok {
return false
}

presentScopes := strings.Split(scopeString, " ")
if len(presentScopes) > 0 {
return matchAll(requiredScopes, presentScopes)
}

return false
}

Expand All @@ -200,24 +212,37 @@ func ScopesAnyMatcher(scopesKey string, claims map[string]interface{}, requiredS
if !ok {
return false
}
scopeClaim, ok := tmp.(string)
if !ok {
return false
}

presentScopes := strings.Split(scopeClaim, " ")
if len(presentScopes) > 0 {
for _, rScope := range requiredScopes {
for _, pScope := range presentScopes {
matchAny := func(required []string, given []string) bool {
for _, rScope := range required {
for _, pScope := range given {
if rScope == pScope {
return true // found any of the required scopes --> return
}
}
}

// none of the scopes have been found in provided (claims) scopes
return false
}

scopes, ok := tmp.([]interface{})
if ok {
if len(scopes) > 0 {
return matchAny(requiredScopes, convertToStringSlice(scopes))
}
}

scopeClaim, ok := tmp.(string)
if !ok {
return false
}

presentScopes := strings.Split(scopeClaim, " ")
if len(presentScopes) > 0 {
return matchAny(requiredScopes, presentScopes)
}

return false
}

Expand Down Expand Up @@ -320,3 +345,13 @@ var supportedAlgorithms = map[string]jose.SignatureAlgorithm{
"PS384": jose.PS384,
"PS512": jose.PS512,
}

func convertToStringSlice(input []interface{}) []string {
result := make([]string, len(input))

for i, v := range input {
result[i] = v.(string)
}

return result
}
130 changes: 130 additions & 0 deletions jose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,71 @@ func TestScopesAllMatcher(t *testing.T) {
requiredScopes []string
expected bool
}{
{
name: "all_simple_success_for_scope_slice",
scopesKey: "scope",
claims: map[string]interface{}{"scope": []interface{}{"a", "b"}},
requiredScopes: []string{"a", "b"},
expected: true,
},
{
name: "all_simple_fail_for_scope_slice",
scopesKey: "scope",
claims: map[string]interface{}{"scope": []interface{}{"a", "b"}},
requiredScopes: []string{"c"},
expected: false,
},
{
name: "all_missingone_fail_for_scope_slice",
scopesKey: "scope",
claims: map[string]interface{}{"scope": []interface{}{"a", "b"}},
requiredScopes: []string{"a", "b", "c"},
expected: false,
},
{
name: "all_one_simple_success_for_scope_slice",
scopesKey: "scope",
claims: map[string]interface{}{"scope": []interface{}{"a", "b"}},
requiredScopes: []string{"b"},
expected: true,
},
{
name: "all_no_req_scopes_success_for_scope_slice",
scopesKey: "scope",
claims: map[string]interface{}{"scope": []interface{}{"a", "b"}},
requiredScopes: []string{},
expected: true,
},
{
name: "all_struct_success_for_scope_slice",
scopesKey: "data.scope",
claims: map[string]interface{}{"data": map[string]interface{}{"scope": []interface{}{"a", "b"}}},
requiredScopes: []string{"a", "b"},
expected: true,
},
{
name: "all_deep_struct_success_for_scope_slice",
scopesKey: "data.data.data.data.data.data.data.scope",
claims: map[string]interface{}{
"data": map[string]interface{}{
"data": map[string]interface{}{
"data": map[string]interface{}{
"data": map[string]interface{}{
"data": map[string]interface{}{
"data": map[string]interface{}{
"data": map[string]interface{}{
"scope": []interface{}{"a", "b"},
},
},
},
},
},
},
},
},
requiredScopes: []string{"a", "b"},
expected: true,
},
{
name: "all_simple_success",
scopesKey: "scope",
Expand Down Expand Up @@ -232,6 +297,71 @@ func TestScopesAnyMatcher(t *testing.T) {
requiredScopes []string
expected bool
}{
{
name: "any_simple_success_for_scope_slice",
scopesKey: "scope",
claims: map[string]interface{}{"scope": []interface{}{"a", "b"}},
requiredScopes: []string{"a", "b"},
expected: true,
},
{
name: "any_simple_fail_for_scope_slice",
scopesKey: "scope",
claims: map[string]interface{}{"scope": []interface{}{"a", "b"}},
requiredScopes: []string{"c"},
expected: false,
},
{
name: "any_missingone_success_for_scope_slice",
scopesKey: "scope",
claims: map[string]interface{}{"scope": []interface{}{"a"}},
requiredScopes: []string{"a", "b"},
expected: true,
},
{
name: "any_one_simple_success_for_scope_slice",
scopesKey: "scope",
claims: map[string]interface{}{"scope": []interface{}{"a", "b"}},
requiredScopes: []string{"b"},
expected: true,
},
{
name: "any_no_req_scopes_success_for_scope_slice",
scopesKey: "scope",
claims: map[string]interface{}{"scope": []interface{}{"a", "b"}},
requiredScopes: []string{},
expected: true,
},
{
name: "any_struct_success_for_scope_slice",
scopesKey: "data.scope",
claims: map[string]interface{}{"data": map[string]interface{}{"scope": []interface{}{"a"}}},
requiredScopes: []string{"a", "b"},
expected: true,
},
{
name: "any_deep_struct_success_for_scope_slice",
scopesKey: "data.data.data.data.data.data.data.scope",
claims: map[string]interface{}{
"data": map[string]interface{}{
"data": map[string]interface{}{
"data": map[string]interface{}{
"data": map[string]interface{}{
"data": map[string]interface{}{
"data": map[string]interface{}{
"data": map[string]interface{}{
"scope": []interface{}{"a"},
},
},
},
},
},
},
},
},
requiredScopes: []string{"a", "b"},
expected: true,
},
{
name: "any_simple_success",
scopesKey: "scope",
Expand Down

0 comments on commit 9d87c6f

Please sign in to comment.