Skip to content

Commit 8a34394

Browse files
authored
[CLOUDTRUST-4637] Get users profile
1 parent 83e30b5 commit 8a34394

17 files changed

+461
-176
lines changed

api/credentials.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func (c *Client) ResetPassword(accessToken string, realmName, userID string, cre
2828
return c.put(accessToken, url.Path(resetPasswordPath), url.Param("realm", realmName), url.Param("id", userID), body.JSON(cred))
2929
}
3030

31-
// Logout all sessions of the user.
31+
// LogoutAllSessions of the user.
3232
func (c *Client) LogoutAllSessions(accessToken string, realmName, userID string) error {
3333
var _, err = c.post(accessToken, nil, url.Path(logoutPath), url.Param("realm", realmName), url.Param("id", userID))
3434
return err

api/roles.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func (c *Client) GetRoles(accessToken string, realmName string) ([]keycloak.Role
3232
return resp, err
3333
}
3434

35-
// GetRoles gets all roles for the realm or client with their attributes
35+
// GetRolesWithAttributes gets all roles for the realm or client with their attributes
3636
func (c *Client) GetRolesWithAttributes(accessToken string, realmName string) ([]keycloak.RoleRepresentation, error) {
3737
var resp = []keycloak.RoleRepresentation{}
3838
var err = c.get(accessToken, &resp, url.Path(rolePath), url.Param("realm", realmName), query.Add("briefRepresentation", "false"))

api/users.go

+9
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const (
3232
expiredToUAcceptancePath = adminRootPath + "/expired-tou-acceptance"
3333
getSupportInfoPath = adminRootPath + "/support-infos"
3434
generateTrustIDAuthToken = "/auth/realms/:realmReq/trustid-auth-token/realms/:realm/users/:userId/generate"
35+
profilePath = userPath + "/profile"
3536
)
3637

3738
// GetUsers returns a list of users, filtered according to the query parameters.
@@ -191,8 +192,16 @@ func (c *Client) GetSupportInfo(accessToken string, email string) ([]keycloak.Em
191192
return emailInfos, err
192193
}
193194

195+
// GenerateTrustIDAuthToken generates a TrustID auth token
194196
func (c *Client) GenerateTrustIDAuthToken(accessToken string, reqRealmName string, realmName string, userID string) (string, error) {
195197
var token keycloak.TrustIDAuthTokenRepresentation
196198
err := c.get(accessToken, &token, url.Path(generateTrustIDAuthToken), url.Param("realmReq", reqRealmName), url.Param("realm", realmName), url.Param("userId", userID))
197199
return *token.Token, err
198200
}
201+
202+
// GetUserProfile gets the configuration of attribute management
203+
func (c *Client) GetUserProfile(accessToken string, realmName string) (keycloak.UserProfileRepresentation, error) {
204+
var profile keycloak.UserProfileRepresentation
205+
err := c.get(accessToken, &profile, url.Path(profilePath), url.Param("realm", realmName))
206+
return profile, err
207+
}

config.go

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"time"
55
)
66

7+
// KeycloakURIProvider interface
78
type KeycloakURIProvider interface {
89
GetDefaultKey() string
910
GetAllBaseURIs() []string

definitions-profile.go

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package keycloak
2+
3+
import "strings"
4+
5+
// UserProfileRepresentation struct
6+
type UserProfileRepresentation struct {
7+
Attributes []ProfileAttrbRepresentation `json:"attributes"`
8+
Groups []ProfileGroupRepresentation `json:"groups"`
9+
}
10+
11+
// ProfileAttrbRepresentation struct
12+
type ProfileAttrbRepresentation struct {
13+
Name *string `json:"name,omitempty"`
14+
DisplayName *string `json:"displayName,omitempty"`
15+
Group *string `json:"group,omitempty"`
16+
Required *ProfileAttrbRequiredRepresentation `json:"required,omitempty"`
17+
Permissions *ProfileAttrbPermissionsRepresentation `json:"permissions,omitempty"`
18+
Validations ProfileAttrbValidationRepresentation `json:"validations,omitempty"`
19+
Selector *ProfileAttrbSelectorRepresentation `json:"selector,omitempty"`
20+
Annotations map[string]string `json:"annotations,omitempty"`
21+
}
22+
23+
// ProfileAttrbValidationRepresentation struct
24+
// Known keys:
25+
// - email: empty
26+
// - length: min, max (int/string), trim-disabled (boolean as string)
27+
// - integer: min, max (integer as string)
28+
// - double: min, max (double as string)
29+
// - options: options (array of allowed values)
30+
// - pattern: pattern (regex as string), error-message (string)
31+
// - local-date: empty
32+
// - uri: empty
33+
// - username-prohibited-characters: error-message (string)
34+
// - person-name-prohibited-characters: error-message (string)
35+
type ProfileAttrbValidationRepresentation map[string]ProfileAttrValidatorRepresentation
36+
37+
type ProfileAttrValidatorRepresentation map[string]interface{}
38+
39+
// ProfileAttrbRequiredRepresentation struct
40+
type ProfileAttrbRequiredRepresentation struct {
41+
Roles []string `json:"roles,omitempty"`
42+
Scopes []string `json:"scopes,omitempty"`
43+
}
44+
45+
// ProfileAttrbPermissionsRepresentation struct
46+
type ProfileAttrbPermissionsRepresentation struct {
47+
View []string `json:"view,omitempty"`
48+
Edit []string `json:"edit,omitempty"`
49+
}
50+
51+
// ProfileAttrbSelectorRepresentation struct
52+
type ProfileAttrbSelectorRepresentation struct {
53+
Scopes []string `json:"scopes,omitempty"`
54+
}
55+
56+
// ProfileGroupRepresentation struct
57+
type ProfileGroupRepresentation struct {
58+
Name *string `json:"name,omitempty"`
59+
DisplayHeader *string `displayHeader:"name,omitempty"`
60+
DisplayDescription *string `displayDescription:"name,omitempty"`
61+
Annotations map[string]string `annotations:"name,omitempty"`
62+
}
63+
64+
// IsAnnotationTrue checks if an annotation is true
65+
func (attrb *ProfileAttrbRepresentation) IsAnnotationTrue(key string) bool {
66+
return attrb.AnnotationEqualsIgnoreCase(key, "true")
67+
}
68+
69+
// IsAnnotationFalse checks if an annotation is false
70+
func (attrb *ProfileAttrbRepresentation) IsAnnotationFalse(key string) bool {
71+
return attrb.AnnotationEqualsIgnoreCase(key, "false")
72+
}
73+
74+
// AnnotationEqualsIgnoreCase checks if an annotation
75+
func (attrb *ProfileAttrbRepresentation) AnnotationEqualsIgnoreCase(key string, value string) bool {
76+
return attrb.AnnotationMatches(key, func(attrbValue string) bool {
77+
return strings.EqualFold(value, attrbValue)
78+
})
79+
}
80+
81+
// AnnotationMatches checks if an annotation
82+
func (attrb *ProfileAttrbRepresentation) AnnotationMatches(key string, matcher func(value string) bool) bool {
83+
if attrb.Annotations != nil {
84+
if attrbValue, ok := attrb.Annotations[key]; ok {
85+
return matcher(attrbValue)
86+
}
87+
}
88+
return false
89+
}

definitions-profile_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package keycloak
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestAttribute(t *testing.T) {
10+
var attrb = ProfileAttrbRepresentation{}
11+
var key = "key"
12+
13+
t.Run("Annotations is nil", func(t *testing.T) {
14+
assert.False(t, attrb.IsAnnotationFalse(key))
15+
assert.False(t, attrb.IsAnnotationTrue(key))
16+
})
17+
t.Run("Annotations is empty", func(t *testing.T) {
18+
attrb.Annotations = map[string]string{}
19+
assert.False(t, attrb.IsAnnotationFalse(key))
20+
assert.False(t, attrb.IsAnnotationTrue(key))
21+
})
22+
t.Run("Annotations is empty", func(t *testing.T) {
23+
attrb.Annotations[key] = "false"
24+
assert.True(t, attrb.IsAnnotationFalse(key))
25+
assert.False(t, attrb.IsAnnotationTrue(key))
26+
})
27+
}

definitions.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package keycloak
22

3+
import (
4+
"strings"
5+
)
6+
37
// AdminEventRepresentation struct
48
type AdminEventRepresentation struct {
59
AuthDetails *AuthDetailsRepresentation `json:"authDetails,omitempty"`
@@ -442,7 +446,7 @@ type RealmRepresentation struct {
442446
AdminEventsDetailsEnabled *bool `json:"adminEventsDetailsEnabled,omitempty"`
443447
AdminEventsEnabled *bool `json:"adminEventsEnabled,omitempty"`
444448
AdminTheme *string `json:"adminTheme,omitempty"`
445-
Attributes *map[string]interface{} `json:"attributes,omitempty"`
449+
Attributes *map[string]*string `json:"attributes,omitempty"`
446450
AuthenticationFlows *[]AuthenticationFlowRepresentation `json:"authenticationFlows,omitempty"`
447451
AuthenticatorConfig *[]AuthenticatorConfigRepresentation `json:"authenticatorConfig,omitempty"`
448452
BrowserFlow *string `json:"browserFlow,omitempty"`
@@ -811,6 +815,17 @@ type EmailInfoRepresentation struct {
811815
CreationDate *int64 `json:"creationDate,omitempty"`
812816
}
813817

818+
// TrustIDAuthTokenRepresentation struct
814819
type TrustIDAuthTokenRepresentation struct {
815820
Token *string `json:"token"`
816821
}
822+
823+
// IsUserProfileEnabled tells if user profile is enabled for the given realm
824+
func (r *RealmRepresentation) IsUserProfileEnabled() bool {
825+
if r.Attributes != nil && *r.Attributes != nil {
826+
if v, ok := (*r.Attributes)["userProfileEnabled"]; ok {
827+
return v != nil && strings.EqualFold(*v, "true")
828+
}
829+
}
830+
return false
831+
}

definitions_test.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package keycloak
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestIsUserProfileEnabled(t *testing.T) {
10+
var realm = RealmRepresentation{Attributes: nil}
11+
var bFalse = "FALSE"
12+
var bTrue = "TRue"
13+
14+
t.Run("No attributes", func(t *testing.T) {
15+
assert.False(t, realm.IsUserProfileEnabled())
16+
})
17+
t.Run("Empty attributes", func(t *testing.T) {
18+
realm.Attributes = &map[string]*string{}
19+
assert.False(t, realm.IsUserProfileEnabled())
20+
})
21+
t.Run("User profile attribute is false", func(t *testing.T) {
22+
(*realm.Attributes)["userProfileEnabled"] = &bFalse
23+
assert.False(t, realm.IsUserProfileEnabled())
24+
})
25+
t.Run("User profile attribute is true", func(t *testing.T) {
26+
(*realm.Attributes)["userProfileEnabled"] = &bTrue
27+
assert.True(t, realm.IsUserProfileEnabled())
28+
})
29+
}

errormessages_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package keycloak
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestHTTPError(t *testing.T) {
10+
var err = HTTPError{HTTPStatus: 400, Message: "error message"}
11+
assert.Equal(t, "400:error message", err.Error())
12+
}
13+
14+
func TestClientDetailedError(t *testing.T) {
15+
var err = ClientDetailedError{HTTPStatus: 400, Message: "error message"}
16+
assert.Equal(t, "400:error message", err.Error())
17+
assert.Equal(t, 400, err.Status())
18+
assert.Equal(t, "error message", err.ErrorMessage())
19+
}

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/cloudtrust/keycloak-client/v2
33
go 1.17
44

55
require (
6-
github.com/cloudtrust/common-service/v2 v2.6.0
6+
github.com/cloudtrust/common-service/v2 v2.6.4
77
github.com/coreos/go-oidc v2.2.1+incompatible
88
github.com/gbrlsnchs/jwt/v2 v2.0.0
99
github.com/go-kit/kit v0.12.0

0 commit comments

Comments
 (0)