Skip to content

Commit

Permalink
Add missing endpoints for client scopes -> scope mappings -> client r…
Browse files Browse the repository at this point in the history
…oles (#348)

#348 Add missing endpoints for client scopes -> scope mappings -> client roles
  • Loading branch information
literalplus authored May 10, 2022
1 parent c740220 commit 04f3ff4
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 2 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ type GoCloak interface {
CreateClientScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfClient string, roles []Role) error
CreateClientScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string, roles []Role) error
CreateClientScopesScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfCLientScope string, roles []Role) error
CreateClientScopesScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClientScope, idOfClient string, roles []Role) error

UpdateUser(ctx context.Context, accessToken, realm string, user User) error
UpdateGroup(ctx context.Context, accessToken, realm string, updatedGroup Group) error
Expand All @@ -177,6 +178,7 @@ type GoCloak interface {
DeleteClientScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfClient string, roles []Role) error
DeleteClientScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string, roles []Role) error
DeleteClientScopesScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfCLientScope string, roles []Role) error
DeleteClientScopesScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClientScope, ifOfClient string, roles []Role) error

GetClient(ctx context.Context, accessToken, realm, idOfClient string) (*Client, error)
GetClientsDefaultScopes(ctx context.Context, token, realm, idOfClient string) ([]*ClientScope, error)
Expand All @@ -193,8 +195,10 @@ type GoCloak interface {
GetClientScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfClient string) ([]*Role, error)
GetClientScopeMappingsRealmRolesAvailable(ctx context.Context, token, realm, idOfClient string) ([]*Role, error)
GetClientScopesScopeMappingsRealmRolesAvailable(ctx context.Context, token, realm, idOfClientScope string) ([]*Role, error)
GetClientScopesScopeMappingsClientRolesAvailable(ctx context.Context, token, realm, idOfClientScope, idOfClient string) ([]*Role, error)
GetClientScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string) ([]*Role, error)
GetClientScopesScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfClientScope string) ([]*Role, error)
GetClientScopesScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClientScope, idOfClient string) ([]*Role, error)
GetClientScopeMappingsClientRolesAvailable(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string) ([]*Role, error)
GetClientSecret(ctx context.Context, token, realm, idOfClient string) (*CredentialRepresentation, error)
GetClientServiceAccount(ctx context.Context, token, realm, idOfClient string) (*User, error)
Expand Down
62 changes: 62 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3777,3 +3777,65 @@ func (client *gocloak) UpdateRequiredAction(ctx context.Context, token string, r

return err
}

// CreateClientScopesScopeMappingsClientRoles attaches a client role to a client scope (not client's scope)
func (client *gocloak) CreateClientScopesScopeMappingsClientRoles(
ctx context.Context, token, realm, idOfClientScope, idOfClient string, roles []Role,
) error {
const errMessage = "could not create client-level roles to the client-scope"

resp, err := client.getRequestWithBearerAuth(ctx, token).
SetBody(roles).
Post(client.getAdminRealmURL(realm, "client-scopes", idOfClientScope, "scope-mappings", "clients", idOfClient))

return checkForError(resp, err, errMessage)
}

// GetClientScopesScopeMappingsClientRolesAvailable returns available (i.e. not attached via
// CreateClientScopesScopeMappingsClientRoles) client roles for a specific client, for a client scope
// (not client's scope).
func (client *gocloak) GetClientScopesScopeMappingsClientRolesAvailable(ctx context.Context, token, realm, idOfClientScope, idOfClient string) ([]*Role, error) {
const errMessage = "could not get available client-level roles with the client-scope"

var result []*Role

resp, err := client.getRequestWithBearerAuth(ctx, token).
SetResult(&result).
Get(client.getAdminRealmURL(realm, "client-scopes", idOfClientScope, "scope-mappings", "clients", idOfClient, "available"))

if err := checkForError(resp, err, errMessage); err != nil {
return nil, err
}

return result, nil
}

