2 // ========================LICENSE_START=================================
5 // Copyright (C) 2023: Nordix Foundation
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 // ========================LICENSE_END===================================
30 log "github.com/sirupsen/logrus"
31 "oransc.org/nonrtric/capifcore/internal/config"
32 "oransc.org/nonrtric/capifcore/internal/restclient"
35 //go:generate mockery --name AccessManagement
36 type AccessManagement interface {
37 // Get JWT token for a client.
38 // Returns JWT token if client exits and credentials are correct otherwise returns error.
39 GetToken(realm string, data map[string][]string) (Jwttoken, error)
40 // Add new client in keycloak
41 AddClient(clientId string, realm string) error
42 // Returns information about client including secret
43 GetClientRepresentation(clientId string, realm string) (*Client, error)
46 type AdminUser struct {
51 type KeycloakManager struct {
52 keycloakServerUrl string
54 realms map[string]string
55 client restclient.HTTPClient
58 func NewKeycloakManager(cfg *config.Config, c restclient.HTTPClient) *KeycloakManager {
60 keycloakUrl := "http://" + cfg.AuthorizationServer.Host + ":" + cfg.AuthorizationServer.Port
62 return &KeycloakManager{
63 keycloakServerUrl: keycloakUrl,
66 User: cfg.AuthorizationServer.AdminUser.User,
67 Password: cfg.AuthorizationServer.AdminUser.Password,
69 realms: cfg.AuthorizationServer.Realms,
73 type Jwttoken struct {
74 AccessToken string `json:"access_token"`
75 IDToken string `json:"id_token"`
76 ExpiresIn int `json:"expires_in"`
77 RefreshExpiresIn int `json:"refresh_expires_in"`
78 RefreshToken string `json:"refresh_token"`
79 TokenType string `json:"token_type"`
80 NotBeforePolicy int `json:"not-before-policy"`
81 SessionState string `json:"session_state"`
82 Scope string `json:"scope"`
85 func (km *KeycloakManager) GetToken(realm string, data map[string][]string) (Jwttoken, error) {
87 getTokenUrl := km.keycloakServerUrl + "/realms/" + realm + "/protocol/openid-connect/token"
88 resp, err := http.PostForm(getTokenUrl, data)
94 defer resp.Body.Close()
95 body, err := io.ReadAll(resp.Body)
101 if resp.StatusCode != http.StatusOK {
102 return jwt, errors.New(string(body))
105 json.Unmarshal([]byte(body), &jwt)
110 AdminURL string `json:"adminUrl,omitempty"`
111 AuthorizationServicesEnabled *bool `json:"authorizationServicesEnabled,omitempty"`
112 BearerOnly bool `json:"bearerOnly,omitempty"`
113 ClientID string `json:"clientId,omitempty"`
114 Enabled bool `json:"enabled,omitempty"`
115 ID *string `json:"id,omitempty"`
116 PublicClient bool `json:"publicClient,omitempty"`
117 RootURL string `json:"rootUrl,omitempty"`
118 Secret *string `json:"secret,omitempty"`
119 ServiceAccountsEnabled bool `json:"serviceAccountsEnabled,omitempty"`
122 func (km *KeycloakManager) AddClient(clientId string, realm string) error {
124 data := url.Values{"grant_type": {"password"}, "username": {km.admin.User}, "password": {km.admin.Password}, "client_id": {"admin-cli"}}
125 token, err := km.GetToken("master", data)
127 log.Errorf("error wrong credentials or url %v\n", err)
131 createClientUrl := km.keycloakServerUrl + "/admin/realms/" + realm + "/clients"
132 newClient := map[string]interface{}{"clientId": clientId, "serviceAccountsEnabled": true}
134 body, err := json.Marshal(newClient)
139 var headers = map[string]string{"Content-Type": "application/json", "Authorization": "Bearer " + token.AccessToken}
140 if err := restclient.Post(createClientUrl, body, headers, km.client); err != nil {
141 log.Errorf("addClient - error with http request: %+v\n", err)
145 log.Debug("Created new client")
150 func (km *KeycloakManager) GetClientRepresentation(clientId string, realm string) (*Client, error) {
152 data := url.Values{"grant_type": {"password"}, "username": {km.admin.User}, "password": {km.admin.Password}, "client_id": {"admin-cli"}}
153 token, err := km.GetToken("master", data)
155 log.Errorf("error wrong credentials or url %v\n", err)
159 createClientUrl, _ := url.Parse(km.keycloakServerUrl + "/admin/realms/" + realm + "/clients")
160 q := createClientUrl.Query()
161 q.Add("clientId", clientId)
162 createClientUrl.RawQuery = q.Encode()
164 var headers = map[string]string{"Content-Type": "application/json", "Authorization": "Bearer " + token.AccessToken}
166 if resp, err := restclient.Get(createClientUrl.String(), headers, km.client); err == nil {
169 if err = json.Unmarshal(resp, &client); err != nil {
170 log.Errorf("error unmarshal keycloak client object: %+v\n", err)
175 return &client[0], nil
180 log.Errorf("error with http request: %+v\n", err)