diff --git a/client.go b/client.go index 3630f08a..c01017e3 100644 --- a/client.go +++ b/client.go @@ -83,6 +83,12 @@ func checkForError(resp *resty.Response, err error) error { return nil } +func getID(resp *resty.Response) string { + header := resp.Header().Get("Location") + splittedPath := strings.Split(header, urlSeparator) + return splittedPath[len(splittedPath)-1] +} + func findUsedKey(usedKeyID string, keys []*CertResponseKey) *CertResponseKey { for _, key := range keys { if *(key.Kid) == usedKeyID { @@ -352,7 +358,7 @@ func (client *gocloak) RequestPermission(clientID, clientSecret, realm, username } // ExecuteActionsEmail executes an actions email -func (client *gocloak) ExecuteActionsEmail(token string, realm string, params ExecuteActionsEmail) error { +func (client *gocloak) ExecuteActionsEmail(token, realm string, params ExecuteActionsEmail) error { queryParams, err := GetQueryParams(params) if err != nil { return err @@ -365,53 +371,64 @@ func (client *gocloak) ExecuteActionsEmail(token string, realm string, params Ex return checkForError(resp, err) } -// CreateUser creates a new user -func (client *gocloak) CreateGroup(token string, realm string, group Group) error { +func (client *gocloak) CreateGroup(token, realm string, group Group) (string, error) { resp, err := client.getRequestWithBearerAuth(token). SetBody(group). Post(client.getAdminRealmURL(realm, "groups")) - return checkForError(resp, err) + if err := checkForError(resp, err); err != nil { + return "", err + } + return getID(resp), nil } -// CreateComponent creates a new user -func (client *gocloak) CreateComponent(token string, realm string, component Component) error { +func (client *gocloak) CreateComponent(token, realm string, component Component) (string, error) { resp, err := client.getRequestWithBearerAuth(token). SetBody(component). Post(client.getAdminRealmURL(realm, "components")) - return checkForError(resp, err) + if err := checkForError(resp, err); err != nil { + return "", err + } + return getID(resp), nil } -// CreateUser creates a new user -func (client *gocloak) CreateClient(token string, realm string, newClient Client) error { +func (client *gocloak) CreateClient(token, realm string, newClient Client) (string, error) { resp, err := client.getRequestWithBearerAuth(token). SetBody(newClient). Post(client.getAdminRealmURL(realm, "clients")) - return checkForError(resp, err) + if err := checkForError(resp, err); err != nil { + return "", err + } + return getID(resp), nil } // CreateClientRole creates a new role for a client -func (client *gocloak) CreateClientRole(token string, realm string, clientID string, role Role) error { +func (client *gocloak) CreateClientRole(token, realm, clientID string, role Role) (string, error) { resp, err := client.getRequestWithBearerAuth(token). SetBody(role). Post(client.getAdminRealmURL(realm, "clients", clientID, "roles")) - return checkForError(resp, err) + if err := checkForError(resp, err); err != nil { + return "", err + } + return getID(resp), nil } // CreateClientScope creates a new client scope -func (client *gocloak) CreateClientScope(token string, realm string, scope ClientScope) error { +func (client *gocloak) CreateClientScope(token, realm string, scope ClientScope) (string, error) { resp, err := client.getRequestWithBearerAuth(token). SetBody(scope). Post(client.getAdminRealmURL(realm, "client-scopes")) - return checkForError(resp, err) + if err := checkForError(resp, err); err != nil { + return "", err + } + return getID(resp), nil } -// UpdateUser creates a new user -func (client *gocloak) UpdateGroup(token string, realm string, updatedGroup Group) error { +func (client *gocloak) UpdateGroup(token, realm string, updatedGroup Group) error { if NilOrEmpty(updatedGroup.ID) { return errors.New("ID of a group required") } @@ -423,7 +440,7 @@ func (client *gocloak) UpdateGroup(token string, realm string, updatedGroup Grou } // UpdateClient updates the given Client -func (client *gocloak) UpdateClient(token string, realm string, updatedClient Client) error { +func (client *gocloak) UpdateClient(token, realm string, updatedClient Client) error { if NilOrEmpty(updatedClient.ID) { return errors.New("ID of a client required") } @@ -434,8 +451,7 @@ func (client *gocloak) UpdateClient(token string, realm string, updatedClient Cl return checkForError(resp, err) } -// UpdateUser creates a new user -func (client *gocloak) UpdateRole(token string, realm string, clientID string, role Role) error { +func (client *gocloak) UpdateRole(token, realm, clientID string, role Role) error { resp, err := client.getRequestWithBearerAuth(token). SetBody(role). Put(client.getAdminRealmURL(realm, "clients", clientID, "roles", PString(role.Name))) @@ -443,7 +459,6 @@ func (client *gocloak) UpdateRole(token string, realm string, clientID string, r return checkForError(resp, err) } -// UpdateClientScope creates a new client scope func (client *gocloak) UpdateClientScope(token string, realm string, scope ClientScope) error { resp, err := client.getRequestWithBearerAuth(token). SetBody(scope). @@ -452,7 +467,6 @@ func (client *gocloak) UpdateClientScope(token string, realm string, scope Clien return checkForError(resp, err) } -// DeleteUser creates a new user func (client *gocloak) DeleteGroup(token string, realm string, groupID string) error { resp, err := client.getRequestWithBearerAuth(token). Delete(client.getAdminRealmURL(realm, "groups", groupID)) @@ -468,7 +482,6 @@ func (client *gocloak) DeleteClient(token string, realm string, clientID string) return checkForError(resp, err) } -// DeleteComponent creates a new user func (client *gocloak) DeleteComponent(token string, realm string, componentID string) error { resp, err := client.getRequestWithBearerAuth(token). Delete(client.getAdminRealmURL(realm, "components", componentID)) @@ -484,7 +497,6 @@ func (client *gocloak) DeleteClientRole(token, realm, clientID, roleName string) return checkForError(resp, err) } -// DeleteClientScope creates a new client scope func (client *gocloak) DeleteClientScope(token string, realm string, scopeID string) error { resp, err := client.getRequestWithBearerAuth(token). Delete(client.getAdminRealmURL(realm, "client-scopes", scopeID)) @@ -696,12 +708,15 @@ func (client *gocloak) GetClientUserSessions(token, realm, clientID string) ([]* } // CreateClientProtocolMapper creates a protocol mapper in client scope -func (client *gocloak) CreateClientProtocolMapper(token, realm, clientID string, mapper ProtocolMapperRepresentation) error { +func (client *gocloak) CreateClientProtocolMapper(token, realm, clientID string, mapper ProtocolMapperRepresentation) (string, error) { resp, err := client.getRequestWithBearerAuth(token). SetBody(mapper). Post(client.getAdminRealmURL(realm, "clients", clientID, "protocol-mappers", "models")) - return checkForError(resp, err) + if err := checkForError(resp, err); err != nil { + return "", err + } + return getID(resp), nil } // DeleteClientProtocolMapper deletes a protocol mapper in client scope @@ -881,12 +896,15 @@ func (client *gocloak) UserAttributeContains(attributes map[string][]string, att // ----------- // CreateRealmRole creates a role in a realm -func (client *gocloak) CreateRealmRole(token string, realm string, role Role) error { +func (client *gocloak) CreateRealmRole(token string, realm string, role Role) (string, error) { resp, err := client.getRequestWithBearerAuth(token). SetBody(role). Post(client.getAdminRealmURL(realm, "roles")) - return checkForError(resp, err) + if err := checkForError(resp, err); err != nil { + return "", err + } + return getID(resp), nil } // GetRealmRole returns a role from a realm by role's name @@ -1028,12 +1046,15 @@ func (client *gocloak) GetRealms(token string) ([]*RealmRepresentation, error) { } // CreateRealm creates a realm -func (client *gocloak) CreateRealm(token string, realm RealmRepresentation) error { +func (client *gocloak) CreateRealm(token string, realm RealmRepresentation) (string, error) { resp, err := client.getRequestWithBearerAuth(token). SetBody(&realm). Post(client.getAdminRealmURL("")) - return checkForError(resp, err) + if err := checkForError(resp, err); err != nil { + return "", err + } + return getID(resp), nil } // DeleteRealm removes a realm @@ -1063,12 +1084,7 @@ func (client *gocloak) CreateUser(token string, realm string, user User) (string if err := checkForError(resp, err); err != nil { return "", err } - - userPath := resp.Header().Get("Location") - splittedPath := strings.Split(userPath, urlSeparator) - userID := splittedPath[len(splittedPath)-1] - - return userID, nil + return getID(resp), nil } // DeleteUser delete a given user @@ -1169,7 +1185,7 @@ func (client *gocloak) SetPassword(token string, userID string, realm string, pa return checkForError(resp, err) } -// UpdateUser creates a new user +// UpdateUser updates a given user func (client *gocloak) UpdateUser(token string, realm string, user User) error { resp, err := client.getRequestWithBearerAuth(token). SetBody(user). diff --git a/client_test.go b/client_test.go index 93427297..1bf8030e 100644 --- a/client_test.go +++ b/client_test.go @@ -192,29 +192,12 @@ func CreateGroup(t *testing.T, client GoCloak) (func(), string) { "bar": {"baz"}, }, } - err := client.CreateGroup( + groupID, err := client.CreateGroup( token.AccessToken, cfg.GoCloak.Realm, group) - FailIfErr(t, err, "CreateGroup failed") - groups, err := client.GetGroups( - token.AccessToken, - cfg.GoCloak.Realm, - GetGroupsParams{ - Search: group.Name, - }) - FailIfErr(t, err, "GetGroups failed") - var groupID string - for _, fetchedGroup := range groups { - if fetchedGroup.Name == nil { - continue - } - if *(fetchedGroup.Name) == *(group.Name) { - groupID = PString(fetchedGroup.ID) - break - } - } - t.Logf("Created Group with ID: %s. Group: %+v", groupID, group) + assert.NoError(t, err, "CreateGroup failed") + t.Logf("Created Group ID: %s ", groupID) tearDown := func() { err := client.DeleteGroup( @@ -712,13 +695,16 @@ func CreateClientRole(t *testing.T, client GoCloak) (func(), string) { roleName := GetRandomName("Role") t.Logf("Creating Client Role: %s", roleName) - err := client.CreateClientRole( + clientRoleID, err := client.CreateClientRole( token.AccessToken, cfg.GoCloak.Realm, gocloakClientID, Role{ Name: &roleName, }) + t.Logf("Created Client Role ID: %s", clientRoleID) + assert.Equal(t, roleName, clientRoleID) + assert.NoError(t, err, "CreateClientRole failed") tearDown := func() { err := client.DeleteClientRole( @@ -776,21 +762,24 @@ func CreateClientScope(t *testing.T, client GoCloak, scope *ClientScope) (func() } t.Logf("Creating Client Scope: %+v", scope) - err := client.CreateClientScope( + clientScopeID, err := client.CreateClientScope( token.AccessToken, cfg.GoCloak.Realm, *scope, ) + if !NilOrEmpty(scope.ID) { + assert.Equal(t, clientScopeID, *(scope.ID)) + } assert.NoError(t, err, "CreateClientScope failed") tearDown := func() { err := client.DeleteClientScope( token.AccessToken, cfg.GoCloak.Realm, - *(scope.ID), + clientScopeID, ) assert.NoError(t, err, "DeleteClientScope failed") } - return tearDown, *(scope.ID) + return tearDown, clientScopeID } func TestGocloak_CreateClientScope_DeleteClientScope(t *testing.T) { @@ -988,7 +977,7 @@ func TestGocloak_CreateListGetUpdateDeleteClient(t *testing.T) { t.Logf("Client ID: %s", *clientID) // Creating a client - err := client.CreateClient( + createdClientID, err := client.CreateClient( token.AccessToken, cfg.GoCloak.Realm, Client{ @@ -1009,13 +998,14 @@ func TestGocloak_CreateListGetUpdateDeleteClient(t *testing.T) { ) assert.NoError(t, err, "CreateClients failed") assert.Len(t, clients, 1, "GetClients should return exact 1 client") + assert.Equal(t, createdClientID, *(clients[0].ID)) t.Logf("Clients: %+v", clients) // Getting exact client createdClient, err := client.GetClient( token.AccessToken, cfg.GoCloak.Realm, - *(clients[0].ID), + createdClientID, ) assert.NoError(t, err, "GetClient failed") t.Logf("Created client: %+v", createdClient) @@ -1045,7 +1035,7 @@ func TestGocloak_CreateListGetUpdateDeleteClient(t *testing.T) { updatedClient, err := client.GetClient( token.AccessToken, cfg.GoCloak.Realm, - *(clients[0].ID), + createdClientID, ) assert.NoError(t, err, "GetClient failed") t.Logf("Update client: %+v", createdClient) @@ -1055,7 +1045,7 @@ func TestGocloak_CreateListGetUpdateDeleteClient(t *testing.T) { err = client.DeleteClient( token.AccessToken, cfg.GoCloak.Realm, - *(createdClient.ID), + createdClientID, ) assert.NoError(t, err, "DeleteClient failed") @@ -1290,17 +1280,18 @@ func CreateRealm(t *testing.T, client GoCloak) (func(), string) { realmName := GetRandomName("Realm") t.Logf("Creating Realm: %s", realmName) - err := client.CreateRealm( + realmID, err := client.CreateRealm( token.AccessToken, RealmRepresentation{ Realm: &realmName, }) - FailIfErr(t, err, "CreateRealm failed") + assert.NoError(t, err, "CreateRealm failed") + assert.Equal(t, realmID, realmName) tearDown := func() { err := client.DeleteRealm( token.AccessToken, realmName) - FailIfErr(t, err, "DeleteRealm failed") + assert.NoError(t, err, "DeleteRealm failed") } return tearDown, realmName } @@ -1328,20 +1319,21 @@ func CreateRealmRole(t *testing.T, client GoCloak) (func(), string) { roleName := GetRandomName("Role") t.Logf("Creating RoleName: %s", roleName) - err := client.CreateRealmRole( + realmRoleID, err := client.CreateRealmRole( token.AccessToken, cfg.GoCloak.Realm, Role{ Name: &roleName, ContainerID: StringP("asd"), }) - FailIfErr(t, err, "CreateRealmRole failed") + assert.NoError(t, err, "CreateRealmRole failed") + assert.Equal(t, roleName, realmRoleID) tearDown := func() { err := client.DeleteRealmRole( token.AccessToken, cfg.GoCloak.Realm, roleName) - FailIfErr(t, err, "DeleteRealmRole failed") + assert.NoError(t, err, "DeleteRealmRole failed") } return tearDown, roleName } @@ -1954,10 +1946,27 @@ func TestGocloak_CreateDeleteClientProtocolMapper(t *testing.T) { t.Parallel() cfg := GetConfig(t) client := NewClientWithDebug(t) + id := GetRandomName("protocol-mapper-id-") testClient := GetClientByClientID(t, client, cfg.GoCloak.ClientID) + + found := false + for _, protocolMapper := range testClient.ProtocolMappers { + if protocolMapper == nil || NilOrEmpty(protocolMapper.ID) { + continue + } + if *(protocolMapper.ID) == id { + found = true + break + } + } + assert.False( + t, + found, + "default client should not have a protocol mapper with ID: %s", id, + ) + token := GetAdminToken(t, client) - id := GetRandomName("protocol-mapper-id-") - err := client.CreateClientProtocolMapper( + createdID, err := client.CreateClientProtocolMapper( token.AccessToken, cfg.GoCloak.Realm, *(testClient.ID), @@ -1978,18 +1987,49 @@ func TestGocloak_CreateDeleteClientProtocolMapper(t *testing.T) { }, }, ) - FailIfErr(t, err, "CreateClientProtocolMapper failed") + assert.NoError(t, err, "CreateClientProtocolMapper failed") + assert.Equal(t, id, createdID) testClientAfter := GetClientByClientID(t, client, cfg.GoCloak.ClientID) - FailIf(t, len(testClient.ProtocolMappers) >= len(testClientAfter.ProtocolMappers), "protocol mapper has not been created") + + found = false + for _, protocolMapper := range testClientAfter.ProtocolMappers { + if protocolMapper == nil || NilOrEmpty(protocolMapper.ID) { + continue + } + if *(protocolMapper.ID) == id { + found = true + break + } + } + assert.True( + t, + found, + "protocol mapper has not been created", + ) err = client.DeleteClientProtocolMapper( token.AccessToken, cfg.GoCloak.Realm, *(testClient.ID), id, ) - FailIfErr(t, err, "DeleteClientProtocolMapper failed") + assert.NoError(t, err, "DeleteClientProtocolMapper failed") testClientAgain := GetClientByClientID(t, client, cfg.GoCloak.ClientID) - FailIf(t, len(testClient.ProtocolMappers) != len(testClientAgain.ProtocolMappers), "protocol mapper has not been deleted") + + found = false + for _, protocolMapper := range testClientAgain.ProtocolMappers { + if protocolMapper == nil || NilOrEmpty(protocolMapper.ID) { + continue + } + if *(protocolMapper.ID) == id { + found = true + break + } + } + assert.False( + t, + found, + "default client should not have a protocol mapper with ID: %s", id, + ) } func TestGocloak_GetClientOfflineSessions(t *testing.T) { @@ -2027,7 +2067,7 @@ func TestGoCloak_ClientSecret(t *testing.T) { token := GetAdminToken(t, client) testClient := Client{ - ID: GetRandomNameP("gocloak-client-secret-id-"), + ID: GetRandomNameP("gocloak-client-id-"), ClientID: GetRandomNameP("gocloak-client-secret-client-id-"), Secret: StringP("initial-secret-key"), ServiceAccountsEnabled: BoolP(true), @@ -2039,30 +2079,31 @@ func TestGoCloak_ClientSecret(t *testing.T) { ClientAuthenticatorType: StringP("client-secret"), } - err := client.CreateClient( + clientID, err := client.CreateClient( token.AccessToken, cfg.GoCloak.Realm, testClient, ) assert.NoError(t, err, "CreateClient failed") + assert.Equal(t, *(testClient.ID), clientID) oldCreds, err := client.GetClientSecret( token.AccessToken, cfg.GoCloak.Realm, - *(testClient.ID), + clientID, ) assert.NoError(t, err, "GetClientSecret failed") regeneratedCreds, err := client.RegenerateClientSecret( token.AccessToken, cfg.GoCloak.Realm, - *(testClient.ID), + clientID, ) assert.NoError(t, err, "RegenerateClientSecret failed") - assert.NotEqual(t, oldCreds.Value, regeneratedCreds.Value) + assert.NotEqual(t, *(oldCreds.Value), *(regeneratedCreds.Value)) - err = client.DeleteClient(token.AccessToken, cfg.GoCloak.Realm, *(testClient.ID)) + err = client.DeleteClient(token.AccessToken, cfg.GoCloak.Realm, clientID) assert.NoError(t, err, "DeleteClient failed") } @@ -2136,7 +2177,7 @@ func TestGocloak_CreateDeleteClientScopeWithMappers(t *testing.T) { rolemapperID := GetRandomName("client-rolemapper-id-") audiencemapperID := GetRandomName("client-audiencemapper-id-") - err := client.CreateClientScope( + createdID, err := client.CreateClientScope( token.AccessToken, cfg.GoCloak.Realm, ClientScope{ @@ -2182,6 +2223,7 @@ func TestGocloak_CreateDeleteClientScopeWithMappers(t *testing.T) { }, ) assert.NoError(t, err, "CreateClientScope failed") + assert.Equal(t, id, createdID) clientScopeActual, err := client.GetClientScope(token.AccessToken, cfg.GoCloak.Realm, id) assert.NoError(t, err) diff --git a/gocloak.go b/gocloak.go index 5d1e21bd..26729de2 100644 --- a/gocloak.go +++ b/gocloak.go @@ -47,13 +47,13 @@ type GoCloak interface { ExecuteActionsEmail(token string, realm string, params ExecuteActionsEmail) error // CreateGroup creates a new group - CreateGroup(accessToken string, realm string, group Group) error + CreateGroup(accessToken, realm string, group Group) (string, error) // CreateClient creates a new client - CreateClient(accessToken string, realm string, clientID Client) error + CreateClient(accessToken, realm string, clientID Client) (string, error) // CreateClientScope creates a new clientScope - CreateClientScope(accessToken string, realm string, scope ClientScope) error + CreateClientScope(accessToken, realm string, scope ClientScope) (string, error) // CreateComponent creates a new component - CreateComponent(accessToken string, realm string, component Component) error + CreateComponent(accessToken, realm string, component Component) (string, error) // UpdateGroup updates the given group UpdateGroup(accessToken string, realm string, updatedGroup Group) error @@ -122,7 +122,7 @@ type GoCloak interface { // GetClientUserSessions returns user sessions associated with the client GetClientUserSessions(token, realm, clientID string) ([]*UserSessionRepresentation, error) // CreateClientProtocolMapper creates a protocol mapper in client scope - CreateClientProtocolMapper(token, realm, clientID string, mapper ProtocolMapperRepresentation) error + CreateClientProtocolMapper(token, realm, clientID string, mapper ProtocolMapperRepresentation) (string, error) // DeleteClientProtocolMapper deletes a protocol mapper in client scope DeleteClientProtocolMapper(token, realm, clientID, mapperID string) error @@ -132,7 +132,7 @@ type GoCloak interface { // *** Realm Roles *** // CreateRealmRole creates a role in a realm - CreateRealmRole(token string, realm string, role Role) error + CreateRealmRole(token, realm string, role Role) (string, error) // GetRealmRole returns a role from a realm by role's name GetRealmRole(token string, realm string, roleName string) (*Role, error) // GetRealmRoles get all roles of the given realm. It's an alias for the GetRoles function @@ -159,7 +159,7 @@ type GoCloak interface { // AddClientRoleToUser adds a client role to the user AddClientRoleToUser(token string, realm string, clientID string, userID string, roles []Role) error // CreateClientRole creates a new role for a client - CreateClientRole(accessToken string, realm string, clientID string, role Role) error + CreateClientRole(accessToken, realm, clientID string, role Role) (string, error) // DeleteClientRole deletes the given role DeleteClientRole(accessToken, realm, clientID, roleName string) error // DeleteClientRoleFromUser removes a client role from from the user @@ -176,7 +176,7 @@ type GoCloak interface { // GetRealms returns top-level representation of all realms GetRealms(token string) ([]*RealmRepresentation, error) // CreateRealm creates a realm - CreateRealm(token string, realm RealmRepresentation) error + CreateRealm(token string, realm RealmRepresentation) (string, error) // DeleteRealm removes a realm DeleteRealm(token string, realm string) error // ClearRealmCache clears realm cache