// GetClientScopesScopeMappingsClientRoles returns attached client roles for a specific client, for a client scope
// (not client's scope).
func (client *gocloak) GetClientScopesScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClientScope, idOfClient string) ([]*Role, error) {
const errMessage = "could not get client-level roles with the client-scope"

var result []*Role

resp, err := client.getRequestWithBearerAuth(ctx, token).
SetResult(&result).
Get(client.getAdminRealmURL(realm, "client-scopes", idOfClientScope, "scope-mappings", "clients", idOfClient))

if err := checkForError(resp, err, errMessage); err != nil {
return nil, err
}

return result, nil
}

// DeleteClientScopesScopeMappingsClientRoles removes attachment of client roles from a client scope
// (not client's scope).
func (client *gocloak) DeleteClientScopesScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClientScope, idOfClient string, roles []Role) error {
const errMessage = "could not delete client-level roles from the client-scope"

resp, err := client.getRequestWithBearerAuth(ctx, token).
SetBody(roles).
Delete(client.getAdminRealmURL(realm, "client-scopes", idOfClientScope, "scope-mappings", "clients", idOfClient))

return checkForError(resp, err, errMessage)
}
112 changes: 110 additions & 2 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1665,7 +1665,7 @@ func Test_ClientScopeMappingsClientRoles(t *testing.T) {
token := GetAdminToken(t, client)
testClient := gocloak.Client{
ClientID: GetRandomNameP("ClientID"),
BaseURL: gocloak.StringP("http://example.com"),
BaseURL: gocloak.StringP("https://example.com"),
FullScopeAllowed: gocloak.BoolP(false),
}
// Creating client
Expand Down Expand Up @@ -1811,6 +1811,112 @@ func Test_ClientScopeMappingsRealmRoles(t *testing.T) {
)
}

func CreateClientScopesMappingsClientRoles(
t *testing.T, client gocloak.GoCloak, scopeID, idOfClient string, roles []gocloak.Role,
) func() {
token := GetAdminToken(t, client)
cfg := GetConfig(t)

// Creating client scope mappings
err := client.CreateClientScopesScopeMappingsClientRoles(
context.Background(),
token.AccessToken,
cfg.GoCloak.Realm,
scopeID,
idOfClient,
roles,
)
require.NoError(t, err, "CreateClientScopesScopeMappingsClientRoles failed")

tearDown := func() {
err = client.DeleteClientScopesScopeMappingsClientRoles(
context.Background(),
token.AccessToken,
cfg.GoCloak.Realm,
scopeID,
idOfClient,
roles,
)
require.NoError(t, err, "DeleteClientScopesScopeMappingsClientRoles failed")
}
return tearDown
}

// Test_ClientScopesMappingsClientRoles tests API calls related to client role attachment for a client scope.
func Test_ClientScopesMappingsClientRoles(t *testing.T) {
cfg := GetConfig(t)
client := NewClientWithDebug(t)
token := GetAdminToken(t, client)

// Creating client roles (on shared client)
var roles []gocloak.Role
tearDownRole1, assignRoleName := CreateClientRole(t, client)
defer tearDownRole1()
role, err := client.GetClientRole(
context.Background(),
token.AccessToken,
cfg.GoCloak.Realm,
gocloakClientID,
assignRoleName,
)
require.NoError(t, err, "CreateClientRole failed")
roles = append(roles, *role)
tearDownRole2, noAssignRoleName := CreateClientRole(t, client)
defer tearDownRole2()
role, err = client.GetClientRole(
context.Background(),
token.AccessToken,
cfg.GoCloak.Realm,
gocloakClientID,
noAssignRoleName,
)
require.NoError(t, err, "GetClientRole after CreateClientRole failed")
roles = append(roles, *role)

// Creating scope
tearDownScope, scopeID := CreateClientScope(t, client, nil)
defer tearDownScope()

// Creating client roles for client scope mappings
onlyFirstRole := roles[:1]
tearDownMappings := CreateClientScopesMappingsClientRoles(t, client, scopeID, gocloakClientID, onlyFirstRole)
defer tearDownMappings()

// Check client roles
mappedRoles, err := client.GetClientScopesScopeMappingsClientRoles(
context.Background(),
token.AccessToken,
cfg.GoCloak.Realm,
scopeID,
gocloakClientID,
)
require.NoError(t, err, "GetClientScopesScopeMappingsClientRoles failed")
require.Len(
t, mappedRoles, len(onlyFirstRole),
"GetClientScopeMappingsClientRoles should return exact %s roles", len(onlyFirstRole),
)

clientRolesAvailable, err := client.GetClientScopesScopeMappingsClientRolesAvailable(
context.Background(),
token.AccessToken,
cfg.GoCloak.Realm,
scopeID,
gocloakClientID,
)
require.NoError(t, err, "GetClientScopesScopeMappingsClientRolesAvailable failed")
foundUnassignedRole := false
for _, roleAvailable := range clientRolesAvailable {
require.NotEqual(
t, assignRoleName, roleAvailable.Name,
"assigned role %v should not be available", assignRoleName,
)
if *roleAvailable.Name == noAssignRoleName {
foundUnassignedRole = true
}
}
require.True(t, foundUnassignedRole, "expected role %s to be available", noAssignRoleName)
}

