From ae82f6cd6f24e28502a100cc3099ef1b8af421e6 Mon Sep 17 00:00:00 2001 From: Jonas Gratz Date: Fri, 25 Jun 2021 17:37:19 +0200 Subject: [PATCH 1/2] Add and update various API calls updated function signatures, backwards incompatible: - GetRealmRoles(ctx context.Context, accessToken, realm string, params GetRolesParams) ([]*Role, error) - GetClientRoles(ctx context.Context, accessToken, realm, clientID string, params GetRolesParams) ([]*Role, error) - CreateIdentityProviderMapper(ctx context.Context, token, realm, alias string, mapper IdentityProviderMapper) (string, error) added new functions: - GetRealmRoleByID(ctx context.Context, token, realm, roleID string) (*Role, error) - UpdateRealmRoleByID(ctx context.Context, token, realm, roleID string, role Role) error - GetCompositeRealmRoles(ctx context.Context, token, realm, roleName string) ([]*Role, error) - GetIdentityProviderMapper(ctx context.Context, token string, realm string, alias string, mapperID string) (*IdentityProviderMapper, error) --- README.md | 9 ++++-- client.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++---- client_test.go | 14 ++++++--- gocloak.go | 10 +++++-- models.go | 7 +++-- 5 files changed, 101 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 65c9c0f8..63c5c366 100644 --- a/README.md +++ b/README.md @@ -258,7 +258,7 @@ type GoCloak interface { GetGroupMembers(ctx context.Context, accessToken, realm, groupID string, params GetGroupsParams) ([]*User, error) GetRoleMappingByGroupID(ctx context.Context, accessToken, realm, groupID string) (*MappingsRepresentation, error) GetRoleMappingByUserID(ctx context.Context, accessToken, realm, userID string) (*MappingsRepresentation, error) - GetClientRoles(ctx context.Context, accessToken, realm, idOfClient string) ([]*Role, error) + GetClientRoles(ctx context.Context, accessToken, realm, idOfClient string, params GetRoleParams) ([]*Role, error) GetClientRole(ctx context.Context, token, realm, idOfClient, roleName string) (*Role, error) GetClientRoleByID(ctx context.Context, accessToken, realm, roleID string) (*Role, error) GetClients(ctx context.Context, accessToken, realm string, params GetClientsParams) ([]*Client, error) @@ -274,10 +274,12 @@ type GoCloak interface { CreateRealmRole(ctx context.Context, token, realm string, role Role) (string, error) GetRealmRole(ctx context.Context, token, realm, roleName string) (*Role, error) - GetRealmRoles(ctx context.Context, accessToken, realm string) ([]*Role, error) + GetRealmRoles(ctx context.Context, accessToken, realm string, params GetRoleParams) ([]*Role, error) + GetRealmRoleByID(ctx context.Context, token, realm, roleID string) (*Role, error) GetRealmRolesByUserID(ctx context.Context, accessToken, realm, userID string) ([]*Role, error) GetRealmRolesByGroupID(ctx context.Context, accessToken, realm, groupID string) ([]*Role, error) UpdateRealmRole(ctx context.Context, token, realm, roleName string, role Role) error + UpdateRealmRoleByID(ctx context.Context, token, realm, roleID string, role Role) error DeleteRealmRole(ctx context.Context, token, realm, roleName string) error AddRealmRoleToUser(ctx context.Context, token, realm, userID string, roles []Role) error DeleteRealmRoleFromUser(ctx context.Context, token, realm, userID string, roles []Role) error @@ -285,6 +287,7 @@ type GoCloak interface { DeleteRealmRoleFromGroup(ctx context.Context, token, realm, groupID string, roles []Role) error AddRealmRoleComposite(ctx context.Context, token, realm, roleName string, roles []Role) error DeleteRealmRoleComposite(ctx context.Context, token, realm, roleName string, roles []Role) error + GetCompositeRealmRoles(ctx context.Context, token, realm, roleName string) ([]*Role, error) GetCompositeRealmRolesByRoleID(ctx context.Context, token, realm, roleID string) ([]*Role, error) GetCompositeRealmRolesByUserID(ctx context.Context, token, realm, userID string) ([]*Role, error) GetCompositeRealmRolesByGroupID(ctx context.Context, token, realm, groupID string) ([]*Role, error) @@ -385,6 +388,8 @@ type GoCloak interface { UpdateIdentityProvider(ctx context.Context, token, realm, alias string, providerRep IdentityProviderRepresentation) error DeleteIdentityProvider(ctx context.Context, token, realm, alias string) error + CreateIdentityProviderMapper(ctx context.Context, token, realm, alias string, mapper IdentityProviderMapper) (string, error) + GetIdentityProviderMapper(ctx context.Context, token string, realm string, alias string, mapperID string) (*IdentityProviderMapper, error) CreateUserFederatedIdentity(ctx context.Context, token, realm, userID, providerID string, federatedIdentityRep FederatedIdentityRepresentation) error GetUserFederatedIdentities(ctx context.Context, token, realm, userID string) ([]*FederatedIdentityRepresentation, error) DeleteUserFederatedIdentity(ctx context.Context, token, realm, userID, providerID string) error diff --git a/client.go b/client.go index 3dba3e52..08f92d06 100644 --- a/client.go +++ b/client.go @@ -1411,12 +1411,18 @@ func (client *gocloak) GetGroupMembers(ctx context.Context, token, realm, groupI } // GetClientRoles get all roles for the given client in realm -func (client *gocloak) GetClientRoles(ctx context.Context, token, realm, idOfClient string) ([]*Role, error) { +func (client *gocloak) GetClientRoles(ctx context.Context, token, realm, idOfClient string, params GetRoleParams) ([]*Role, error) { const errMessage = "could not get client roles" var result []*Role + queryParams, err := GetQueryParams(params) + if err != nil { + return nil, errors.Wrap(err, errMessage) + } + resp, err := client.getRequestWithBearerAuth(ctx, token). SetResult(&result). + SetQueryParams(queryParams). Get(client.getAdminRealmURL(realm, "clients", idOfClient, "roles")) if err := checkForError(resp, err, errMessage); err != nil { @@ -1637,6 +1643,22 @@ func (client *gocloak) GetRealmRole(ctx context.Context, token, realm, roleName return &result, nil } +// GetRealmRoleByID returns a role from a realm by role's ID +func (client *gocloak) GetRealmRoleByID(ctx context.Context, token, realm, roleID string) (*Role, error) { + const errMessage = "could not get realm role" + + var result Role + resp, err := client.getRequestWithBearerAuth(ctx, token). + SetResult(&result). + Get(client.getAdminRealmURL(realm, "roles-by-id", roleID)) + + if err := checkForError(resp, err, errMessage); err != nil { + return nil, err + } + + return &result, nil +} + // GetRealmRoles get all roles of the given realm. func (client *gocloak) GetRealmRoles(ctx context.Context, token, realm string, params GetRoleParams) ([]*Role, error) { const errMessage = "could not get realm roles" @@ -1702,6 +1724,17 @@ func (client *gocloak) UpdateRealmRole(ctx context.Context, token, realm, roleNa return checkForError(resp, err, errMessage) } +// UpdateRealmRoleByID updates a role in a realm by role's ID +func (client *gocloak) UpdateRealmRoleByID(ctx context.Context, token, realm, roleID string, role Role) error { + const errMessage = "could not update realm role" + + resp, err := client.getRequestWithBearerAuth(ctx, token). + SetBody(role). + Put(client.getAdminRealmURL(realm, "roles-by-id", roleID)) + + return checkForError(resp, err, errMessage) +} + // DeleteRealmRole deletes a role in a realm by role's name func (client *gocloak) DeleteRealmRole(ctx context.Context, token, realm, roleName string) error { const errMessage = "could not delete realm role" @@ -1776,6 +1809,22 @@ func (client *gocloak) DeleteRealmRoleComposite(ctx context.Context, token, real return checkForError(resp, err, errMessage) } +// GetCompositeRealmRoles returns all realm composite roles associated with the given realm role +func (client *gocloak) GetCompositeRealmRoles(ctx context.Context, token, realm, roleName string) ([]*Role, error) { + const errMessage = "could not get composite realm roles by role" + + var result []*Role + resp, err := client.getRequestWithBearerAuth(ctx, token). + SetResult(&result). + Get(client.getAdminRealmURL(realm, "roles", roleName, "composites")) + + if err = checkForError(resp, err, errMessage); err != nil { + return nil, err + } + + return result, nil +} + // GetCompositeRealmRolesByRoleID returns all realm composite roles associated with the given client role func (client *gocloak) GetCompositeRealmRolesByRoleID(ctx context.Context, token, realm, roleID string) ([]*Role, error) { const errMessage = "could not get composite client roles by role id" @@ -2416,14 +2465,34 @@ func (client *gocloak) ImportIdentityProviderConfigFromFile(ctx context.Context, } // CreateIdentityProviderMapper creates an instance of an identity provider mapper associated with the given alias -func (client *gocloak) CreateIdentityProviderMapper(ctx context.Context, token, realm, alias string, mapper IdentityProviderMapper) error { +func (client *gocloak) CreateIdentityProviderMapper(ctx context.Context, token, realm, alias string, mapper IdentityProviderMapper) (string, error) { const errMessage = "could not create mapper for identity provider" resp, err := client.getRequestWithBearerAuth(ctx, token). SetBody(mapper). Post(client.getAdminRealmURL(realm, "identity-provider", "instances", alias, "mappers")) - return checkForError(resp, err, errMessage) + if err := checkForError(resp, err, errMessage); err != nil { + return "", err + } + + return getID(resp), nil +} + +// GetIdentityProviderMapper gets the mapper by id for the given identity provider alias in a realm +func (client *gocloak) GetIdentityProviderMapper(ctx context.Context, token string, realm string, alias string, mapperID string) (*IdentityProviderMapper, error) { + const errMessage = "could not get identity provider mapper" + + result := IdentityProviderMapper{} + resp, err := client.getRequestWithBearerAuth(ctx, token). + SetResult(&result). + Get(client.getAdminRealmURL(realm, "identity-provider", "instances", alias, "mappers", mapperID)) + + if err := checkForError(resp, err, errMessage); err != nil { + return nil, err + } + + return &result, nil } // DeleteIdentityProviderMapper deletes an instance of an identity provider mapper associated with the given alias and mapper ID @@ -3368,5 +3437,3 @@ func (client *gocloak) CreateClientScopesScopeMappingsRealmRoles(ctx context.Con return checkForError(resp, err, errMessage) } - - diff --git a/client_test.go b/client_test.go index e8bac443..1f779e16 100644 --- a/client_test.go +++ b/client_test.go @@ -1609,6 +1609,7 @@ func TestGocloak_ClientScopeMappingsClientRoles(t *testing.T) { token.AccessToken, cfg.GoCloak.Realm, gocloakClientID, + gocloak.GetRoleParams{}, ) require.NoError(t, err, "GetClientRoles failed") @@ -1991,7 +1992,8 @@ func TestGocloak_GetClientRoles(t *testing.T) { context.Background(), token.AccessToken, cfg.GoCloak.Realm, - *testClient.ID) + *testClient.ID, + gocloak.GetRoleParams{}) require.NoError(t, err, "GetClientRoles failed") } @@ -3215,7 +3217,8 @@ func TestGoCloak_ClientSecret(t *testing.T) { defer tearDown() require.Equal(t, *testClient.ID, idOfClient) - oldCreds, err := client.GetClientSecret( + // Keycloak does not support setting the secret while creating the client + _, err := client.GetClientSecret( context.Background(), token.AccessToken, cfg.GoCloak.Realm, @@ -3230,7 +3233,8 @@ func TestGoCloak_ClientSecret(t *testing.T) { idOfClient, ) require.NoError(t, err, "RegenerateClientSecret failed") - require.NotEqual(t, *oldCreds.Value, *regeneratedCreds.Value) + require.NotNil(t, regeneratedCreds.Value, "RegenerateClientSecret value is nil") + require.NotEmpty(t, *regeneratedCreds.Value, "RegenerateClientSecret value is empty") err = client.DeleteClient( context.Background(), @@ -3916,7 +3920,7 @@ func TestGocloak_CreateGetDeleteUserFederatedIdentity(t *testing.T) { }, } - err = client.CreateIdentityProviderMapper( + mapperPID, err := client.CreateIdentityProviderMapper( context.Background(), token.AccessToken, cfg.GoCloak.Realm, @@ -3924,6 +3928,7 @@ func TestGocloak_CreateGetDeleteUserFederatedIdentity(t *testing.T) { mapperP, ) require.NoError(t, err) + require.NotEmpty(t, mapperPID) mappers, err := client.GetIdentityProviderMappers( context.Background(), @@ -3934,6 +3939,7 @@ func TestGocloak_CreateGetDeleteUserFederatedIdentity(t *testing.T) { require.NoError(t, err) require.Len(t, mappers, 1) mapperID := mappers[0].ID + require.Equal(t, mapperPID, gocloak.PString(mapperID)) mapperP.ID = mapperID // get single mapper diff --git a/gocloak.go b/gocloak.go index 9c388f1f..8f0d2230 100644 --- a/gocloak.go +++ b/gocloak.go @@ -188,6 +188,8 @@ type GoCloak interface { CreateRealmRole(ctx context.Context, token, realm string, role Role) (string, error) // GetRealmRole returns a role from a realm by role's name GetRealmRole(ctx context.Context, token, realm, roleName string) (*Role, error) + // GetRealmRoleByID returns a role from a realm by role's ID + GetRealmRoleByID(ctx context.Context, token, realm, roleID string) (*Role, error) // GetRealmRoles get all roles of the given realm. It's an alias for the GetRoles function GetRealmRoles(ctx context.Context, accessToken, realm string, params GetRoleParams) ([]*Role, error) // GetRealmRolesByUserID returns all roles assigned to the given user @@ -196,6 +198,8 @@ type GoCloak interface { GetRealmRolesByGroupID(ctx context.Context, accessToken, realm, groupID string) ([]*Role, error) // UpdateRealmRole updates a role in a realm UpdateRealmRole(ctx context.Context, token, realm, roleName string, role Role) error + // UpdateRealmRoleByID updates a role in a realm by role's ID + UpdateRealmRoleByID(ctx context.Context, token, realm, roleID string, role Role) error // DeleteRealmRole deletes a role in a realm by role's name DeleteRealmRole(ctx context.Context, token, realm, roleName string) error // AddRealmRoleToUser adds realm-level role mappings @@ -210,6 +214,8 @@ type GoCloak interface { AddRealmRoleComposite(ctx context.Context, token, realm, roleName string, roles []Role) error // AddRealmRoleComposite adds roles as composite DeleteRealmRoleComposite(ctx context.Context, token, realm, roleName string, roles []Role) error + // GetCompositeRealmRoles returns all realm composite roles associated with the given realm role + GetCompositeRealmRoles(ctx context.Context, token, realm, roleName string) ([]*Role, error) // GetCompositeRealmRolesByRoleID returns all realm composite roles associated with the given client role GetCompositeRealmRolesByRoleID(ctx context.Context, token, realm, roleID string) ([]*Role, error) // GetCompositeRealmRolesByUserID returns all realm roles and composite roles assigned to the given user @@ -236,7 +242,7 @@ type GoCloak interface { // DeleteClientRoleFromGroup removes a client role from from the group DeleteClientRoleFromGroup(ctx context.Context, token, realm, idOfClient, groupID string, roles []Role) error // GetClientRoles gets roles for the given client - GetClientRoles(ctx context.Context, accessToken, realm, idOfClient string) ([]*Role, error) + GetClientRoles(ctx context.Context, accessToken, realm, idOfClient string, params GetRoleParams) ([]*Role, error) // GetClientRoleById gets role for the given client using role id GetClientRoleByID(ctx context.Context, accessToken, realm, roleID string) (*Role, error) // GetRealmRolesByUserID returns all client roles assigned to the given user @@ -334,7 +340,7 @@ type GoCloak interface { // ExportIDPPublicBrokerConfig exports the broker config for a given alias ExportIDPPublicBrokerConfig(ctx context.Context, token, realm, alias string) (*string, error) // CreateIdentityProviderMapper creates an instance of an identity provider mapper associated with the given alias - CreateIdentityProviderMapper(ctx context.Context, token, realm, alias string, mapper IdentityProviderMapper) error + CreateIdentityProviderMapper(ctx context.Context, token, realm, alias string, mapper IdentityProviderMapper) (string, error) // GetIdentityProviderMapperByID gets the mapper of an identity provider GetIdentityProviderMapperByID(ctx context.Context, token, realm, alias, mapperID string) (*IdentityProviderMapper, error) // UpdateIdentityProviderMapper updates mapper of an identity provider diff --git a/models.go b/models.go index 50297777..63e8c66f 100644 --- a/models.go +++ b/models.go @@ -342,9 +342,10 @@ type Role struct { // GetRoleParams represents the optional parameters for getting roles type GetRoleParams struct { - First *int `json:"first,string,omitempty"` - Max *int `json:"max,string,omitempty"` - Search *string `json:"search,omitempty"` + First *int `json:"first,string,omitempty"` + Max *int `json:"max,string,omitempty"` + Search *string `json:"search,omitempty"` + BriefRepresentation *bool `json:"briefRepresentation,string,omitempty"` } // ClientMappingsRepresentation is a client role mappings From 26a3dbd437631872eaf3ab4a5f7c6b5990fbe5f5 Mon Sep 17 00:00:00 2001 From: Jonas Gratz Date: Fri, 25 Jun 2021 17:45:23 +0200 Subject: [PATCH 2/2] Fix TestGocloak_ImportIdentityProviderConfigFromFile --- client_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client_test.go b/client_test.go index 1f779e16..e7903981 100644 --- a/client_test.go +++ b/client_test.go @@ -5600,6 +5600,8 @@ E8go1LcvbfHNyknHu2sptnRq55fHZSHr18vVsQRfDYMG "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "wantAuthnRequestsSigned": "false", "addExtensionsElementWithKeyInfo": "false", + "loginHint": "false", + "enabledFromMetadata": "true", } require.Len(