diff --git a/.travis.yml b/.travis.yml index 5e74d305..720a1fd5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ jobs: - docker pull quay.io/keycloak/keycloak - docker run -d -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=secret -e KEYCLOAK_IMPORT=/tmp/gocloak-realm.json -v "`pwd`/testdata/gocloak-realm.json:/tmp/gocloak-realm.json" -p 8080:8080 --name keycloak quay.io/keycloak/keycloak script: - - go test -cover -v -race -coverprofile=coverage.txt -covermode=atomic + - go test -cover -race -coverprofile=coverage.txt -covermode=atomic after_success: - bash <(curl -s https://codecov.io/bash) after_failure: diff --git a/client.go b/client.go index a7f31c7d..3630f08a 100644 --- a/client.go +++ b/client.go @@ -83,10 +83,10 @@ func checkForError(resp *resty.Response, err error) error { return nil } -func findUsedKey(usedKeyID string, keys []CertResponseKey) *CertResponseKey { +func findUsedKey(usedKeyID string, keys []*CertResponseKey) *CertResponseKey { for _, key := range keys { - if key.Kid == usedKeyID { - return &key + if *(key.Kid) == usedKeyID { + return key } } @@ -99,7 +99,6 @@ func findUsedKey(usedKeyID string, keys []CertResponseKey) *CertResponseKey { // NewClient creates a new Client func NewClient(basePath string) GoCloak { - c := gocloak{ basePath: strings.TrimRight(basePath, urlSeparator), certsCache: make(map[string]*CertResponse), @@ -114,8 +113,8 @@ func (client *gocloak) RestyClient() *resty.Client { return client.restyClient } -func (client *gocloak) SetRestyClient(restyClient resty.Client) { - client.restyClient = &restyClient +func (client *gocloak) SetRestyClient(restyClient *resty.Client) { + client.restyClient = restyClient } func (client *gocloak) getRealmURL(realm string, path ...string) string { @@ -219,7 +218,7 @@ func (client *gocloak) RetrospectToken(accessToken string, clientID, clientSecre } // DecodeAccessToken decodes the accessToken -func (client *gocloak) DecodeAccessToken(accessToken string, realm string) (*jwt.Token, *jwt.MapClaims, error) { +func (client *gocloak) DecodeAccessToken(accessToken, realm string) (*jwt.Token, *jwt.MapClaims, error) { decodedHeader, err := jwx.DecodeAccessTokenHeader(accessToken) if err != nil { return nil, nil, err @@ -261,8 +260,8 @@ func (client *gocloak) DecodeAccessTokenCustomClaims(accessToken string, realm s func (client *gocloak) GetToken(realm string, options TokenOptions) (*JWT, error) { var token JWT var req *resty.Request - if len(options.ClientSecret) > 0 { - req = client.getRequestWithBasicAuth(options.ClientID, options.ClientSecret) + if !NilOrEmpty(options.ClientSecret) { + req = client.getRequestWithBasicAuth(*(options.ClientID), *(options.ClientSecret)) } else { req = client.getRequest() } @@ -280,40 +279,40 @@ func (client *gocloak) GetToken(realm string, options TokenOptions) (*JWT, error // RefreshToken refreshes the given token func (client *gocloak) RefreshToken(refreshToken, clientID, clientSecret, realm string) (*JWT, error) { return client.GetToken(realm, TokenOptions{ - ClientID: clientID, - ClientSecret: clientSecret, - GrantType: "refresh_token", - RefreshToken: refreshToken, + ClientID: &clientID, + ClientSecret: &clientSecret, + GrantType: StringP("refresh_token"), + RefreshToken: &refreshToken, }) } // LoginAdmin performs a login with Admin client func (client *gocloak) LoginAdmin(username, password, realm string) (*JWT, error) { return client.GetToken(realm, TokenOptions{ - ClientID: adminClientID, - GrantType: "password", - Username: username, - Password: password, + ClientID: StringP(adminClientID), + GrantType: StringP("password"), + Username: &username, + Password: &password, }) } // Login performs a login with client credentials func (client *gocloak) LoginClient(clientID, clientSecret, realm string) (*JWT, error) { return client.GetToken(realm, TokenOptions{ - ClientID: clientID, - ClientSecret: clientSecret, - GrantType: "client_credentials", + ClientID: &clientID, + ClientSecret: &clientSecret, + GrantType: StringP("client_credentials"), }) } // Login performs a login with user credentials and a client func (client *gocloak) Login(clientID, clientSecret, realm, username, password string) (*JWT, error) { return client.GetToken(realm, TokenOptions{ - ClientID: clientID, - ClientSecret: clientSecret, - GrantType: "password", - Username: username, - Password: password, + ClientID: &clientID, + ClientSecret: &clientSecret, + GrantType: StringP("password"), + Username: &username, + Password: &password, }) } @@ -343,12 +342,12 @@ func (client *gocloak) LogoutPublicClient(clientID, realm, accessToken, refreshT // RequestPermission request a permission func (client *gocloak) RequestPermission(clientID, clientSecret, realm, username, password string, permission string) (*JWT, error) { return client.GetToken(realm, TokenOptions{ - ClientID: clientID, - ClientSecret: clientSecret, - GrantType: "password", - Username: username, - Password: password, - Permission: permission, + ClientID: &clientID, + ClientSecret: &clientSecret, + GrantType: StringP("password"), + Username: &username, + Password: &password, + Permission: &permission, }) } @@ -361,7 +360,7 @@ func (client *gocloak) ExecuteActionsEmail(token string, realm string, params Ex resp, err := client.getRequestWithBearerAuth(token). SetBody(params.Actions). SetQueryParams(queryParams). - Put(client.getAdminRealmURL(realm, "users", params.UserID, "execute-actions-email")) + Put(client.getAdminRealmURL(realm, "users", *(params.UserID), "execute-actions-email")) return checkForError(resp, err) } @@ -413,24 +412,24 @@ func (client *gocloak) CreateClientScope(token string, realm string, scope Clien // UpdateUser creates a new user func (client *gocloak) UpdateGroup(token string, realm string, updatedGroup Group) error { - if len(updatedGroup.ID) == 0 { + if NilOrEmpty(updatedGroup.ID) { return errors.New("ID of a group required") } resp, err := client.getRequestWithBearerAuth(token). SetBody(updatedGroup). - Put(client.getAdminRealmURL(realm, "groups", updatedGroup.ID)) + Put(client.getAdminRealmURL(realm, "groups", PString(updatedGroup.ID))) return checkForError(resp, err) } // UpdateClient updates the given Client func (client *gocloak) UpdateClient(token string, realm string, updatedClient Client) error { - if len(updatedClient.ID) == 0 { + if NilOrEmpty(updatedClient.ID) { return errors.New("ID of a client required") } resp, err := client.getRequestWithBearerAuth(token). SetBody(updatedClient). - Put(client.getAdminRealmURL(realm, "clients", updatedClient.ID)) + Put(client.getAdminRealmURL(realm, "clients", PString(updatedClient.ID))) return checkForError(resp, err) } @@ -439,7 +438,7 @@ func (client *gocloak) UpdateClient(token string, realm string, updatedClient Cl func (client *gocloak) UpdateRole(token string, realm string, clientID string, role Role) error { resp, err := client.getRequestWithBearerAuth(token). SetBody(role). - Put(client.getAdminRealmURL(realm, "clients", clientID, "roles", role.Name)) + Put(client.getAdminRealmURL(realm, "clients", clientID, "roles", PString(role.Name))) return checkForError(resp, err) } @@ -448,7 +447,7 @@ func (client *gocloak) UpdateRole(token string, realm string, clientID string, r func (client *gocloak) UpdateClientScope(token string, realm string, scope ClientScope) error { resp, err := client.getRequestWithBearerAuth(token). SetBody(scope). - Put(client.getAdminRealmURL(realm, "client-scopes", scope.ID)) + Put(client.getAdminRealmURL(realm, "client-scopes", PString(scope.ID))) return checkForError(resp, err) } @@ -478,7 +477,7 @@ func (client *gocloak) DeleteComponent(token string, realm string, componentID s } // DeleteClientRole deletes a given role -func (client *gocloak) DeleteClientRole(token string, realm string, clientID, roleName string) error { +func (client *gocloak) DeleteClientRole(token, realm, clientID, roleName string) error { resp, err := client.getRequestWithBearerAuth(token). Delete(client.getAdminRealmURL(realm, "clients", clientID, "roles", roleName)) @@ -1162,7 +1161,7 @@ func (client *gocloak) GetUsersByRoleName(token string, realm string, roleName s // SetPassword sets a new password for the user with the given id. Needs elevated privileges func (client *gocloak) SetPassword(token string, userID string, realm string, password string, temporary bool) error { - requestBody := SetPasswordRequest{Password: password, Temporary: temporary, Type: "password"} + requestBody := SetPasswordRequest{Password: &password, Temporary: &temporary, Type: StringP("password")} resp, err := client.getRequestWithBearerAuth(token). SetBody(requestBody). Put(client.getAdminRealmURL(realm, "users", userID, "reset-password")) @@ -1174,7 +1173,7 @@ func (client *gocloak) SetPassword(token string, userID string, realm string, pa func (client *gocloak) UpdateUser(token string, realm string, user User) error { resp, err := client.getRequestWithBearerAuth(token). SetBody(user). - Put(client.getAdminRealmURL(realm, "users", user.ID)) + Put(client.getAdminRealmURL(realm, "users", PString(user.ID))) return checkForError(resp, err) } diff --git a/client_test.go b/client_test.go index b036df29..93427297 100644 --- a/client_test.go +++ b/client_test.go @@ -11,7 +11,6 @@ import ( "net/url" "os" "path/filepath" - "reflect" "runtime" "strconv" "strings" @@ -75,20 +74,6 @@ func FailIfErr(t *testing.T, err error, msg string, args ...interface{}) { } } -func FailIfNotErr(t *testing.T, err error, msg string, args ...interface{}) { - if err == nil { - _, file, line, _ := runtime.Caller(1) - if len(msg) == 0 { - msg = "unexpected success" - } else { - if len(args) > 0 { - msg = fmt.Sprintf(msg, args...) - } - } - t.Fatalf("%s:%d: %s", filepath.Base(file), line, msg) - } -} - func FailIf(t *testing.T, cond bool, msg string, args ...interface{}) { if cond { if len(args) > 0 { @@ -99,20 +84,6 @@ func FailIf(t *testing.T, cond bool, msg string, args ...interface{}) { } } -func AssertEquals(t *testing.T, exp interface{}, act interface{}) { - FailIf( - t, - !reflect.DeepEqual(exp, act), - "The expected and actual results are not equal.\nExpected: %+v.\nActual: %+v", exp, act) -} - -func AssertNotEquals(t *testing.T, exp interface{}, act interface{}) { - FailIf( - t, - reflect.DeepEqual(exp, act), - "The expected and actual results are equal.\nExpected: %+v.\nActual: %+v", exp, act) -} - func GetConfig(t *testing.T) *Config { configOnce.Do(func() { rand.Seed(time.Now().UTC().UnixNano()) @@ -184,6 +155,11 @@ func GetRandomName(name string) string { return name + strconv.Itoa(randomNumber) } +func GetRandomNameP(name string) *string { + r := GetRandomName(name) + return &r +} + func GetClientByClientID(t *testing.T, client GoCloak, clientID string) *Client { cfg := GetConfig(t) token := GetAdminToken(t, client) @@ -191,11 +167,14 @@ func GetClientByClientID(t *testing.T, client GoCloak, clientID string) *Client token.AccessToken, cfg.GoCloak.Realm, GetClientsParams{ - ClientID: clientID, + ClientID: &clientID, }) - FailIfErr(t, err, "GetClients failed") + assert.NoError(t, err, "GetClients failed") for _, fetchedClient := range clients { - if fetchedClient.ClientID == clientID { + if fetchedClient.ClientID == nil { + continue + } + if *(fetchedClient.ClientID) == clientID { return fetchedClient } } @@ -207,7 +186,7 @@ func CreateGroup(t *testing.T, client GoCloak) (func(), string) { cfg := GetConfig(t) token := GetAdminToken(t, client) group := Group{ - Name: GetRandomName("GroupName"), + Name: GetRandomNameP("GroupName"), Attributes: map[string][]string{ "foo": {"bar", "alice", "bob", "roflcopter"}, "bar": {"baz"}, @@ -227,8 +206,11 @@ func CreateGroup(t *testing.T, client GoCloak) (func(), string) { FailIfErr(t, err, "GetGroups failed") var groupID string for _, fetchedGroup := range groups { - if fetchedGroup.Name == group.Name { - groupID = fetchedGroup.ID + if fetchedGroup.Name == nil { + continue + } + if *(fetchedGroup.Name) == *(group.Name) { + groupID = PString(fetchedGroup.ID) break } } @@ -250,10 +232,10 @@ func SetUpTestUser(t *testing.T, client GoCloak) { token := GetAdminToken(t, client) user := User{ - Username: cfg.GoCloak.UserName, - Email: cfg.GoCloak.UserName + "@localhost", - EmailVerified: true, - Enabled: true, + Username: StringP(cfg.GoCloak.UserName), + Email: StringP(cfg.GoCloak.UserName + "@localhost"), + EmailVerified: BoolP(true), + Enabled: BoolP(true), } createdUserID, err := client.CreateUser( @@ -266,12 +248,12 @@ func SetUpTestUser(t *testing.T, client GoCloak) { token.AccessToken, cfg.GoCloak.Realm, GetUsersParams{ - Username: cfg.GoCloak.UserName, + Username: StringP(cfg.GoCloak.UserName), }) FailIfErr(t, err, "GetUsers failed") for _, user := range users { - if user.Username == cfg.GoCloak.UserName { - testUserID = user.ID + if PString(user.Username) == cfg.GoCloak.UserName { + testUserID = PString(user.ID) break } } @@ -381,44 +363,20 @@ func ClearRealmCache(t *testing.T, client GoCloak, realm ...string) { // Tests // ----- -func TestGetQueryParams(t *testing.T) { +func TestGocloak_RestyClient(t *testing.T) { t.Parallel() - - type TestParams struct { - IntField int `json:"int_field,string,omitempty"` - StringField string `json:"string_field,omitempty"` - BoolField bool `json:"bool_field,string,omitempty"` - } - - params, err := GetQueryParams(TestParams{}) - FailIfErr(t, err, "GetQueryParams failed") - FailIf( - t, - len(params) > 0, - "Params must be empty, but got: %+v", params) - - params, err = GetQueryParams(TestParams{ - IntField: 1, - StringField: "fake", - BoolField: true, - }) - FailIfErr(t, err, "GetQueryParams failed") - AssertEquals(t, map[string]string{ - "int_field": "1", - "string_field": "fake", - "bool_field": "true", - }, params) + client := NewClientWithDebug(t) + restyClient := client.RestyClient() + assert.NotEqual(t, restyClient, resty.New()) } -func TestGocloak_RestyClient(t *testing.T) { +func TestGocloak_SetRestyClient(t *testing.T) { t.Parallel() client := NewClientWithDebug(t) + newRestyClient := resty.New() + client.SetRestyClient(newRestyClient) restyClient := client.RestyClient() - FailIf( - t, - restyClient == resty.New(), - "Resty client of the GoCloak client and the Default resty client are equal", - ) + assert.Equal(t, newRestyClient, restyClient) } func TestGocloak_checkForError(t *testing.T) { @@ -426,7 +384,7 @@ func TestGocloak_checkForError(t *testing.T) { client := NewClientWithDebug(t) FailRequest(client, nil, 1, 0) _, err := client.Login("", "", "", "", "") - FailIfNotErr(t, err, "All requests must fail with NewClientWithError") + assert.Error(t, err, "All requests must fail with NewClientWithError") t.Logf("Error: %s", err.Error()) } @@ -441,14 +399,14 @@ func TestGocloak_GetServerInfo(t *testing.T) { serverInfo, err := client.GetServerInfo( token.AccessToken, ) - FailIfErr(t, err, "Failed to fetch server info") + assert.NoError(t, err, "Failed to fetch server info") t.Logf("Server Info: %+v", serverInfo) FailRequest(client, nil, 1, 0) _, err = client.GetServerInfo( token.AccessToken, ) - FailIfNotErr(t, err, "") + assert.Error(t, err) } func TestGocloak_GetUserInfo(t *testing.T) { @@ -459,13 +417,13 @@ func TestGocloak_GetUserInfo(t *testing.T) { userInfo, err := client.GetUserInfo( token.AccessToken, cfg.GoCloak.Realm) - FailIfErr(t, err, "Failed to fetch userinfo") + assert.NoError(t, err, "Failed to fetch userinfo") t.Log(userInfo) FailRequest(client, nil, 1, 0) _, err = client.GetUserInfo( token.AccessToken, cfg.GoCloak.Realm) - FailIfNotErr(t, err, "") + assert.Error(t, err, "") } func TestGocloak_RequestPermission(t *testing.T) { @@ -489,7 +447,7 @@ func TestGocloak_RequestPermission(t *testing.T) { cfg.GoCloak.Realm) t.Log(rptResult) FailIfErr(t, err, "inspection failed") - FailIf(t, !rptResult.Active, "Inactive Token oO") + FailIf(t, !PBool(rptResult.Active), "Inactive Token oO") } func TestGocloak_GetCerts(t *testing.T) { @@ -534,7 +492,7 @@ func TestGocloak_RetrospectToken_InactiveToken(t *testing.T) { cfg.GoCloak.Realm) t.Log(rptResult) FailIfErr(t, err, "inspection failed") - FailIf(t, rptResult.Active, "That should never happen. Token is active") + FailIf(t, PBool(rptResult.Active), "That should never happen. Token is active") } @@ -551,7 +509,7 @@ func TestGocloak_RetrospectToken(t *testing.T) { cfg.GoCloak.Realm) t.Log(rptResult) FailIfErr(t, err, "Inspection failed") - FailIf(t, !rptResult.Active, "Inactive Token oO") + FailIf(t, !PBool(rptResult.Active), "Inactive Token oO") } func TestGocloak_DecodeAccessToken(t *testing.T) { @@ -562,10 +520,11 @@ func TestGocloak_DecodeAccessToken(t *testing.T) { resultToken, claims, err := client.DecodeAccessToken( token.AccessToken, - cfg.GoCloak.Realm) + cfg.GoCloak.Realm, + ) + assert.NoError(t, err) t.Log(resultToken) t.Log(claims) - FailIfErr(t, err, "DecodeAccessToken") } func TestGocloak_DecodeAccessTokenCustomClaims(t *testing.T) { @@ -578,10 +537,11 @@ func TestGocloak_DecodeAccessTokenCustomClaims(t *testing.T) { resultToken, err := client.DecodeAccessTokenCustomClaims( token.AccessToken, cfg.GoCloak.Realm, - claims) + claims, + ) + assert.NoError(t, err) t.Log(resultToken) t.Log(claims) - FailIfErr(t, err, "DecodeAccessTokenCustomClaims") } func TestGocloak_RefreshToken(t *testing.T) { @@ -646,19 +606,19 @@ func TestGocloak_GetToken(t *testing.T) { newToken, err := client.GetToken( cfg.GoCloak.Realm, TokenOptions{ - ClientID: cfg.GoCloak.ClientID, - ClientSecret: cfg.GoCloak.ClientSecret, - Username: cfg.GoCloak.UserName, - Password: cfg.GoCloak.Password, - GrantType: "password", + ClientID: &cfg.GoCloak.ClientID, + ClientSecret: &cfg.GoCloak.ClientSecret, + Username: &cfg.GoCloak.UserName, + Password: &cfg.GoCloak.Password, + GrantType: StringP("password"), ResponseTypes: []string{"token", "id_token"}, Scopes: []string{"openid", "offline_access"}, }, ) - FailIfErr(t, err, "Login failed") + assert.NoError(t, err, "Login failed") t.Logf("New token: %+v", *newToken) - FailIf(t, newToken.RefreshExpiresIn > 0, "Got a refresh token instead of offline") - FailIf(t, len(newToken.IDToken) == 0, "Got an empty if token") + assert.Equal(t, newToken.RefreshExpiresIn, 0, "Got a refresh token instead of offline") + assert.NotEmpty(t, newToken.IDToken, "Got an empty if token") } func TestGocloak_LoginClient(t *testing.T) { @@ -707,43 +667,43 @@ func TestGocloak_CreateListGetUpdateDeleteGroup(t *testing.T) { client := NewClientWithDebug(t) token := GetAdminToken(t, client) - // Create, List + // Create tearDown, groupID := CreateGroup(t, client) + // Delete + defer tearDown() + // List createdGroup, err := client.GetGroup( token.AccessToken, cfg.GoCloak.Realm, groupID, ) - FailIfErr(t, err, "GetGroup failed") + assert.NoError(t, err, "GetGroup failed") t.Logf("Created Group: %+v", createdGroup) - AssertEquals(t, groupID, createdGroup.ID) + assert.Equal(t, groupID, *(createdGroup.ID)) err = client.UpdateGroup( token.AccessToken, cfg.GoCloak.Realm, Group{}, ) - FailIfNotErr(t, err, "Should fail because of missing ID of the group") + assert.Error(t, err, "Should fail because of missing ID of the group") - createdGroup.Name = GetRandomName("GroupName") + createdGroup.Name = GetRandomNameP("GroupName") err = client.UpdateGroup( token.AccessToken, cfg.GoCloak.Realm, *createdGroup, ) - FailIfErr(t, err, "UpdateGroup failed") + assert.NoError(t, err, "UpdateGroup failed") updatedGroup, err := client.GetGroup( token.AccessToken, cfg.GoCloak.Realm, groupID, ) - FailIfErr(t, err, "GetGroup failed") - AssertEquals(t, createdGroup.Name, updatedGroup.Name) - - // Delete - defer tearDown() + assert.NoError(t, err, "GetGroup failed") + assert.Equal(t, *(createdGroup.Name), *(updatedGroup.Name)) } func CreateClientRole(t *testing.T, client GoCloak) (func(), string) { @@ -757,7 +717,7 @@ func CreateClientRole(t *testing.T, client GoCloak) (func(), string) { cfg.GoCloak.Realm, gocloakClientID, Role{ - Name: roleName, + Name: &roleName, }) assert.NoError(t, err, "CreateClientRole failed") tearDown := func() { @@ -809,10 +769,11 @@ func CreateClientScope(t *testing.T, client GoCloak, scope *ClientScope) (func() token := GetAdminToken(t, client) if scope == nil { - scope = &ClientScope{} + scope = &ClientScope{ + ID: GetRandomNameP("client-scope-id-"), + Name: GetRandomNameP("client-scope-name-"), + } } - scope.ID = GetRandomName("client-scope-id-") - scope.Name = GetRandomName("client-scope-name-") t.Logf("Creating Client Scope: %+v", scope) err := client.CreateClientScope( @@ -825,11 +786,11 @@ func CreateClientScope(t *testing.T, client GoCloak, scope *ClientScope) (func() err := client.DeleteClientScope( token.AccessToken, cfg.GoCloak.Realm, - scope.ID, + *(scope.ID), ) assert.NoError(t, err, "DeleteClientScope failed") } - return tearDown, scope.ID + return tearDown, *(scope.ID) } func TestGocloak_CreateClientScope_DeleteClientScope(t *testing.T) { @@ -848,9 +809,11 @@ func TestGocloak_ListAddRemoveDefaultClientScopes(t *testing.T) { defer ClearRealmCache(t, client) scope := ClientScope{ - Protocol: "openid-connect", + ID: GetRandomNameP("client-scope-id-"), + Name: GetRandomNameP("client-scope-name-"), + Protocol: StringP("openid-connect"), ClientScopeAttributes: &ClientScopeAttributes{ - IncludeInTokenScope: "true", + IncludeInTokenScope: StringP("true"), }, } @@ -907,9 +870,11 @@ func TestGocloak_ListAddRemoveOptionalClientScopes(t *testing.T) { defer ClearRealmCache(t, client) scope := ClientScope{ - Protocol: "openid-connect", + ID: GetRandomNameP("client-scope-id-"), + Name: GetRandomNameP("client-scope-name-"), + Protocol: StringP("openid-connect"), ClientScopeAttributes: &ClientScopeAttributes{ - IncludeInTokenScope: "true", + IncludeInTokenScope: StringP("true"), }, } tearDown, scopeID := CreateClientScope(t, client, &scope) @@ -995,7 +960,8 @@ func TestGocloak_GetClientScope(t *testing.T) { ) assert.NoError(t, err, "GetClientScope failed") // Checking that GetClientScope returns same client scope - assert.Equal(t, scopeID, createdClientScope.ID) + assert.NotNil(t, createdClientScope.ID) + assert.Equal(t, scopeID, *(createdClientScope.ID)) } func TestGocloak_GetClientScopes(t *testing.T) { @@ -1018,8 +984,8 @@ func TestGocloak_CreateListGetUpdateDeleteClient(t *testing.T) { cfg := GetConfig(t) client := NewClientWithDebug(t) token := GetAdminToken(t, client) - clientID := GetRandomName("ClientID") - t.Logf("Client ID: %s", clientID) + clientID := GetRandomNameP("ClientID") + t.Logf("Client ID: %s", *clientID) // Creating a client err := client.CreateClient( @@ -1027,11 +993,11 @@ func TestGocloak_CreateListGetUpdateDeleteClient(t *testing.T) { cfg.GoCloak.Realm, Client{ ClientID: clientID, - Name: GetRandomName("Name"), - BaseURL: "http://example.com", + Name: GetRandomNameP("Name"), + BaseURL: StringP("http://example.com"), }, ) - FailIfErr(t, err, "CreateClient failed") + assert.NoError(t, err, "CreateClient failed") // Looking for a created client clients, err := client.GetClients( @@ -1041,20 +1007,20 @@ func TestGocloak_CreateListGetUpdateDeleteClient(t *testing.T) { ClientID: clientID, }, ) - FailIfErr(t, err, "CreateClients failed") - FailIf(t, len(clients) != 1, "GetClients should return exact 1 client") + assert.NoError(t, err, "CreateClients failed") + assert.Len(t, clients, 1, "GetClients should return exact 1 client") t.Logf("Clients: %+v", clients) // Getting exact client createdClient, err := client.GetClient( token.AccessToken, cfg.GoCloak.Realm, - clients[0].ID, + *(clients[0].ID), ) - FailIfErr(t, err, "GetClient failed") + assert.NoError(t, err, "GetClient failed") t.Logf("Created client: %+v", createdClient) // Checking that GetClient returns same client - AssertEquals(t, clients[0], createdClient) + assert.Equal(t, clients[0], createdClient) // Updating the client @@ -1064,34 +1030,34 @@ func TestGocloak_CreateListGetUpdateDeleteClient(t *testing.T) { cfg.GoCloak.Realm, Client{}, ) - FailIfNotErr(t, err, "Should fail because of missing ID of the client") + assert.Error(t, err, "Should fail because of missing ID of the client") // Update existing client - createdClient.Name = GetRandomName("Name") + createdClient.Name = GetRandomNameP("Name") err = client.UpdateClient( token.AccessToken, cfg.GoCloak.Realm, *createdClient, ) - FailIfErr(t, err, "GetClient failed") + assert.NoError(t, err, "GetClient failed") // Getting updated client updatedClient, err := client.GetClient( token.AccessToken, cfg.GoCloak.Realm, - clients[0].ID, + *(clients[0].ID), ) - FailIfErr(t, err, "GetClient failed") + assert.NoError(t, err, "GetClient failed") t.Logf("Update client: %+v", createdClient) - AssertEquals(t, *createdClient, *updatedClient) + assert.Equal(t, *createdClient, *updatedClient) // Deleting the client err = client.DeleteClient( token.AccessToken, cfg.GoCloak.Realm, - createdClient.ID, + *(createdClient.ID), ) - FailIfErr(t, err, "DeleteClient failed") + assert.NoError(t, err, "DeleteClient failed") // Verifying that the client was deleted clients, err = client.GetClients( @@ -1101,9 +1067,8 @@ func TestGocloak_CreateListGetUpdateDeleteClient(t *testing.T) { ClientID: clientID, }, ) - FailIfErr(t, err, "CreateClients failed") - FailIf(t, len(clients) != 0, "GetClients should not return any clients") - + assert.NoError(t, err, "CreateClients failed") + assert.Len(t, clients, 0, "GetClients should not return any clients") } func TestGocloak_GetGroups(t *testing.T) { @@ -1132,12 +1097,15 @@ func TestGocloak_GetGroupsFull(t *testing.T) { token.AccessToken, cfg.GoCloak.Realm, GetGroupsParams{ - Full: true, + Full: BoolP(true), }) assert.NoError(t, err, "GetGroups failed") for _, group := range groups { - if group.ID == groupID { + if NilOrEmpty(group.ID) { + continue + } + if *(group.ID) == groupID { ok := client.UserAttributeContains(group.Attributes, "foo", "alice") assert.True(t, ok, "UserAttributeContains") return @@ -1212,7 +1180,7 @@ func TestGocloak_GetClientRoles(t *testing.T) { _, err := client.GetClientRoles( token.AccessToken, cfg.GoCloak.Realm, - testClient.ID) + *(testClient.ID)) FailIfErr(t, err, "GetClientRoles failed") } @@ -1258,8 +1226,8 @@ func TestGocloak_ExecuteActionsEmail_UpdatePassword(t *testing.T) { defer tearDown() params := ExecuteActionsEmail{ - ClientID: cfg.GoCloak.ClientID, - UserID: userID, + ClientID: &(cfg.GoCloak.ClientID), + UserID: &userID, Actions: []string{"UPDATE_PASSWORD"}, } @@ -1325,7 +1293,7 @@ func CreateRealm(t *testing.T, client GoCloak) (func(), string) { err := client.CreateRealm( token.AccessToken, RealmRepresentation{ - Realm: realmName, + Realm: &realmName, }) FailIfErr(t, err, "CreateRealm failed") tearDown := func() { @@ -1364,8 +1332,8 @@ func CreateRealmRole(t *testing.T, client GoCloak) (func(), string) { token.AccessToken, cfg.GoCloak.Realm, Role{ - Name: roleName, - ContainerID: "asd", + Name: &roleName, + ContainerID: StringP("asd"), }) FailIfErr(t, err, "CreateRealmRole failed") tearDown := func() { @@ -1402,7 +1370,7 @@ func TestGocloak_GetRealmRole(t *testing.T) { t.Logf("Role: %+v", *role) FailIf( t, - role.Name != roleName, + *(role.Name) != roleName, "GetRealmRole returns unexpected result. Expected: %s; Actual: %+v", roleName, role) } @@ -1437,14 +1405,14 @@ func TestGocloak_UpdateRealmRole(t *testing.T) { cfg.GoCloak.Realm, oldRoleName, Role{ - Name: newRoleName, + Name: &newRoleName, }) - FailIfErr(t, err, "UpdateRealmRole failed") + assert.NoError(t, err, "UpdateRealmRole failed") err = client.DeleteRealmRole( token.AccessToken, cfg.GoCloak.Realm, oldRoleName) - FailIfNotErr( + assert.Error( t, err, "Role with old name was deleted successfully, but it shouldn't. Old role: %s; Updated role: %s", @@ -1453,7 +1421,7 @@ func TestGocloak_UpdateRealmRole(t *testing.T) { token.AccessToken, cfg.GoCloak.Realm, newRoleName) - FailIfErr(t, err, "DeleteRealmRole failed") + assert.NoError(t, err, "DeleteRealmRole failed") } func TestGocloak_DeleteRealmRole(t *testing.T) { @@ -1518,7 +1486,7 @@ func TestGocloak_GetRealmRolesByUserID(t *testing.T) { token.AccessToken, cfg.GoCloak.Realm, roleName) - FailIfErr(t, err, "GetRealmRole failed") + assert.NoError(t, err) err = client.AddRealmRoleToUser( token.AccessToken, @@ -1527,20 +1495,23 @@ func TestGocloak_GetRealmRolesByUserID(t *testing.T) { []Role{ *role, }) - FailIfErr(t, err, "AddRealmRoleToUser failed") + assert.NoError(t, err) roles, err := client.GetRealmRolesByUserID( token.AccessToken, cfg.GoCloak.Realm, userID) - FailIfErr(t, err, "GetRealmRolesByUserID failed") + assert.NoError(t, err) t.Logf("User roles: %+v", roles) for _, r := range roles { - if r.Name == role.Name { + if r.Name == nil { + continue + } + if *(r.Name) == *(role.Name) { return } } - t.Fatalf("The role has not been found in the assined roles. Role: %+v", *role) + assert.Fail(t, "The role has not been found in the assined roles. Role: %+v", *role) } func TestGocloak_GetRealmRolesByGroupID(t *testing.T) { @@ -1612,10 +1583,10 @@ func CreateUser(t *testing.T, client GoCloak) (func(), string) { token := GetAdminToken(t, client) user := User{ - FirstName: GetRandomName("FirstName"), - LastName: GetRandomName("LastName"), - Email: GetRandomName("email") + "@localhost", - Enabled: true, + FirstName: GetRandomNameP("FirstName"), + LastName: GetRandomNameP("LastName"), + Email: StringP(GetRandomName("email") + "@localhost"), + Enabled: BoolP(true), Attributes: map[string][]string{ "foo": {"bar", "alice", "bob", "roflcopter"}, "bar": {"baz"}, @@ -1628,17 +1599,17 @@ func CreateUser(t *testing.T, client GoCloak) (func(), string) { cfg.GoCloak.Realm, user) FailIfErr(t, err, "CreateUser failed") - user.ID = userID + user.ID = &userID t.Logf("Created User: %+v", user) tearDown := func() { err := client.DeleteUser( token.AccessToken, cfg.GoCloak.Realm, - user.ID) + *(user.ID)) FailIfErr(t, err, "DeleteUser") } - return tearDown, user.ID + return tearDown, *(user.ID) } func TestGocloak_CreateUser(t *testing.T) { @@ -1696,7 +1667,7 @@ func TestGocloak_GetUsers(t *testing.T) { token.AccessToken, cfg.GoCloak.Realm, GetUsersParams{ - Username: cfg.GoCloak.UserName, + Username: &(cfg.GoCloak.UserName), }) FailIfErr(t, err, "GetUsers failed") t.Log(users) @@ -1779,20 +1750,21 @@ func TestGocloak_GetUserGroups(t *testing.T) { userID, groupID, ) - FailIfErr(t, err, "AddUserToGroup failed") + assert.NoError(t, err) groups, err := client.GetUserGroups( token.AccessToken, cfg.GoCloak.Realm, userID) - FailIfErr(t, err, "GetUserGroups failed") - FailIf( + assert.NoError(t, err) + assert.NotEqual( t, - len(groups) == 0, - "User is not in the Group") - AssertEquals( + len(groups), + 0, + ) + assert.Equal( t, groupID, - groups[0].ID) + *(groups[0].ID)) } func TestGocloak_DeleteUser(t *testing.T) { @@ -1816,7 +1788,7 @@ func TestGocloak_UpdateUser(t *testing.T) { cfg.GoCloak.Realm, userID) FailIfErr(t, err, "GetUserByID failed") - user.FirstName = GetRandomName("UpdateUserFirstName") + user.FirstName = GetRandomNameP("UpdateUserFirstName") err = client.UpdateUser( token.AccessToken, cfg.GoCloak.Realm, @@ -1824,6 +1796,35 @@ func TestGocloak_UpdateUser(t *testing.T) { FailIfErr(t, err, "UpdateUser failed") } +func TestGocloak_UpdateUserSetEmptyEmail(t *testing.T) { + t.Parallel() + cfg := GetConfig(t) + client := NewClientWithDebug(t) + token := GetAdminToken(t, client) + + tearDown, userID := CreateUser(t, client) + defer tearDown() + user, err := client.GetUserByID( + token.AccessToken, + cfg.GoCloak.Realm, + userID, + ) + assert.NoError(t, err) + user.Email = StringP("") + err = client.UpdateUser( + token.AccessToken, + cfg.GoCloak.Realm, + *user) + assert.NoError(t, err) + user, err = client.GetUserByID( + token.AccessToken, + cfg.GoCloak.Realm, + userID, + ) + assert.NoError(t, err) + assert.Nil(t, user.Email) +} + func TestGocloak_GetUsersByRoleName(t *testing.T) { t.Parallel() cfg := GetConfig(t) @@ -1840,7 +1841,7 @@ func TestGocloak_GetUsersByRoleName(t *testing.T) { token.AccessToken, cfg.GoCloak.Realm, roleName) - FailIfErr(t, err, "GetRealmRole failed") + assert.NoError(t, err) err = client.AddRealmRoleToUser( token.AccessToken, cfg.GoCloak.Realm, @@ -1848,22 +1849,24 @@ func TestGocloak_GetUsersByRoleName(t *testing.T) { []Role{ *role, }) - FailIfErr(t, err, "AddRealmRoleToUser failed") + assert.NoError(t, err) users, err := client.GetUsersByRoleName( token.AccessToken, cfg.GoCloak.Realm, roleName) - FailIfErr(t, err, "GetUsersByRoleName failed") + assert.NoError(t, err) - FailIf( + assert.NotEqual( t, - len(users) == 0, - "User is not in the Group") - AssertEquals( + len(users), + 0, + ) + assert.Equal( t, userID, - users[0].ID) + *(users[0].ID), + ) } func TestGocloak_GetUserSessions(t *testing.T) { @@ -1874,11 +1877,11 @@ func TestGocloak_GetUserSessions(t *testing.T) { _, err := client.GetToken( cfg.GoCloak.Realm, TokenOptions{ - ClientID: cfg.GoCloak.ClientID, - ClientSecret: cfg.GoCloak.ClientSecret, - Username: cfg.GoCloak.UserName, - Password: cfg.GoCloak.Password, - GrantType: "password", + ClientID: &(cfg.GoCloak.ClientID), + ClientSecret: &(cfg.GoCloak.ClientSecret), + Username: &(cfg.GoCloak.UserName), + Password: &(cfg.GoCloak.Password), + GrantType: StringP("password"), }, ) FailIfErr(t, err, "Login failed") @@ -1900,11 +1903,11 @@ func TestGocloak_GetUserOfflineSessionsForClient(t *testing.T) { _, err := client.GetToken( cfg.GoCloak.Realm, TokenOptions{ - ClientID: cfg.GoCloak.ClientID, - ClientSecret: cfg.GoCloak.ClientSecret, - Username: cfg.GoCloak.UserName, - Password: cfg.GoCloak.Password, - GrantType: "password", + ClientID: &(cfg.GoCloak.ClientID), + ClientSecret: &(cfg.GoCloak.ClientSecret), + Username: &(cfg.GoCloak.UserName), + Password: &(cfg.GoCloak.Password), + GrantType: StringP("password"), ResponseTypes: []string{"token", "id_token"}, Scopes: []string{"openid", "offline_access"}, }, @@ -1929,11 +1932,11 @@ func TestGocloak_GetClientUserSessions(t *testing.T) { _, err := client.GetToken( cfg.GoCloak.Realm, TokenOptions{ - ClientID: cfg.GoCloak.ClientID, - ClientSecret: cfg.GoCloak.ClientSecret, - Username: cfg.GoCloak.UserName, - Password: cfg.GoCloak.Password, - GrantType: "password", + ClientID: &(cfg.GoCloak.ClientID), + ClientSecret: &(cfg.GoCloak.ClientSecret), + Username: &(cfg.GoCloak.UserName), + Password: &(cfg.GoCloak.Password), + GrantType: StringP("password"), }, ) FailIfErr(t, err, "Login failed") @@ -1957,12 +1960,12 @@ func TestGocloak_CreateDeleteClientProtocolMapper(t *testing.T) { err := client.CreateClientProtocolMapper( token.AccessToken, cfg.GoCloak.Realm, - testClient.ID, + *(testClient.ID), ProtocolMapperRepresentation{ - ID: id, - Name: "test", - Protocol: "openid-connect", - ProtocolMapper: "oidc-usermodel-attribute-mapper", + ID: &id, + Name: StringP("test"), + Protocol: StringP("openid-connect"), + ProtocolMapper: StringP("oidc-usermodel-attribute-mapper"), Config: map[string]string{ "access.token.claim": "true", "aggregate.attrs": "", @@ -1981,7 +1984,7 @@ func TestGocloak_CreateDeleteClientProtocolMapper(t *testing.T) { err = client.DeleteClientProtocolMapper( token.AccessToken, cfg.GoCloak.Realm, - testClient.ID, + *(testClient.ID), id, ) FailIfErr(t, err, "DeleteClientProtocolMapper failed") @@ -1997,11 +2000,11 @@ func TestGocloak_GetClientOfflineSessions(t *testing.T) { _, err := client.GetToken( cfg.GoCloak.Realm, TokenOptions{ - ClientID: cfg.GoCloak.ClientID, - ClientSecret: cfg.GoCloak.ClientSecret, - Username: cfg.GoCloak.UserName, - Password: cfg.GoCloak.Password, - GrantType: "password", + ClientID: &(cfg.GoCloak.ClientID), + ClientSecret: &(cfg.GoCloak.ClientSecret), + Username: &(cfg.GoCloak.UserName), + Password: &(cfg.GoCloak.Password), + GrantType: StringP("password"), ResponseTypes: []string{"token", "id_token"}, Scopes: []string{"openid", "offline_access"}, }, @@ -2024,16 +2027,16 @@ func TestGoCloak_ClientSecret(t *testing.T) { token := GetAdminToken(t, client) testClient := Client{ - ID: GetRandomName("gocloak-client-secret-id-"), - ClientID: GetRandomName("gocloak-client-secret-client-id-"), - Secret: "initial-secret-key", - ServiceAccountsEnabled: true, - StandardFlowEnabled: true, - Enabled: true, - FullScopeAllowed: true, - Protocol: "openid-connect", + ID: GetRandomNameP("gocloak-client-secret-id-"), + ClientID: GetRandomNameP("gocloak-client-secret-client-id-"), + Secret: StringP("initial-secret-key"), + ServiceAccountsEnabled: BoolP(true), + StandardFlowEnabled: BoolP(true), + Enabled: BoolP(true), + FullScopeAllowed: BoolP(true), + Protocol: StringP("openid-connect"), RedirectURIs: []string{"localhost"}, - ClientAuthenticatorType: "client-secret", + ClientAuthenticatorType: StringP("client-secret"), } err := client.CreateClient( @@ -2041,25 +2044,25 @@ func TestGoCloak_ClientSecret(t *testing.T) { cfg.GoCloak.Realm, testClient, ) - FailIfErr(t, err, "CreateClient failed") + assert.NoError(t, err, "CreateClient failed") oldCreds, err := client.GetClientSecret( token.AccessToken, cfg.GoCloak.Realm, - testClient.ID, + *(testClient.ID), ) - FailIfErr(t, err, "GetClientSecret failed") + assert.NoError(t, err, "GetClientSecret failed") regeneratedCreds, err := client.RegenerateClientSecret( token.AccessToken, cfg.GoCloak.Realm, - testClient.ID, + *(testClient.ID), ) - FailIfErr(t, err, "RegenerateClientSecret failed") + assert.NoError(t, err, "RegenerateClientSecret failed") - AssertNotEquals(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, *(testClient.ID)) assert.NoError(t, err, "DeleteClient failed") } @@ -2070,11 +2073,12 @@ func TestGoCloak_ClientServiceAccount(t *testing.T) { token := GetAdminToken(t, client) serviceAccount, err := client.GetClientServiceAccount(token.AccessToken, cfg.GoCloak.Realm, gocloakClientID) - FailIfErr(t, err, "GetClientServiceAccount failed") + assert.NoError(t, err) - AssertNotEquals(t, "", serviceAccount.ID) - AssertNotEquals(t, gocloakClientID, serviceAccount.ID) - AssertEquals(t, "service-account-gocloak", serviceAccount.Username) + assert.NotNil(t, serviceAccount.ID) + assert.NotNil(t, serviceAccount.Username) + assert.NotEqual(t, gocloakClientID, *(serviceAccount.ID)) + assert.Equal(t, "service-account-gocloak", *(serviceAccount.Username)) } func TestGocloak_AddClientRoleToUser_DeleteClientRoleFromUser(t *testing.T) { @@ -2136,42 +2140,42 @@ func TestGocloak_CreateDeleteClientScopeWithMappers(t *testing.T) { token.AccessToken, cfg.GoCloak.Realm, ClientScope{ - ID: id, - Name: "test-scope", - Description: "testing scope", - Protocol: "openid-connect", + ID: &id, + Name: StringP("test-scope"), + Description: StringP("testing scope"), + Protocol: StringP("openid-connect"), ClientScopeAttributes: &ClientScopeAttributes{ - ConsentScreenText: "false", - DisplayOnConsentScreen: "true", - IncludeInTokenScope: "false", + ConsentScreenText: StringP("false"), + DisplayOnConsentScreen: StringP("true"), + IncludeInTokenScope: StringP("false"), }, - ProtocolMappers: []ProtocolMappers{ + ProtocolMappers: []*ProtocolMappers{ { - ID: rolemapperID, - Name: "roles", - Protocol: "openid-connect", - ProtocolMapper: "oidc-usermodel-client-role-mapper", - ConsentRequired: false, - ProtocolMappersConfig: ProtocolMappersConfig{ - UserinfoTokenClaim: "false", - AccessTokenClaim: "true", - IDTokenClaim: "true", - ClaimName: "test", - Multivalued: "true", - UsermodelClientRoleMappingClientID: "test", + ID: &rolemapperID, + Name: StringP("roles"), + Protocol: StringP("openid-connect"), + ProtocolMapper: StringP("oidc-usermodel-client-role-mapper"), + ConsentRequired: BoolP(false), + ProtocolMappersConfig: &ProtocolMappersConfig{ + UserinfoTokenClaim: StringP("false"), + AccessTokenClaim: StringP("true"), + IDTokenClaim: StringP("true"), + ClaimName: StringP("test"), + Multivalued: StringP("true"), + UsermodelClientRoleMappingClientID: StringP("test"), }, }, { - ID: audiencemapperID, - Name: "audience", - Protocol: "openid-connect", - ProtocolMapper: "oidc-audience-mapper", - ConsentRequired: false, - ProtocolMappersConfig: ProtocolMappersConfig{ - UserinfoTokenClaim: "false", - IDTokenClaim: "true", - AccessTokenClaim: "true", - IncludedClientAudience: "test", + ID: &audiencemapperID, + Name: StringP("audience"), + Protocol: StringP("openid-connect"), + ProtocolMapper: StringP("oidc-audience-mapper"), + ConsentRequired: BoolP(false), + ProtocolMappersConfig: &ProtocolMappersConfig{ + UserinfoTokenClaim: StringP("false"), + IDTokenClaim: StringP("true"), + AccessTokenClaim: StringP("true"), + IncludedClientAudience: StringP("test"), }, }, }, @@ -2179,6 +2183,7 @@ func TestGocloak_CreateDeleteClientScopeWithMappers(t *testing.T) { ) assert.NoError(t, err, "CreateClientScope failed") clientScopeActual, err := client.GetClientScope(token.AccessToken, cfg.GoCloak.Realm, id) + assert.NoError(t, err) assert.NotNil(t, clientScopeActual, "client scope has not been created") assert.Len(t, clientScopeActual.ProtocolMappers, 2, "unexpected number of protocol mappers created") @@ -2189,5 +2194,6 @@ func TestGocloak_CreateDeleteClientScopeWithMappers(t *testing.T) { ) assert.NoError(t, err, "DeleteClientScope failed") clientScopeActual, err = client.GetClientScope(token.AccessToken, cfg.GoCloak.Realm, id) + assert.EqualError(t, err, "404 Not Found: Could not find client scope") assert.Nil(t, clientScopeActual, "client scope has not been deleted") } diff --git a/gocloak.go b/gocloak.go index 6f166d5e..5d1e21bd 100644 --- a/gocloak.go +++ b/gocloak.go @@ -10,7 +10,7 @@ type GoCloak interface { // RestyClient returns a resty client that gocloak uses RestyClient() *resty.Client // Sets the resty Client that gocloak uses - SetRestyClient(restyClient resty.Client) + SetRestyClient(restyClient *resty.Client) // GetToken returns a token GetToken(realm string, options TokenOptions) (*JWT, error) @@ -161,7 +161,7 @@ type GoCloak interface { // CreateClientRole creates a new role for a client CreateClientRole(accessToken string, realm string, clientID string, role Role) error // DeleteClientRole deletes the given role - DeleteClientRole(accessToken string, realm, clientID, roleName string) error + DeleteClientRole(accessToken, realm, clientID, roleName string) error // DeleteClientRoleFromUser removes a client role from from the user DeleteClientRoleFromUser(token string, realm string, clientID string, userID string, roles []Role) error // GetClientRoles gets roles for the given client diff --git a/model_test.go b/model_test.go index 3c955ae0..b788c63a 100644 --- a/model_test.go +++ b/model_test.go @@ -36,3 +36,52 @@ func TestStringOrArray_Marshal(t *testing.T) { assert.NoError(t, err, "Marshalling failed for array of strings: %s", dataString) assert.Equal(t, "[\"1\",\"2\",\"3\"]", string(jsonArray)) } + +func TestGetQueryParams(t *testing.T) { + t.Parallel() + + type TestParams struct { + IntField *int `json:"int_field,string,omitempty"` + StringField *string `json:"string_field,omitempty"` + BoolField *bool `json:"bool_field,string,omitempty"` + } + + params, err := GetQueryParams(TestParams{}) + assert.NoError(t, err) + assert.True( + t, + len(params) == 0, + "Params must be empty, but got: %+v", + params, + ) + + params, err = GetQueryParams(TestParams{ + IntField: IntP(1), + StringField: StringP("fake"), + BoolField: BoolP(true), + }) + assert.NoError(t, err) + assert.Equal( + t, + map[string]string{ + "int_field": "1", + "string_field": "fake", + "bool_field": "true", + }, + params, + ) + + params, err = GetQueryParams(TestParams{ + StringField: StringP("fake"), + BoolField: BoolP(false), + }) + assert.NoError(t, err) + assert.Equal( + t, + map[string]string{ + "string_field": "fake", + "bool_field": "false", + }, + params, + ) +} diff --git a/models.go b/models.go index b4aff9d5..b135c1c6 100644 --- a/models.go +++ b/models.go @@ -47,11 +47,11 @@ func (s *StringOrArray) UnmarshalJSON(data []byte) error { } // MarshalJSON converts the array of strings to a JSON array or JSON string if there is only one item in the array -func (s StringOrArray) MarshalJSON() ([]byte, error) { - if len(s) == 1 { - return json.Marshal([]string(s)[0]) +func (s *StringOrArray) MarshalJSON() ([]byte, error) { + if len(*s) == 1 { + return json.Marshal([]string(*s)[0]) } - return json.Marshal([]string(s)) + return json.Marshal([]string(*s)) } // APIError represents an api error @@ -67,79 +67,79 @@ func (apiError APIError) Error() string { // CertResponseKey is returned by the certs endpoint type CertResponseKey struct { - Kid string `json:"kid,omitempty"` - Kty string `json:"kty,omitempty"` - Alg string `json:"alg,omitempty"` - Use string `json:"use,omitempty"` - N string `json:"n,omitempty"` - E string `json:"e,omitempty"` + Kid *string `json:"kid,omitempty"` + Kty *string `json:"kty,omitempty"` + Alg *string `json:"alg,omitempty"` + Use *string `json:"use,omitempty"` + N *string `json:"n,omitempty"` + E *string `json:"e,omitempty"` } // CertResponse is returned by the certs endpoint type CertResponse struct { - Keys []CertResponseKey `json:"keys,omitempty"` + Keys []*CertResponseKey `json:"keys,omitempty"` } // IssuerResponse is returned by the issuer endpoint type IssuerResponse struct { - Realm string `json:"realm,omitempty"` - PublicKey string `json:"public_key,omitempty"` - TokenService string `json:"token-service,omitempty"` - AccountService string `json:"account-service,omitempty"` - TokensNotBefore int `json:"tokens-not-before,omitempty"` + Realm *string `json:"realm,omitempty"` + PublicKey *string `json:"public_key,omitempty"` + TokenService *string `json:"token-service,omitempty"` + AccountService *string `json:"account-service,omitempty"` + TokensNotBefore *int `json:"tokens-not-before,omitempty"` } // RetrospecTokenResult is returned when a token was checked type RetrospecTokenResult struct { Permissions map[string]string `json:"permissions,omitempty"` - Exp int `json:"exp,omitempty"` - Nbf int `json:"nbf,omitempty"` - Iat int `json:"iat,omitempty"` - Aud StringOrArray `json:"aud,omitempty"` - Active bool `json:"active"` - AuthTime int `json:"auth_time,omitempty"` - Jti string `json:"jti,omitempty"` - Type string `json:"typ,omitempty"` + Exp *int `json:"exp,omitempty"` + Nbf *int `json:"nbf,omitempty"` + Iat *int `json:"iat,omitempty"` + Aud *StringOrArray `json:"aud,omitempty"` + Active *bool `json:"active"` + AuthTime *int `json:"auth_time,omitempty"` + Jti *string `json:"jti,omitempty"` + Type *string `json:"typ,omitempty"` } // User represents the Keycloak User Structure type User struct { - ID string `json:"id,omitempty"` - CreatedTimestamp int64 `json:"createdTimestamp,omitempty"` - Username string `json:"username,omitempty"` - Enabled bool `json:"enabled"` - Totp bool `json:"totp"` - EmailVerified bool `json:"emailVerified"` - FirstName string `json:"firstName,omitempty"` - LastName string `json:"lastName,omitempty"` - Email string `json:"email,omitempty"` - FederationLink string `json:"federationLink,omitempty"` + ID *string `json:"id,omitempty"` + CreatedTimestamp *int64 `json:"createdTimestamp,omitempty"` + Username *string `json:"username,omitempty"` + Enabled *bool `json:"enabled"` + Totp *bool `json:"totp"` + EmailVerified *bool `json:"emailVerified"` + FirstName *string `json:"firstName,omitempty"` + LastName *string `json:"lastName,omitempty"` + Email *string `json:"email,omitempty"` + FederationLink *string `json:"federationLink,omitempty"` Attributes map[string][]string `json:"attributes,omitempty"` DisableableCredentialTypes []interface{} `json:"disableableCredentialTypes,omitempty"` RequiredActions []string `json:"requiredActions,omitempty"` Access map[string]bool `json:"access"` ClientRoles map[string][]string `json:"clientRoles,omitempty"` RealmRoles []string `json:"realmRoles,omitempty"` - ServiceAccountClientID string `json:"serviceAccountClientId,omitempty"` + ServiceAccountClientID *string `json:"serviceAccountClientId,omitempty"` Credentials []*CredentialRepresentation `json:"credentials,omitempty"` } // SetPasswordRequest sets a new password type SetPasswordRequest struct { - Type string `json:"type,omitempty"` - Temporary bool `json:"temporary"` - Password string `json:"value,omitempty"` + Type *string `json:"type,omitempty"` + Temporary *bool `json:"temporary"` + Password *string `json:"value,omitempty"` } // Component is a component type Component struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - ProviderID string `json:"providerId,omitempty"` - ProviderType string `json:"providerType,omitempty"` - ParentID string `json:"parentId,omitempty"` - ComponentConfig ComponentConfig `json:"config,omitempty"` - SubType string `json:"subType,omitempty"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + ProviderID *string `json:"providerId,omitempty"` + ProviderType *string `json:"providerType,omitempty"` + ParentID *string `json:"parentId,omitempty"` + ComponentConfig *ComponentConfig `json:"config,omitempty"` + SubType *string `json:"subType,omitempty"` } // ComponentConfig is a componentconfig @@ -150,27 +150,27 @@ type ComponentConfig struct { // KeyStoreConfig holds the keyStoreConfig type KeyStoreConfig struct { - ActiveKeys ActiveKeys `json:"active,omitempty"` - Key []Key `json:"keys,omitempty"` + ActiveKeys *ActiveKeys `json:"active,omitempty"` + Key []*Key `json:"keys,omitempty"` } // ActiveKeys holds the active keys type ActiveKeys struct { - HS256 string `json:"HS256,omitempty"` - RS256 string `json:"RS256,omitempty"` - AES string `json:"AES,omitempty"` + HS256 *string `json:"HS256,omitempty"` + RS256 *string `json:"RS256,omitempty"` + AES *string `json:"AES,omitempty"` } // Key is a key type Key struct { - ProviderID string `json:"providerId,omitempty"` - ProviderPriority int `json:"providerPriority,omitempty"` - Kid string `json:"kid,omitempty"` - Status string `json:"status,omitempty"` - Type string `json:"type,omitempty"` - Algorithm string `json:"algorithm,omitempty"` - PublicKey string `json:"publicKey,omitempty"` - Certificate string `json:"certificate,omitempty"` + ProviderID *string `json:"providerId,omitempty"` + ProviderPriority *int `json:"providerPriority,omitempty"` + Kid *string `json:"kid,omitempty"` + Status *string `json:"status,omitempty"` + Type *string `json:"type,omitempty"` + Algorithm *string `json:"algorithm,omitempty"` + PublicKey *string `json:"publicKey,omitempty"` + Certificate *string `json:"certificate,omitempty"` } // Attributes holds Attributes @@ -181,46 +181,46 @@ type Attributes struct { // Access represents access type Access struct { - ManageGroupMembership bool `json:"manageGroupMembership"` - View bool `json:"view"` - MapRoles bool `json:"mapRoles"` - Impersonate bool `json:"impersonate"` - Manage bool `json:"manage"` + ManageGroupMembership *bool `json:"manageGroupMembership"` + View *bool `json:"view"` + MapRoles *bool `json:"mapRoles"` + Impersonate *bool `json:"impersonate"` + Manage *bool `json:"manage"` } // UserGroup is a UserGroup type UserGroup struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Path string `json:"path,omitempty"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Path *string `json:"path,omitempty"` } // GetUsersParams represents the optional parameters for getting users type GetUsersParams struct { - BriefRepresentation bool `json:"briefRepresentation,string"` - Email string `json:"email,omitempty"` - First int `json:"first,string,omitempty"` - FirstName string `json:"firstName,omitempty"` - LastName string `json:"lastName,omitempty"` - Max int `json:"max,string,omitempty"` - Search string `json:"search,omitempty"` - Username string `json:"username,omitempty"` + BriefRepresentation *bool `json:"briefRepresentation,string"` + Email *string `json:"email,omitempty"` + First *int `json:"first,string,omitempty"` + FirstName *string `json:"firstName,omitempty"` + LastName *string `json:"lastName,omitempty"` + Max *int `json:"max,string,omitempty"` + Search *string `json:"search,omitempty"` + Username *string `json:"username,omitempty"` } // ExecuteActionsEmail represents parameters for executing action emails type ExecuteActionsEmail struct { - UserID string `json:"-"` - ClientID string `json:"client_id,omitempty"` - Lifespan int `json:"lifespan,string,omitempty"` - RedirectURI string `json:"redirect_uri,omitempty"` + UserID *string `json:"-"` + ClientID *string `json:"client_id,omitempty"` + Lifespan *int `json:"lifespan,string,omitempty"` + RedirectURI *string `json:"redirect_uri,omitempty"` Actions []string `json:"-"` } // Group is a Group type Group struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Path string `json:"path,omitempty"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Path *string `json:"path,omitempty"` SubGroups []*Group `json:"subGroups,omitempty"` Attributes map[string][]string `json:"attributes,emitempty"` Access map[string]bool `json:"access,omitempty"` @@ -230,129 +230,129 @@ type Group struct { // GetGroupsParams represents the optional parameters for getting groups type GetGroupsParams struct { - First int `json:"first,string,omitempty"` - Max int `json:"max,string,omitempty"` - Search string `json:"search,omitempty"` - Full bool `json:"full,string,omitempty"` + First *int `json:"first,string,omitempty"` + Max *int `json:"max,string,omitempty"` + Search *string `json:"search,omitempty"` + Full *bool `json:"full,string,omitempty"` } // Role is a role type Role struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - ScopeParamRequired bool `json:"scopeParamRequired"` - Composite bool `json:"composite"` - ClientRole bool `json:"clientRole"` - ContainerID string `json:"containerId,omitempty"` - Description string `json:"description,omitempty"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + ScopeParamRequired *bool `json:"scopeParamRequired"` + Composite *bool `json:"composite"` + ClientRole *bool `json:"clientRole"` + ContainerID *string `json:"containerId,omitempty"` + Description *string `json:"description,omitempty"` Attributes map[string][]string `json:"attributes,omitempty"` } // ClientMappingsRepresentation is a client role mappings type ClientMappingsRepresentation struct { - ID string `json:"id,omitempty"` - Client string `json:"client,omitempty"` - Mappings []Role `json:"mappings,omitempty"` + ID *string `json:"id,omitempty"` + Client *string `json:"client,omitempty"` + Mappings []*Role `json:"mappings,omitempty"` } // MappingsRepresentation is a representation of role mappings type MappingsRepresentation struct { - ClientMappings map[string]ClientMappingsRepresentation `json:"clientMappings,omitempty"` - RealmMappings []Role `json:"realmMappings,omitempty"` + ClientMappings map[string]*ClientMappingsRepresentation `json:"clientMappings,omitempty"` + RealmMappings []*Role `json:"realmMappings,omitempty"` } // ClientScope is a ClientScope type ClientScope struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Protocol string `json:"protocol,omitempty"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Protocol *string `json:"protocol,omitempty"` ClientScopeAttributes *ClientScopeAttributes `json:"attributes,omitempty"` - ProtocolMappers []ProtocolMappers `json:"protocolMappers,omitempty"` + ProtocolMappers []*ProtocolMappers `json:"protocolMappers,omitempty"` } // ClientScopeAttributes are attributes of client scopes type ClientScopeAttributes struct { - ConsentScreenText string `json:"consent.screen.text,omitempty"` - DisplayOnConsentScreen string `json:"display.on.consent.screen,omitempty"` - IncludeInTokenScope string `json:"include.in.token.scope,omitempty"` + ConsentScreenText *string `json:"consent.screen.text,omitempty"` + DisplayOnConsentScreen *string `json:"display.on.consent.screen,omitempty"` + IncludeInTokenScope *string `json:"include.in.token.scope,omitempty"` } // ProtocolMappers are protocolmappers type ProtocolMappers struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Protocol string `json:"protocol,omitempty"` - ProtocolMapper string `json:"protocolMapper,omitempty"` - ConsentRequired bool `json:"consentRequired"` - ProtocolMappersConfig ProtocolMappersConfig `json:"config,omitempty"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Protocol *string `json:"protocol,omitempty"` + ProtocolMapper *string `json:"protocolMapper,omitempty"` + ConsentRequired *bool `json:"consentRequired"` + ProtocolMappersConfig *ProtocolMappersConfig `json:"config,omitempty"` } // ProtocolMappersConfig is a config of a protocol mapper type ProtocolMappersConfig struct { - UserinfoTokenClaim string `json:"userinfo.token.claim,omitempty"` - UserAttribute string `json:"user.attribute,omitempty"` - IDTokenClaim string `json:"id.token.claim,omitempty"` - AccessTokenClaim string `json:"access.token.claim,omitempty"` - ClaimName string `json:"claim.name,omitempty"` - ClaimValue string `json:"claim.value,omitempty"` - JSONTypeLabel string `json:"jsonType.label,omitempty"` - Multivalued string `json:"multivalued,omitempty"` - UsermodelClientRoleMappingClientID string `json:"usermodel.clientRoleMapping.clientId,omitempty"` - IncludedClientAudience string `json:"included.client.audience,omitempty"` + UserinfoTokenClaim *string `json:"userinfo.token.claim,omitempty"` + UserAttribute *string `json:"user.attribute,omitempty"` + IDTokenClaim *string `json:"id.token.claim,omitempty"` + AccessTokenClaim *string `json:"access.token.claim,omitempty"` + ClaimName *string `json:"claim.name,omitempty"` + ClaimValue *string `json:"claim.value,omitempty"` + JSONTypeLabel *string `json:"jsonType.label,omitempty"` + Multivalued *string `json:"multivalued,omitempty"` + UsermodelClientRoleMappingClientID *string `json:"usermodel.clientRoleMapping.clientId,omitempty"` + IncludedClientAudience *string `json:"included.client.audience,omitempty"` } // Client is a ClientRepresentation type Client struct { - Access map[string]interface{} `json:"access,omitempty"` - AdminURL string `json:"adminUrl,omitempty"` - Attributes map[string]string `json:"attributes,omitempty"` - AuthenticationFlowBindingOverrides map[string]string `json:"authenticationFlowBindingOverrides,omitempty"` - AuthorizationServicesEnabled bool `json:"authorizationServicesEnabled"` - AuthorizationSettings *ResourceServerRepresentation `json:"authorizationSettings,omitempty"` - BaseURL string `json:"baseUrl,omitempty"` - BearerOnly bool `json:"bearerOnly"` - ClientAuthenticatorType string `json:"clientAuthenticatorType,omitempty"` - ClientID string `json:"clientId,omitempty"` - ConsentRequired bool `json:"consentRequired"` - DefaultClientScopes []string `json:"defaultClientScopes,omitempty"` - DefaultRoles []string `json:"defaultRoles,omitempty"` - Description string `json:"description,omitempty"` - DirectAccessGrantsEnabled bool `json:"directAccessGrantsEnabled"` - Enabled bool `json:"enabled"` - FrontChannelLogout bool `json:"frontchannelLogout"` - FullScopeAllowed bool `json:"fullScopeAllowed"` - ID string `json:"id,omitempty"` - ImplicitFlowEnabled bool `json:"implicitFlowEnabled"` - Name string `json:"name,omitempty"` - NodeReRegistrationTimeout int32 `json:"nodeReRegistrationTimeout,omitempty"` - NotBefore int32 `json:"notBefore,omitempty"` - OptionalClientScopes []string `json:"optionalClientScopes,omitempty"` - Origin string `json:"origin,omitempty"` - Protocol string `json:"protocol,omitempty"` - ProtocolMappers []ProtocolMapperRepresentation `json:"protocolMappers,omitempty"` - PublicClient bool `json:"publicClient"` - RedirectURIs []string `json:"redirectUris,omitempty"` - RegisteredNodes map[string]string `json:"registeredNodes,omitempty"` - RegistrationAccessToken string `json:"registrationAccessToken,omitempty"` - RootURL string `json:"rootUrl,omitempty"` - Secret string `json:"secret,omitempty"` - ServiceAccountsEnabled bool `json:"serviceAccountsEnabled"` - StandardFlowEnabled bool `json:"standardFlowEnabled"` - SurrogateAuthRequired bool `json:"surrogateAuthRequired"` - WebOrigins []string `json:"webOrigins,omitempty"` + Access map[string]interface{} `json:"access,omitempty"` + AdminURL *string `json:"adminUrl,omitempty"` + Attributes map[string]string `json:"attributes,omitempty"` + AuthenticationFlowBindingOverrides map[string]string `json:"authenticationFlowBindingOverrides,omitempty"` + AuthorizationServicesEnabled *bool `json:"authorizationServicesEnabled"` + AuthorizationSettings *ResourceServerRepresentation `json:"authorizationSettings,omitempty"` + BaseURL *string `json:"baseUrl,omitempty"` + BearerOnly *bool `json:"bearerOnly"` + ClientAuthenticatorType *string `json:"clientAuthenticatorType,omitempty"` + ClientID *string `json:"clientId,omitempty"` + ConsentRequired *bool `json:"consentRequired"` + DefaultClientScopes []string `json:"defaultClientScopes,omitempty"` + DefaultRoles []string `json:"defaultRoles,omitempty"` + Description *string `json:"description,omitempty"` + DirectAccessGrantsEnabled *bool `json:"directAccessGrantsEnabled"` + Enabled *bool `json:"enabled"` + FrontChannelLogout *bool `json:"frontchannelLogout"` + FullScopeAllowed *bool `json:"fullScopeAllowed"` + ID *string `json:"id,omitempty"` + ImplicitFlowEnabled *bool `json:"implicitFlowEnabled"` + Name *string `json:"name,omitempty"` + NodeReRegistrationTimeout *int32 `json:"nodeReRegistrationTimeout,omitempty"` + NotBefore *int32 `json:"notBefore,omitempty"` + OptionalClientScopes []string `json:"optionalClientScopes,omitempty"` + Origin *string `json:"origin,omitempty"` + Protocol *string `json:"protocol,omitempty"` + ProtocolMappers []*ProtocolMapperRepresentation `json:"protocolMappers,omitempty"` + PublicClient *bool `json:"publicClient"` + RedirectURIs []string `json:"redirectUris,omitempty"` + RegisteredNodes map[string]string `json:"registeredNodes,omitempty"` + RegistrationAccessToken *string `json:"registrationAccessToken,omitempty"` + RootURL *string `json:"rootUrl,omitempty"` + Secret *string `json:"secret,omitempty"` + ServiceAccountsEnabled *bool `json:"serviceAccountsEnabled"` + StandardFlowEnabled *bool `json:"standardFlowEnabled"` + SurrogateAuthRequired *bool `json:"surrogateAuthRequired"` + WebOrigins []string `json:"webOrigins,omitempty"` } // ResourceServerRepresentation represents the resources of a Server type ResourceServerRepresentation struct { - AllowRemoteResourceManagement bool `json:"allowRemoteResourceManagement"` - ClientID string `json:"clientId,omitempty"` - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Policies []PolicyRepresentation `json:"policies,omitempty"` - PolicyEnforcementMode *PolicyEnforcementMode `json:"policyEnforcementMode,omitempty"` - Resources []ResourceRepresentation `json:"resources,omitempty"` - Scopes []ScopeRepresentation `json:"scopes,omitempty"` + AllowRemoteResourceManagement *bool `json:"allowRemoteResourceManagement"` + ClientID *string `json:"clientId,omitempty"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Policies []*PolicyRepresentation `json:"policies,omitempty"` + PolicyEnforcementMode *PolicyEnforcementMode `json:"policyEnforcementMode,omitempty"` + Resources []*ResourceRepresentation `json:"resources,omitempty"` + Scopes []*ScopeRepresentation `json:"scopes,omitempty"` } // PolicyEnforcementMode is an enum type for PolicyEnforcementMode of ResourceServerRepresentation @@ -369,15 +369,15 @@ const ( type PolicyRepresentation struct { Config map[string]string `json:"config,omitempty"` DecisionStrategy *DecisionStrategy `json:"decisionStrategy,omitempty"` - Description string `json:"description,omitempty"` - ID string `json:"id,omitempty"` + Description *string `json:"description,omitempty"` + ID *string `json:"id,omitempty"` Logic *Logic `json:"logic,omitempty"` - Name string `json:"name,omitempty"` - Owner string `json:"owner,omitempty"` + Name *string `json:"name,omitempty"` + Owner *string `json:"owner,omitempty"` Policies []string `json:"policies,omitempty"` Resources []string `json:"resources,omitempty"` Scopes []string `json:"scopes,omitempty"` - Type string `json:"type,omitempty"` + Type *string `json:"type,omitempty"` } // DecisionStrategy is an enum type for DecisionStrategy of PolicyRepresentation @@ -401,197 +401,197 @@ const ( // ResourceRepresentation is a representation of a Resource type ResourceRepresentation struct { - ID string `json:"id,omitempty"` //TODO: is marked "_optional" in template, input error or deliberate? - Attributes map[string]string `json:"attributes,omitempty"` - DisplayName string `json:"displayName,omitempty"` - IconURI string `json:"icon_uri,omitempty"` //TODO: With "_" because that's how it's written down in the template - Name string `json:"name,omitempty"` - OwnerManagedAccess bool `json:"ownerManagedAccess"` - Scopes []ScopeRepresentation `json:"scopes,omitempty"` - Type string `json:"type,omitempty"` - URIs []string `json:"uris,omitempty"` + ID *string `json:"id,omitempty"` //TODO: is marked "_optional" in template, input error or deliberate? + Attributes map[string]string `json:"attributes,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + IconURI *string `json:"icon_uri,omitempty"` //TODO: With "_" because that's how it's written down in the template + Name *string `json:"name,omitempty"` + OwnerManagedAccess *bool `json:"ownerManagedAccess"` + Scopes []*ScopeRepresentation `json:"scopes,omitempty"` + Type *string `json:"type,omitempty"` + URIs []string `json:"uris,omitempty"` } // ScopeRepresentation is a represents a Scope type ScopeRepresentation struct { - DisplayName string `json:"displayName,omitempty"` - IconURI string `json:"iconUri,omitempty"` - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Policies []PolicyRepresentation `json:"policies,omitempty"` - Resources []ResourceRepresentation `json:"resources,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + IconURI *string `json:"iconUri,omitempty"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Policies []*PolicyRepresentation `json:"policies,omitempty"` + Resources []*ResourceRepresentation `json:"resources,omitempty"` } // ProtocolMapperRepresentation represents.... type ProtocolMapperRepresentation struct { Config map[string]string `json:"config,omitempty"` - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Protocol string `json:"protocol,omitempty"` - ProtocolMapper string `json:"protocolMapper,omitempty"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Protocol *string `json:"protocol,omitempty"` + ProtocolMapper *string `json:"protocolMapper,omitempty"` } // GetClientsParams represents the query parameters type GetClientsParams struct { - ClientID string `json:"clientId,omitempty"` - ViewableOnly bool `json:"viewableOnly,string"` + ClientID *string `json:"clientId,omitempty"` + ViewableOnly *bool `json:"viewableOnly,string"` } // UserInfo is returned by the userinfo endpoint type UserInfo struct { - Sub string `json:"sub,omitempty"` - EmailVerified bool `json:"email_verified"` + Sub *string `json:"sub,omitempty"` + EmailVerified *bool `json:"email_verified"` Address interface{} `json:"address,omitempty"` - PreferredUsername string `json:"preferred_username,omitempty"` - Email string `json:"email,omitempty"` + PreferredUsername *string `json:"preferred_username,omitempty"` + Email *string `json:"email,omitempty"` } // RealmRepresentation represent a realm type RealmRepresentation struct { - AccessCodeLifespan int `json:"accessCodeLifespan,omitempty"` - AccessCodeLifespanLogin int `json:"accessCodeLifespanLogin,omitempty"` - AccessCodeLifespanUserAction int `json:"accessCodeLifespanUserAction,omitempty"` - AccessTokenLifespan int `json:"accessTokenLifespan,omitempty"` - AccessTokenLifespanForImplicitFlow int `json:"accessTokenLifespanForImplicitFlow,omitempty"` - AccountTheme string `json:"accountTheme,omitempty"` - ActionTokenGeneratedByAdminLifespan int `json:"actionTokenGeneratedByAdminLifespan,omitempty"` - ActionTokenGeneratedByUserLifespan int `json:"actionTokenGeneratedByUserLifespan,omitempty"` - AdminEventsDetailsEnabled bool `json:"adminEventsDetailsEnabled"` - AdminEventsEnabled bool `json:"adminEventsEnabled"` - AdminTheme string `json:"adminTheme,omitempty"` + AccessCodeLifespan *int `json:"accessCodeLifespan,omitempty"` + AccessCodeLifespanLogin *int `json:"accessCodeLifespanLogin,omitempty"` + AccessCodeLifespanUserAction *int `json:"accessCodeLifespanUserAction,omitempty"` + AccessTokenLifespan *int `json:"accessTokenLifespan,omitempty"` + AccessTokenLifespanForImplicitFlow *int `json:"accessTokenLifespanForImplicitFlow,omitempty"` + AccountTheme *string `json:"accountTheme,omitempty"` + ActionTokenGeneratedByAdminLifespan *int `json:"actionTokenGeneratedByAdminLifespan,omitempty"` + ActionTokenGeneratedByUserLifespan *int `json:"actionTokenGeneratedByUserLifespan,omitempty"` + AdminEventsDetailsEnabled *bool `json:"adminEventsDetailsEnabled"` + AdminEventsEnabled *bool `json:"adminEventsEnabled"` + AdminTheme *string `json:"adminTheme,omitempty"` Attributes map[string]string `json:"attributes,omitempty"` AuthenticationFlows []interface{} `json:"authenticationFlows,omitempty"` AuthenticatorConfig []interface{} `json:"authenticatorConfig,omitempty"` - BrowserFlow string `json:"browserFlow,omitempty"` + BrowserFlow *string `json:"browserFlow,omitempty"` BrowserSecurityHeaders map[string]string `json:"browserSecurityHeaders,omitempty"` - BruteForceProtected bool `json:"bruteForceProtected"` - ClientAuthenticationFlow string `json:"clientAuthenticationFlow,omitempty"` + BruteForceProtected *bool `json:"bruteForceProtected"` + ClientAuthenticationFlow *string `json:"clientAuthenticationFlow,omitempty"` ClientScopeMappings map[string]string `json:"clientScopeMappings,omitempty"` - ClientScopes []ClientScope `json:"clientScopes,omitempty"` - Clients []Client `json:"clients,omitempty"` + ClientScopes []*ClientScope `json:"clientScopes,omitempty"` + Clients []*Client `json:"clients,omitempty"` Components interface{} `json:"components,omitempty"` DefaultDefaultClientScopes []string `json:"defaultDefaultClientScopes,omitempty"` DefaultGroups []string `json:"defaultGroups,omitempty"` - DefaultLocale string `json:"defaultLocale,omitempty"` + DefaultLocale *string `json:"defaultLocale,omitempty"` DefaultOptionalClientScopes []string `json:"defaultOptionalClientScopes,omitempty"` DefaultRoles []string `json:"defaultRoles,omitempty"` - DefaultSignatureAlgorithm string `json:"defaultSignatureAlgorithm,omitempty"` - DirectGrantFlow string `json:"directGrantFlow,omitempty"` - DisplayName string `json:"displayName,omitempty"` - DisplayNameHTML string `json:"displayNameHtml,omitempty"` - DockerAuthenticationFlow string `json:"dockerAuthenticationFlow,omitempty"` - DuplicateEmailsAllowed bool `json:"duplicateEmailsAllowed"` - EditUsernameAllowed bool `json:"editUsernameAllowed"` - EmailTheme string `json:"emailTheme,omitempty"` - Enabled bool `json:"enabled"` + DefaultSignatureAlgorithm *string `json:"defaultSignatureAlgorithm,omitempty"` + DirectGrantFlow *string `json:"directGrantFlow,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + DisplayNameHTML *string `json:"displayNameHtml,omitempty"` + DockerAuthenticationFlow *string `json:"dockerAuthenticationFlow,omitempty"` + DuplicateEmailsAllowed *bool `json:"duplicateEmailsAllowed"` + EditUsernameAllowed *bool `json:"editUsernameAllowed"` + EmailTheme *string `json:"emailTheme,omitempty"` + Enabled *bool `json:"enabled"` EnabledEventTypes []string `json:"enabledEventTypes,omitempty"` - EventsEnabled bool `json:"eventsEnabled"` - EventsExpiration int64 `json:"eventsExpiration,omitempty"` + EventsEnabled *bool `json:"eventsEnabled"` + EventsExpiration *int64 `json:"eventsExpiration,omitempty"` EventsListeners []string `json:"eventsListeners,omitempty"` - FailureFactor int `json:"failureFactor,omitempty"` + FailureFactor *int `json:"failureFactor,omitempty"` FederatedUsers []interface{} `json:"federatedUsers,omitempty"` Groups []interface{} `json:"groups,omitempty"` - ID string `json:"id,omitempty"` + ID *string `json:"id,omitempty"` IdentityProviderMappers []interface{} `json:"identityProviderMappers,omitempty"` IdentityProviders []interface{} `json:"identityProviders,omitempty"` - InternationalizationEnabled bool `json:"internationalizationEnabled"` - KeycloakVersion string `json:"keycloakVersion,omitempty"` - LoginTheme string `json:"loginTheme,omitempty"` - LoginWithEmailAllowed bool `json:"loginWithEmailAllowed"` - MaxDeltaTimeSeconds int `json:"maxDeltaTimeSeconds,omitempty"` - MaxFailureWaitSeconds int `json:"maxFailureWaitSeconds,omitempty"` - MinimumQuickLoginWaitSeconds int `json:"minimumQuickLoginWaitSeconds,omitempty"` - NotBefore int `json:"notBefore,omitempty"` - OfflineSessionIdleTimeout int `json:"offlineSessionIdleTimeout,omitempty"` - OfflineSessionMaxLifespan int `json:"offlineSessionMaxLifespan,omitempty"` - OfflineSessionMaxLifespanEnabled bool `json:"offlineSessionMaxLifespanEnabled"` - OtpPolicyAlgorithm string `json:"otpPolicyAlgorithm,omitempty"` - OtpPolicyDigits int `json:"otpPolicyDigits,omitempty"` - OtpPolicyInitialCounter int `json:"otpPolicyInitialCounter,omitempty"` - OtpPolicyLookAheadWindow int `json:"otpPolicyLookAheadWindow,omitempty"` - OtpPolicyPeriod int `json:"otpPolicyPeriod,omitempty"` - OtpPolicyType string `json:"otpPolicyType,omitempty"` + InternationalizationEnabled *bool `json:"internationalizationEnabled"` + KeycloakVersion *string `json:"keycloakVersion,omitempty"` + LoginTheme *string `json:"loginTheme,omitempty"` + LoginWithEmailAllowed *bool `json:"loginWithEmailAllowed"` + MaxDeltaTimeSeconds *int `json:"maxDeltaTimeSeconds,omitempty"` + MaxFailureWaitSeconds *int `json:"maxFailureWaitSeconds,omitempty"` + MinimumQuickLoginWaitSeconds *int `json:"minimumQuickLoginWaitSeconds,omitempty"` + NotBefore *int `json:"notBefore,omitempty"` + OfflineSessionIdleTimeout *int `json:"offlineSessionIdleTimeout,omitempty"` + OfflineSessionMaxLifespan *int `json:"offlineSessionMaxLifespan,omitempty"` + OfflineSessionMaxLifespanEnabled *bool `json:"offlineSessionMaxLifespanEnabled"` + OtpPolicyAlgorithm *string `json:"otpPolicyAlgorithm,omitempty"` + OtpPolicyDigits *int `json:"otpPolicyDigits,omitempty"` + OtpPolicyInitialCounter *int `json:"otpPolicyInitialCounter,omitempty"` + OtpPolicyLookAheadWindow *int `json:"otpPolicyLookAheadWindow,omitempty"` + OtpPolicyPeriod *int `json:"otpPolicyPeriod,omitempty"` + OtpPolicyType *string `json:"otpPolicyType,omitempty"` OtpSupportedApplications []string `json:"otpSupportedApplications,omitempty"` - PasswordPolicy string `json:"passwordPolicy,omitempty"` - PermanentLockout bool `json:"permanentLockout"` + PasswordPolicy *string `json:"passwordPolicy,omitempty"` + PermanentLockout *bool `json:"permanentLockout"` ProtocolMappers []interface{} `json:"protocolMappers,omitempty"` - QuickLoginCheckMilliSeconds int64 `json:"quickLoginCheckMilliSeconds,omitempty"` - Realm string `json:"realm,omitempty"` - RefreshTokenMaxReuse int `json:"refreshTokenMaxReuse,omitempty"` - RegistrationAllowed bool `json:"registrationAllowed"` - RegistrationEmailAsUsername bool `json:"registrationEmailAsUsername"` - RegistrationFlow string `json:"registrationFlow,omitempty"` - RememberMe bool `json:"rememberMe"` + QuickLoginCheckMilliSeconds *int64 `json:"quickLoginCheckMilliSeconds,omitempty"` + Realm *string `json:"realm,omitempty"` + RefreshTokenMaxReuse *int `json:"refreshTokenMaxReuse,omitempty"` + RegistrationAllowed *bool `json:"registrationAllowed"` + RegistrationEmailAsUsername *bool `json:"registrationEmailAsUsername"` + RegistrationFlow *string `json:"registrationFlow,omitempty"` + RememberMe *bool `json:"rememberMe"` RequiredActions []interface{} `json:"requiredActions,omitempty"` - ResetCredentialsFlow string `json:"resetCredentialsFlow,omitempty"` - ResetPasswordAllowed bool `json:"resetPasswordAllowed"` - RevokeRefreshToken bool `json:"revokeRefreshToken"` + ResetCredentialsFlow *string `json:"resetCredentialsFlow,omitempty"` + ResetPasswordAllowed *bool `json:"resetPasswordAllowed"` + RevokeRefreshToken *bool `json:"revokeRefreshToken"` Roles interface{} `json:"roles,omitempty"` ScopeMappings []interface{} `json:"scopeMappings,omitempty"` SMTPServer map[string]string `json:"smtpServer,omitempty"` - SslRequired string `json:"sslRequired,omitempty"` - SsoSessionIdleTimeout int `json:"ssoSessionIdleTimeout,omitempty"` - SsoSessionIdleTimeoutRememberMe int `json:"ssoSessionIdleTimeoutRememberMe,omitempty"` - SsoSessionMaxLifespan int `json:"ssoSessionMaxLifespan,omitempty"` - SsoSessionMaxLifespanRememberMe int `json:"ssoSessionMaxLifespanRememberMe,omitempty"` + SslRequired *string `json:"sslRequired,omitempty"` + SsoSessionIdleTimeout *int `json:"ssoSessionIdleTimeout,omitempty"` + SsoSessionIdleTimeoutRememberMe *int `json:"ssoSessionIdleTimeoutRememberMe,omitempty"` + SsoSessionMaxLifespan *int `json:"ssoSessionMaxLifespan,omitempty"` + SsoSessionMaxLifespanRememberMe *int `json:"ssoSessionMaxLifespanRememberMe,omitempty"` SupportedLocales []string `json:"supportedLocales,omitempty"` UserFederationMappers []interface{} `json:"userFederationMappers,omitempty"` UserFederationProviders []interface{} `json:"userFederationProviders,omitempty"` - UserManagedAccessAllowed bool `json:"userManagedAccessAllowed"` - Users []User `json:"users,omitempty"` - VerifyEmail bool `json:"verifyEmail"` - WaitIncrementSeconds int `json:"waitIncrementSeconds,omitempty"` + UserManagedAccessAllowed *bool `json:"userManagedAccessAllowed"` + Users []*User `json:"users,omitempty"` + VerifyEmail *bool `json:"verifyEmail"` + WaitIncrementSeconds *int `json:"waitIncrementSeconds,omitempty"` } -// MultivaluedHashMap represents something -type MultivaluedHashMap struct { - Empty bool `json:"empty"` - LoadFactor float32 `json:"loadFactor,omitempty"` - Threshold int32 `json:"threshold,omitempty"` +// MultiValuedHashMap represents something +type MultiValuedHashMap struct { + Empty *bool `json:"empty"` + LoadFactor *float32 `json:"loadFactor,omitempty"` + Threshold *int32 `json:"threshold,omitempty"` } // CredentialRepresentation represents credentials type CredentialRepresentation struct { - Algorithm string `json:"algorithm,omitempty"` - Config MultivaluedHashMap `json:"config,omitempty"` - Counter int32 `json:"counter,omitempty"` - CreatedDate int64 `json:"createdDate,omitempty"` - Device string `json:"device,omitempty"` - Digits int32 `json:"digits,omitempty"` - HashIterations int32 `json:"hashIterations,omitempty"` - HashedSaltedValue string `json:"hashedSaltedValue,omitempty"` - Period int32 `json:"period,omitempty"` - Salt string `json:"salt,omitempty"` - Temporary bool `json:"temporary"` - Type string `json:"type,omitempty"` - Value string `json:"value,omitempty"` + Algorithm *string `json:"algorithm,omitempty"` + Config *MultiValuedHashMap `json:"config,omitempty"` + Counter *int32 `json:"counter,omitempty"` + CreatedDate *int64 `json:"createdDate,omitempty"` + Device *string `json:"device,omitempty"` + Digits *int32 `json:"digits,omitempty"` + HashIterations *int32 `json:"hashIterations,omitempty"` + HashedSaltedValue *string `json:"hashedSaltedValue,omitempty"` + Period *int32 `json:"period,omitempty"` + Salt *string `json:"salt,omitempty"` + Temporary *bool `json:"temporary"` + Type *string `json:"type,omitempty"` + Value *string `json:"value,omitempty"` } // TokenOptions represents the options to obtain a token type TokenOptions struct { - ClientID string `json:"client_id"` - ClientSecret string `json:"-"` - GrantType string `json:"grant_type"` - RefreshToken string `json:"refresh_token,omitempty"` + ClientID *string `json:"client_id"` + ClientSecret *string `json:"-"` + GrantType *string `json:"grant_type"` + RefreshToken *string `json:"refresh_token,omitempty"` Scopes []string `json:"-"` - Scope string `json:"scope,omitempty"` + Scope *string `json:"scope,omitempty"` ResponseTypes []string `json:"-"` - ResponseType string `json:"response_type,omitempty"` - Permission string `json:"permission,omitempty"` - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` + ResponseType *string `json:"response_type,omitempty"` + Permission *string `json:"permission,omitempty"` + Username *string `json:"username,omitempty"` + Password *string `json:"password,omitempty"` } // FormData returns a map of options to be used in SetFormData function func (t *TokenOptions) FormData() map[string]string { if len(t.Scopes) > 0 { - t.Scope = strings.Join(t.Scopes, " ") + t.Scope = StringP(strings.Join(t.Scopes, " ")) } if len(t.ResponseTypes) > 0 { - t.ResponseType = strings.Join(t.ResponseTypes, " ") + t.ResponseType = StringP(strings.Join(t.ResponseTypes, " ")) } - if len(t.ResponseType) == 0 { - t.ResponseType = "token" + if NilOrEmpty(t.ResponseType) { + t.ResponseType = StringP("token") } m, _ := json.Marshal(t) var res map[string]string @@ -602,49 +602,49 @@ func (t *TokenOptions) FormData() map[string]string { // UserSessionRepresentation represents a list of user's sessions type UserSessionRepresentation struct { Clients map[string]string `json:"clients,omitempty"` - ID string `json:"id,omitempty"` - IPAddress string `json:"ipAddress,omitempty"` - LastAccess int64 `json:"lastAccess,omitempty"` - Start int64 `json:"start,omitempty"` - UserID string `json:"userId,omitempty"` - Username string `json:"username,omitempty"` + ID *string `json:"id,omitempty"` + IPAddress *string `json:"ipAddress,omitempty"` + LastAccess *int64 `json:"lastAccess,omitempty"` + Start *int64 `json:"start,omitempty"` + UserID *string `json:"userId,omitempty"` + Username *string `json:"username,omitempty"` } // SystemInfoRepresentation represents a system info type SystemInfoRepresentation struct { - FileEncoding string `json:"fileEncoding"` - JavaHome string `json:"javaHome"` - JavaRuntime string `json:"javaRuntime,omitempty"` - JavaVendor string `json:"javaVendor,omitempty"` - JavaVersion string `json:"javaVersion,omitempty"` - JavaVM string `json:"javaVm,omitempty"` - JavaVMVersion string `json:"javaVmVersion,omitempty"` - OSArchitecture string `json:"osArchitecture,omitempty"` - OSName string `json:"osName,omitempty"` - OSVersion string `json:"osVersion,omitempty"` - ServerTime string `json:"serverTime,omitempty"` - Uptime string `json:"uptime,omitempty"` - UptimeMillis int `json:"uptimeMillis,omitempty"` - UserDir string `json:"userDir,omitempty"` - UserLocale string `json:"userLocale,omitempty"` - UserName string `json:"userName,omitempty"` - UserTimezone string `json:"userTimezone,omitempty"` - Version string `json:"version,omitempty"` + FileEncoding *string `json:"fileEncoding"` + JavaHome *string `json:"javaHome"` + JavaRuntime *string `json:"javaRuntime,omitempty"` + JavaVendor *string `json:"javaVendor,omitempty"` + JavaVersion *string `json:"javaVersion,omitempty"` + JavaVM *string `json:"javaVm,omitempty"` + JavaVMVersion *string `json:"javaVmVersion,omitempty"` + OSArchitecture *string `json:"osArchitecture,omitempty"` + OSName *string `json:"osName,omitempty"` + OSVersion *string `json:"osVersion,omitempty"` + ServerTime *string `json:"serverTime,omitempty"` + Uptime *string `json:"uptime,omitempty"` + UptimeMillis *int `json:"uptimeMillis,omitempty"` + UserDir *string `json:"userDir,omitempty"` + UserLocale *string `json:"userLocale,omitempty"` + UserName *string `json:"userName,omitempty"` + UserTimezone *string `json:"userTimezone,omitempty"` + Version *string `json:"version,omitempty"` } // MemoryInfoRepresentation represents a memory info type MemoryInfoRepresentation struct { - Free int `json:"free,omitempty"` - FreeFormated string `json:"freeFormated,omitempty"` - FreePercentage int `json:"freePercentage,omitempty"` - Total int `json:"total,omitempty"` - TotalFormated string `json:"totalFormated,omitempty"` - Used int `json:"used,omitempty"` - UsedFormated string `json:"usedFormated,omitempty"` + Free *int `json:"free,omitempty"` + FreeFormated *string `json:"freeFormated,omitempty"` + FreePercentage *int `json:"freePercentage,omitempty"` + Total *int `json:"total,omitempty"` + TotalFormated *string `json:"totalFormated,omitempty"` + Used *int `json:"used,omitempty"` + UsedFormated *string `json:"usedFormated,omitempty"` } // ServerInfoRepesentation represents a server info type ServerInfoRepesentation struct { - SystemInfo SystemInfoRepresentation `json:"systemInfo,omitempty"` - MemoryInfo MemoryInfoRepresentation `json:"memoryInfo"` + SystemInfo *SystemInfoRepresentation `json:"systemInfo,omitempty"` + MemoryInfo *MemoryInfoRepresentation `json:"memoryInfo"` } diff --git a/pkg/jwx/jwx.go b/pkg/jwx/jwx.go index 49fa043e..59396db2 100644 --- a/pkg/jwx/jwx.go +++ b/pkg/jwx/jwx.go @@ -31,15 +31,15 @@ func DecodeAccessTokenHeader(token string) (*DecodedAccessTokenHeader, error) { return result, nil } -func decodePublicKey(e, n string) (*rsa.PublicKey, error) { - decN, err := base64.RawURLEncoding.DecodeString(n) +func decodePublicKey(e, n *string) (*rsa.PublicKey, error) { + decN, err := base64.RawURLEncoding.DecodeString(*n) if err != nil { return nil, err } nInt := big.NewInt(0) nInt.SetBytes(decN) - decE, err := base64.RawURLEncoding.DecodeString(e) + decE, err := base64.RawURLEncoding.DecodeString(*e) if err != nil { return nil, err } @@ -62,7 +62,7 @@ func decodePublicKey(e, n string) (*rsa.PublicKey, error) { } // DecodeAccessToken currently only supports RSA - sorry for that -func DecodeAccessToken(accessToken string, e string, n string) (*jwt.Token, *jwt.MapClaims, error) { +func DecodeAccessToken(accessToken string, e, n *string) (*jwt.Token, *jwt.MapClaims, error) { rsaPublicKey, err := decodePublicKey(e, n) if err != nil { return nil, nil, err @@ -81,7 +81,7 @@ func DecodeAccessToken(accessToken string, e string, n string) (*jwt.Token, *jwt } // DecodeAccessTokenCustomClaims currently only supports RSA - sorry for that -func DecodeAccessTokenCustomClaims(accessToken string, e string, n string, customClaims jwt.Claims) (*jwt.Token, error) { +func DecodeAccessTokenCustomClaims(accessToken string, e, n *string, customClaims jwt.Claims) (*jwt.Token, error) { rsaPublicKey, err := decodePublicKey(e, n) if err != nil { return nil, err diff --git a/utils.go b/utils.go new file mode 100644 index 00000000..73721d6c --- /dev/null +++ b/utils.go @@ -0,0 +1,79 @@ +package gocloak + +// StringP returns a pointer of a string variable +func StringP(value string) *string { + return &value +} + +// PString returns a string value from a pointer +func PString(value *string) string { + if value == nil { + return "" + } + return *value +} + +// BoolP returns a pointer of a boolean variable +func BoolP(value bool) *bool { + return &value +} + +// PBool returns a boolean value from a pointer +func PBool(value *bool) bool { + return *value +} + +// IntP returns a pointer of an integer variable +func IntP(value int) *int { + return &value +} + +// Int32P returns a pointer of an int32 variable +func Int32P(value int32) *int32 { + return &value +} + +// Int64P returns a pointer of an int64 variable +func Int64P(value int64) *int64 { + return &value +} + +// PInt returns an integer value from a pointer +func PInt(value *int) int { + return *value +} + +// PInt32 returns an int32 value from a pointer +func PInt32(value *int32) int32 { + return *value +} + +// PInt64 returns an int64 value from a pointer +func PInt64(value *int64) int64 { + return *value +} + +// Float32P returns a pointer of a float32 variable +func Float32P(value float32) *float32 { + return &value +} + +// Float64P returns a pointer of a float64 variable +func Float64P(value float64) *float64 { + return &value +} + +// PFloat32 returns an flaot32 value from a pointer +func PFloat32(value *float32) float32 { + return *value +} + +// PFloat64 returns an flaot64 value from a pointer +func PFloat64(value *float64) float64 { + return *value +} + +// NilOrEmpty returns true if string is empty or has a nil value +func NilOrEmpty(value *string) bool { + return value == nil || len(*value) == 0 +} diff --git a/utils_test.go b/utils_test.go new file mode 100644 index 00000000..dcee91c3 --- /dev/null +++ b/utils_test.go @@ -0,0 +1,105 @@ +package gocloak + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStringP(t *testing.T) { + p := StringP("test value") + assert.Equal(t, "test value", *p) +} +func TestPString(t *testing.T) { + p := "test value" + v := PString(&p) + assert.Equal(t, p, v) +} + +func TestPStringNil(t *testing.T) { + v := PString(nil) + assert.Equal(t, "", v) +} + +func TestBoolP(t *testing.T) { + p1 := BoolP(false) + assert.False(t, *p1) + p2 := BoolP(false) + assert.False(t, *p1) + assert.False(t, p1 == p2) + + p := BoolP(true) + assert.True(t, *p) +} + +func TestPBool(t *testing.T) { + p := true + v := PBool(&p) + assert.True(t, v) + + p = false + v = PBool(&p) + assert.False(t, v) +} + +func TestIntP(t *testing.T) { + p := IntP(42) + assert.Equal(t, 42, *p) +} + +func TestInt32P(t *testing.T) { + v := int32(42) + p := Int32P(v) + assert.Equal(t, v, *p) +} + +func TestInt64P(t *testing.T) { + v := int64(42) + p := Int64P(v) + assert.Equal(t, v, *p) +} + +func TestPInt(t *testing.T) { + var p int = 42 + v := PInt(&p) + assert.Equal(t, p, v) + assert.IsType(t, p, v) +} + +func TestPInt32(t *testing.T) { + var p int32 = 42 + v := PInt32(&p) + assert.Equal(t, p, v) + assert.IsType(t, p, v) +} + +func TestPInt64(t *testing.T) { + var p int64 = 42 + v := PInt64(&p) + assert.Equal(t, p, v) + assert.IsType(t, p, v) +} + +func TestFloat32P(t *testing.T) { + v := float32(42.42) + p := Float32P(v) + assert.Equal(t, v, *p) +} +func TestFloat64P(t *testing.T) { + v := float64(42.42) + p := Float64P(v) + assert.Equal(t, v, *p) +} + +func TestPFloat32(t *testing.T) { + var p float32 = 42.42 + v := PFloat32(&p) + assert.Equal(t, p, v) + assert.IsType(t, p, v) +} +func TestPFloat64(t *testing.T) { + var p float64 = 42.42 + v := PFloat64(&p) + assert.Equal(t, p, v) + assert.IsType(t, p, v) +}