diff --git a/README.md b/README.md index b326b7f7..019c76ea 100644 --- a/README.md +++ b/README.md @@ -477,6 +477,10 @@ if !descopeClient.Auth.ValidateTenantPermissions(sessionToken, "my-tenant-ID", [ if !descopeClient.Auth.ValidateTenantRoles(sessionToken, "my-tenant-ID", []string{"Role to validate"}) { // Deny access } + +matchedTenantRoles := descopeClient.Auth.GetTenantRoles(sessionToken, "my-tenant-ID", []string{"role-name1", "role-name2"}) + +matchedTenantPermissions := descopeClient.Auth.GetTenantPermissions(sessionToken, "my-tenant-ID", []string{"permission-name1", "permission-name2"}) ``` When not using tenants use: @@ -491,6 +495,11 @@ if !descopeClient.Auth.ValidatePermissions(sessionToken, []string{"Permission to if !descopeClient.Auth.ValidateRoles(sessionToken, []string{"Role to validate"}) { // Deny access } + +// Or get the matched roles/permissions +matchedRoles := descopeClient.Auth.GetMatchedRoles(sessionToken, []string{"role-name1", "role-name2"}) + +matchedPermissions := descopeClient.Auth.GetMatchedPermissions(sessionToken, []string{"permission-name1", "permission-name2"}) ``` ### Tenant selection diff --git a/descope/internal/auth/auth.go b/descope/internal/auth/auth.go index b5f84cff..226f68fd 100644 --- a/descope/internal/auth/auth.go +++ b/descope/internal/auth/auth.go @@ -345,6 +345,10 @@ func (auth *authenticationService) ValidatePermissions(token *descope.Token, per return auth.ValidateTenantPermissions(token, "", permissions) } +func (auth *authenticationService) GetMatchedPermissions(token *descope.Token, permissions []string) []string { + return auth.GetMatchedTenantPermissions(token, "", permissions) +} + func (auth *authenticationService) ValidateTenantPermissions(token *descope.Token, tenant string, permissions []string) bool { if tenant != "" && !isAssociatedWithTenant(token, tenant) { return false @@ -358,10 +362,28 @@ func (auth *authenticationService) ValidateTenantPermissions(token *descope.Toke return true } +func (auth *authenticationService) GetMatchedTenantPermissions(token *descope.Token, tenant string, permissions []string) []string { + if tenant != "" && !isAssociatedWithTenant(token, tenant) { + return []string{} + } + granted := getAuthorizationClaimItems(token, tenant, claimPermissions) + matched := []string{} + for i := range permissions { + if slices.Contains(granted, permissions[i]) { + matched = append(matched, permissions[i]) + } + } + return matched +} + func (auth *authenticationService) ValidateRoles(token *descope.Token, roles []string) bool { return auth.ValidateTenantRoles(token, "", roles) } +func (auth *authenticationService) GetMatchedRoles(token *descope.Token, roles []string) []string { + return auth.GetMatchedTenantRoles(token, "", roles) +} + func (auth *authenticationService) ValidateTenantRoles(token *descope.Token, tenant string, roles []string) bool { if tenant != "" && !isAssociatedWithTenant(token, tenant) { return false @@ -375,6 +397,20 @@ func (auth *authenticationService) ValidateTenantRoles(token *descope.Token, ten return true } +func (auth *authenticationService) GetMatchedTenantRoles(token *descope.Token, tenant string, roles []string) []string { + if tenant != "" && !isAssociatedWithTenant(token, tenant) { + return []string{} + } + membership := getAuthorizationClaimItems(token, tenant, claimRoles) + matched := []string{} + for i := range roles { + if slices.Contains(membership, roles[i]) { + matched = append(matched, roles[i]) + } + } + return matched +} + // Select Tenant func (auth *authenticationService) SelectTenantWithRequest(tenantID string, request *http.Request, w http.ResponseWriter) (*descope.AuthenticationInfo, error) { diff --git a/descope/internal/auth/auth_test.go b/descope/internal/auth/auth_test.go index 3db53969..a9d07273 100644 --- a/descope/internal/auth/auth_test.go +++ b/descope/internal/auth/auth_test.go @@ -924,7 +924,24 @@ func TestValidatePermissions(t *testing.T) { "t1", []string{"foo"}, )) +} + +func TestGetMatchedPermissions(t *testing.T) { + a, err := newTestAuth(nil, DoOkWithBody(nil, "")) + require.NoError(t, err) + require.Equal(t, []string{}, a.GetMatchedPermissions(nil, []string{})) + require.Equal(t, []string{}, a.GetMatchedPermissions(nil, []string{"abc"})) + + require.Equal(t, []string{}, a.GetMatchedPermissions(mockAuthorizationToken, []string{})) + require.Equal(t, []string{"foo"}, a.GetMatchedPermissions(mockAuthorizationToken, []string{"foo"})) + require.Equal(t, []string{"foo", "bar"}, a.GetMatchedPermissions(mockAuthorizationToken, []string{"foo", "bar"})) + require.Equal(t, []string{"foo", "bar"}, a.GetMatchedPermissions(mockAuthorizationToken, []string{"foo", "bar", "qux"})) + + require.Equal(t, []string{}, a.GetMatchedTenantPermissions(mockAuthorizationTenantToken, "kuku", []string{})) + require.Equal(t, []string{"foo"}, a.GetMatchedTenantPermissions(mockAuthorizationTenantToken, "kuku", []string{"foo"})) + require.Equal(t, []string{"foo", "bar"}, a.GetMatchedTenantPermissions(mockAuthorizationTenantToken, "kuku", []string{"foo", "bar"})) + require.Equal(t, []string{"foo", "bar"}, a.GetMatchedTenantPermissions(mockAuthorizationTenantToken, "kuku", []string{"foo", "bar", "qux"})) } func TestValidateRoles(t *testing.T) { @@ -958,6 +975,24 @@ func TestValidateRoles(t *testing.T) { require.False(t, a.ValidateTenantRoles(mockAuthorizationTenantToken, "t2", []string{})) } +func TestGetMatchedRoles(t *testing.T) { + a, err := newTestAuth(nil, DoOkWithBody(nil, "")) + require.NoError(t, err) + + require.Equal(t, []string{}, a.GetMatchedRoles(nil, []string{})) + require.Equal(t, []string{}, a.GetMatchedRoles(nil, []string{"foo"})) + + require.Equal(t, []string{}, a.GetMatchedRoles(mockAuthorizationToken, []string{})) + require.Equal(t, []string{"abc"}, a.GetMatchedRoles(mockAuthorizationToken, []string{"abc"})) + require.Equal(t, []string{"abc", "xyz"}, a.GetMatchedRoles(mockAuthorizationToken, []string{"abc", "xyz"})) + require.Equal(t, []string{"abc", "xyz"}, a.GetMatchedRoles(mockAuthorizationToken, []string{"abc", "xyz", "tuv"})) + + require.Equal(t, []string{}, a.GetMatchedTenantRoles(mockAuthorizationTenantToken, "kuku", []string{})) + require.Equal(t, []string{"abc"}, a.GetMatchedTenantRoles(mockAuthorizationTenantToken, "kuku", []string{"abc"})) + require.Equal(t, []string{"abc", "xyz"}, a.GetMatchedTenantRoles(mockAuthorizationTenantToken, "kuku", []string{"abc", "xyz"})) + require.Equal(t, []string{"abc", "xyz"}, a.GetMatchedTenantRoles(mockAuthorizationTenantToken, "kuku", []string{"abc", "xyz", "tuv"})) +} + func TestMe(t *testing.T) { a, err := newTestAuth(nil, func(r *http.Request) (*http.Response, error) { return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBufferString(mockUserResponseBody))}, nil diff --git a/descope/sdk/auth.go b/descope/sdk/auth.go index 3788b57f..31c1a4d5 100644 --- a/descope/sdk/auth.go +++ b/descope/sdk/auth.go @@ -300,19 +300,34 @@ type Authentication interface { // This is a shortcut for ValidateTenantPermissions(token, "", permissions) ValidatePermissions(token *descope.Token, permissions []string) bool + // GetMatchedPermissions - Use toeRetrieves the permissions from top level token's claims + // that match the specified permissions list + GetMatchedPermissions(token *descope.Token, permissions []string) []string + // ValidateTenantPermissions - Use to ensure that a validated session token has been // granted the specified permissions for a specific tenant. ValidateTenantPermissions(token *descope.Token, tenant string, permissions []string) bool + // GetMatchedTenantPermissions - Use to retrieve the permissions token's claims of a specific tenant + // that match the specified permissions list + GetMatchedTenantPermissions(token *descope.Token, tenant string, permissions []string) []string + // ValidateRoles - Use to ensure that a validated session token has been granted the // specified roles. // This is a shortcut for ValidateTenantRoles(token, "", roles) ValidateRoles(token *descope.Token, roles []string) bool + // GetMatchedRoles - Use to retrieve the roles token's claims that match the specified roles list + GetMatchedRoles(token *descope.Token, roles []string) []string + // ValidateTenantRoles - Use to ensure that a validated session token has been granted // the specified roles for a specific tenant. ValidateTenantRoles(token *descope.Token, tenant string, roles []string) bool + // GetMatchedTenantRoles - Use to retrieve the roles token's claims of a specific tenant + // that match the specified roles list + GetMatchedTenantRoles(token *descope.Token, tenant string, roles []string) []string + // SelectTenantWithRequest - Adds a dedicated claim to the JWTs to indicate the tenant on which the user is currently authenticated SelectTenantWithRequest(tenantID string, request *http.Request, w http.ResponseWriter) (*descope.AuthenticationInfo, error) diff --git a/descope/tests/mocks/auth/authenticationmock.go b/descope/tests/mocks/auth/authenticationmock.go index 45374fa9..b7cc5352 100644 --- a/descope/tests/mocks/auth/authenticationmock.go +++ b/descope/tests/mocks/auth/authenticationmock.go @@ -522,15 +522,27 @@ type MockSession struct { ValidatePermissionsAssert func(token *descope.Token, permissions []string) ValidatePermissionsResponse bool + GetMatchedPermissionsAssert func(token *descope.Token, permissions []string) + GetMatchedPermissionsResponse []string + ValidateTenantPermissionsAssert func(token *descope.Token, tenant string, permissions []string) ValidateTenantPermissionsResponse bool + GetMatchedTenantPermissionsAssert func(token *descope.Token, tenant string, permissions []string) + GetMatchedTenantPermissionsResponse []string + ValidateRolesAssert func(token *descope.Token, roles []string) ValidateRolesResponse bool + GetMatchedRolesAssert func(token *descope.Token, roles []string) + GetMatchedRolesResponse []string + ValidateTenantRolesAssert func(token *descope.Token, tenant string, roles []string) ValidateTenantRolesResponse bool + GetMatchedTenantRolesAssert func(token *descope.Token, tenant string, roles []string) + GetMatchedTenantRolesResponse []string + SelectTenantWithRequestAssert func(tenantID string, r *http.Request, w http.ResponseWriter) SelectTenantWithRequestResponse *descope.AuthenticationInfo SelectTenantWithRequestError error @@ -635,6 +647,14 @@ func (m *MockSession) ValidatePermissions(token *descope.Token, permissions []st return m.ValidatePermissionsResponse } +func (m *MockSession) GetMatchedPermissions(token *descope.Token, permissions []string) []string { + if m.GetMatchedPermissionsAssert != nil { + m.GetMatchedPermissionsAssert(token, permissions) + } + + return m.GetMatchedPermissionsResponse +} + func (m *MockSession) ValidateTenantPermissions(token *descope.Token, tenant string, permissions []string) bool { if m.ValidateTenantPermissionsAssert != nil { m.ValidateTenantPermissionsAssert(token, tenant, permissions) @@ -642,6 +662,14 @@ func (m *MockSession) ValidateTenantPermissions(token *descope.Token, tenant str return m.ValidateTenantPermissionsResponse } +func (m *MockSession) GetMatchedTenantPermissions(token *descope.Token, tenant string, permissions []string) []string { + if m.GetMatchedTenantPermissionsAssert != nil { + m.GetMatchedTenantPermissionsAssert(token, tenant, permissions) + } + + return m.GetMatchedTenantPermissionsResponse +} + func (m *MockSession) ValidateRoles(token *descope.Token, roles []string) bool { if m.ValidateRolesAssert != nil { m.ValidateRolesAssert(token, roles) @@ -649,6 +677,14 @@ func (m *MockSession) ValidateRoles(token *descope.Token, roles []string) bool { return m.ValidateRolesResponse } +func (m *MockSession) GetMatchedRoles(token *descope.Token, roles []string) []string { + if m.GetMatchedRolesAssert != nil { + m.GetMatchedRolesAssert(token, roles) + } + + return m.GetMatchedRolesResponse +} + func (m *MockSession) ValidateTenantRoles(token *descope.Token, tenant string, roles []string) bool { if m.ValidateTenantRolesAssert != nil { m.ValidateTenantRolesAssert(token, tenant, roles) @@ -656,6 +692,14 @@ func (m *MockSession) ValidateTenantRoles(token *descope.Token, tenant string, r return m.ValidateTenantRolesResponse } +func (m *MockSession) GetMatchedTenantRoles(token *descope.Token, tenant string, roles []string) []string { + if m.GetMatchedTenantRolesAssert != nil { + m.GetMatchedTenantRolesAssert(token, tenant, roles) + } + + return m.GetMatchedTenantRolesResponse +} + func (m *MockSession) SelectTenantWithRequest(tenantID string, request *http.Request, w http.ResponseWriter) (*descope.AuthenticationInfo, error) { if m.SelectTenantWithRequestAssert != nil { m.SelectTenantWithRequestAssert(tenantID, request, w)