func Test_CreateListGetUpdateDeleteClient(t *testing.T) {
// t.Parallel()
cfg := GetConfig(t)
Expand Down Expand Up @@ -5306,7 +5412,9 @@ func Test_CreateGetUpdateDeleteResourcePolicy(t *testing.T) {
Name: policyNameP,
Description: gocloak.StringP("Role Policy"),
Scopes: &scopes,
Roles: &[]string{roleName},
// "gocloak" is the client name here, apparently it's necessary to scope client roles like that here.
// ref: https://github.com/keycloak/keycloak/blob/main/core/src/main/java/org/keycloak/representations/idm/authorization/UmaPermissionRepresentation.java#L53
Roles: &[]string{fmt.Sprintf("gocloak/%v", roleName)},
},
{
Name: policyNameP,
Expand Down
8 changes: 8 additions & 0 deletions gocloak.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ type GoCloak interface {
CreateClientScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string, roles []Role) error
// CreateClientScopesScopeMappingsRealmRoles creates realm-level roles to the client-scope
CreateClientScopesScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfClientScope string, roles []Role) error
// CreateClientScopesScopeMappingsClientRoles creates client-level roles to the client-scope
CreateClientScopesScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClientScope, idOfClient string, roles []Role) error
// CreateClientRepresentation creates a new client representation
CreateClientRepresentation(ctx context.Context, realm string) (*Client, error)

Expand Down Expand Up @@ -118,6 +120,8 @@ type GoCloak interface {
DeleteClientScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string, roles []Role) error
// DeleteClientScopesScopeMappingsRealmRoles deletes realm-level roles from the client-scope
DeleteClientScopesScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfClientScope string, roles []Role) error
// DeleteClientScopesScopeMappingsClientRoles deletes client-level roles from the client-scope
DeleteClientScopesScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClientScope, ifOfClient string, roles []Role) error
// DeleteClientRepresentation deletes a given client representation
DeleteClientRepresentation(ctx context.Context, accessToken, realm, clientID string) error

Expand Down Expand Up @@ -155,10 +159,14 @@ type GoCloak interface {
GetClientScopeMappingsRealmRolesAvailable(ctx context.Context, token, realm, idOfClient string) ([]*Role, error)
// GetClientScopesScopeMappingsRealmRolesAvailable returns realm-level roles that are available to attach to this client-scope
GetClientScopesScopeMappingsRealmRolesAvailable(ctx context.Context, token, realm, idOfClientScope string) ([]*Role, error)
// GetClientScopesScopeMappingsClientRolesAvailable returns client-level roles that are available to attach to this client-scope
GetClientScopesScopeMappingsClientRolesAvailable(ctx context.Context, token, realm, idOfClientScope, idOfClient string) ([]*Role, error)
// GetClientScopeMappingsClientRoles returns roles associated with a client’s scope
GetClientScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string) ([]*Role, error)
// GetClientScopesScopeMappingsRealmRoles returns roles associated with a client-scope
GetClientScopesScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfClientScope string) ([]*Role, error)
// GetClientScopesScopeMappingsClientRoles returns client roles associated with a client-scope
GetClientScopesScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClientScope, idOfClient string) ([]*Role, error)
// GetClientScopeMappingsClientRolesAvailable returns available roles associated with a client’s scope
GetClientScopeMappingsClientRolesAvailable(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string) ([]*Role, error)
// GetClientSecret returns a client's secret
Expand Down

0 comments on commit 04f3ff4

Please sign in to comment.