-
Notifications
You must be signed in to change notification settings - Fork 3
/
keycloak_user.go
117 lines (104 loc) · 3.47 KB
/
keycloak_user.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package data
import (
"encoding/json"
"fmt"
"github.com/google/uuid"
"github.com/ohler55/ojg/jp"
)
const (
pathToPassword = "credentials.password"
)
// KeyCloakUser this structure is for user data that looks similar to KeyCloak, Users in Keycloak have info field with preferred_username and sub
// and others fields, Ferrum users have credentials built-in in user (temporary it stores in non encrypted mode)
type KeyCloakUser struct {
rawData interface{}
jsonRawData string
}
// CreateUser function creates User interface instance (KeyCloakUser) from raw json
/* Function create User instance from any json (interface{})
* Parameters:
* - rawData - any json
* Return: instance of User as KeyCloakUser
*/
func CreateUser(rawData interface{}) User {
jsonData, _ := json.Marshal(&rawData)
kcUser := &KeyCloakUser{rawData: rawData, jsonRawData: string(jsonData)}
user := User(kcUser)
return user
}
// GetUsername returns username as it stores in KeyCloak
/* this function use internal map to navigate over info.preferred_username keys, the last one key is a login key
* We are expecting that username is unique in the Realm
* Parameters: no
* Returns: username
*/
func (user *KeyCloakUser) GetUsername() string {
return getPathStringValue[string](user.rawData, "info.preferred_username")
}
// GetPassword returns password
/* this function use internal map to navigate over credentials.password keys to retrieve a password
* Parameters: no
* Returns: password
*/
func (user *KeyCloakUser) GetPassword() string {
return getPathStringValue[string](user.rawData, pathToPassword)
}
func (user *KeyCloakUser) SetPassword(password string) error {
mask, err := jp.ParseString(pathToPassword)
if err != nil {
return fmt.Errorf("jp.ParseString failed: %w", err)
}
if err := mask.Set(user.rawData, password); err != nil {
return fmt.Errorf("jp.Set failed: %w", err)
}
jsonData, _ := json.Marshal(user.rawData)
user.jsonRawData = string(jsonData)
return nil
}
// GetId returns unique user identifier
/* this function use internal map to navigate over info.sun keys to retrieve a user id
* Parameters: no
* Returns: user id
*/
func (user *KeyCloakUser) GetId() uuid.UUID {
idStrValue := getPathStringValue[string](user.rawData, "info.sub")
id, err := uuid.Parse(idStrValue)
if err != nil {
// todo(UMV): think what to do here, return error!
}
return id
}
// GetUserInfo returns Json with all non-confidential user data as KeyCloak do
/* this function use internal map to navigate over key info ant retrieve all public userinfo
* Parameters: no
* Returns: user info
*/
func (user *KeyCloakUser) GetUserInfo() interface{} {
result := getPathStringValue[interface{}](user.rawData, "info")
return result
}
func (user *KeyCloakUser) GetRawData() interface{} {
return user.rawData
}
func (user *KeyCloakUser) GetJsonString() string {
return user.jsonRawData
}
// getPathStringValue is a generic function to get actually map by key, key represents as a jsonpath navigation property
/* this function uses json path to navigate over nested maps and return any required type
* Parameters:
* - rawData - json object
* - path - json path to retrieve part of json with specified type (T)
* Returns: part of json
*/
func getPathStringValue[T any](rawData interface{}, path string) T {
var result T
mask, err := jp.ParseString(path)
if err != nil {
// todo(UMV): log and think what to do ...
}
res := mask.Get(rawData)
if res != nil && len(res) == 1 {
result = res[0].(T)
}
return result
}