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 realmVal, ok := km.realms[realm]
89 log.Errorf("error realm does not exist\n")
90 return jwt, errors.New("realm does not exist")
92 getTokenUrl := km.keycloakServerUrl + "/realms/" + realmVal + "/protocol/openid-connect/token"
93 resp, err := http.PostForm(getTokenUrl, data)
99 defer resp.Body.Close()
100 body, err := io.ReadAll(resp.Body)
106 if resp.StatusCode != http.StatusOK {
107 return jwt, errors.New(string(body))
110 json.Unmarshal([]byte(body), &jwt)
115 AdminURL string `json:"adminUrl,omitempty"`
116 AuthorizationServicesEnabled *bool `json:"authorizationServicesEnabled,omitempty"`
117 BearerOnly bool `json:"bearerOnly,omitempty"`
118 ClientID string `json:"clientId,omitempty"`
119 Enabled bool `json:"enabled,omitempty"`
120 ID *string `json:"id,omitempty"`
121 PublicClient bool `json:"publicClient,omitempty"`
122 RootURL string `json:"rootUrl,omitempty"`
123 Secret *string `json:"secret,omitempty"`
124 ServiceAccountsEnabled bool `json:"serviceAccountsEnabled,omitempty"`
127 func (km *KeycloakManager) AddClient(clientId string, realm string) error {
129 data := url.Values{"grant_type": {"password"}, "username": {km.admin.User}, "password": {km.admin.Password}, "client_id": {"admin-cli"}}
130 token, err := km.GetToken("master", data)
132 log.Errorf("error wrong credentials or url %v\n", err)
136 realmVal, ok := km.realms[realm]
138 log.Errorf("error realm does not exist\n")
139 return errors.New("realm does not exist")
142 createClientUrl := km.keycloakServerUrl + "/admin/realms/" + realmVal + "/clients"
143 newClient := map[string]interface{}{"clientId": clientId, "serviceAccountsEnabled": true}
145 body, err := json.Marshal(newClient)
150 var headers = map[string]string{"Content-Type": "application/json", "Authorization": "Bearer " + token.AccessToken}
151 if err := restclient.Post(createClientUrl, body, headers, km.client); err != nil {
152 log.Errorf("addClient - error with http request: %+v\n", err)
156 log.Debug("Created new client")
161 func (km *KeycloakManager) GetClientRepresentation(clientId string, realm string) (*Client, error) {
163 data := url.Values{"grant_type": {"password"}, "username": {km.admin.User}, "password": {km.admin.Password}, "client_id": {"admin-cli"}}
164 token, err := km.GetToken("master", data)
166 log.Errorf("error wrong credentials or url %v\n", err)
170 realmVal, ok := km.realms[realm]
172 log.Errorf("error realm does not exist\n")
173 return nil, errors.New("realm does not exist")
176 createClientUrl, _ := url.Parse(km.keycloakServerUrl + "/admin/realms/" + realmVal + "/clients")
177 q := createClientUrl.Query()
178 q.Add("clientId", clientId)
179 createClientUrl.RawQuery = q.Encode()
181 var headers = map[string]string{"Content-Type": "application/json", "Authorization": "Bearer " + token.AccessToken}
183 if resp, err := restclient.Get(createClientUrl.String(), headers, km.client); err == nil {
186 if err = json.Unmarshal(resp, &client); err != nil {
187 log.Errorf("error unmarshal keycloak client object: %+v\n", err)
192 return &client[0], nil
197 log.Errorf("error with http request: %+v\n", err)