3646516cd8cdafc38d3509270557261ceed37725
[nonrtric/plt/sme.git] / capifcore / internal / keycloak / keycloak.go
1 // -
2 //   ========================LICENSE_START=================================
3 //   O-RAN-SC
4 //   %%
5 //   Copyright (C) 2023: Nordix Foundation
6 //   %%
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
10 //
11 //        http://www.apache.org/licenses/LICENSE-2.0
12 //
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===================================
19 //
20
21 package keycloak
22
23 import (
24         "encoding/json"
25         "errors"
26         "io"
27         "net/http"
28         "net/url"
29
30         log "github.com/sirupsen/logrus"
31         "oransc.org/nonrtric/capifcore/internal/config"
32         "oransc.org/nonrtric/capifcore/internal/restclient"
33 )
34
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 }
43
44 type AdminUser struct {
45         User     string
46         Password string
47 }
48
49 type KeycloakManager struct {
50         keycloakServerUrl string
51         admin             AdminUser
52         realms            map[string]string
53         client            restclient.HTTPClient
54 }
55
56 func NewKeycloakManager(cfg *config.Config, c restclient.HTTPClient) *KeycloakManager {
57
58         keycloakUrl := "http://" + cfg.AuthorizationServer.Host + ":" + cfg.AuthorizationServer.Port
59
60         return &KeycloakManager{
61                 keycloakServerUrl: keycloakUrl,
62                 client:            c,
63                 admin: AdminUser{
64                         User:     cfg.AuthorizationServer.AdminUser.User,
65                         Password: cfg.AuthorizationServer.AdminUser.Password,
66                 },
67                 realms: cfg.AuthorizationServer.Realms,
68         }
69 }
70
71 type Jwttoken struct {
72         AccessToken      string `json:"access_token"`
73         IDToken          string `json:"id_token"`
74         ExpiresIn        int    `json:"expires_in"`
75         RefreshExpiresIn int    `json:"refresh_expires_in"`
76         RefreshToken     string `json:"refresh_token"`
77         TokenType        string `json:"token_type"`
78         NotBeforePolicy  int    `json:"not-before-policy"`
79         SessionState     string `json:"session_state"`
80         Scope            string `json:"scope"`
81 }
82
83 func (km *KeycloakManager) GetToken(realm string, data map[string][]string) (Jwttoken, error) {
84         var jwt Jwttoken
85         getTokenUrl := km.keycloakServerUrl + "/realms/" + realm + "/protocol/openid-connect/token"
86
87         resp, err := http.PostForm(getTokenUrl, data)
88
89         if err != nil {
90                 return jwt, err
91         }
92
93         defer resp.Body.Close()
94         body, err := io.ReadAll(resp.Body)
95
96         if err != nil {
97                 return jwt, err
98         }
99         if resp.StatusCode != http.StatusOK {
100                 return jwt, errors.New(string(body))
101         }
102
103         json.Unmarshal([]byte(body), &jwt)
104         return jwt, nil
105 }
106
107 type Client struct {
108         AdminURL               string `json:"adminUrl,omitempty"`
109         BearerOnly             bool   `json:"bearerOnly,omitempty"`
110         ClientID               string `json:"clientId,omitempty"`
111         Enabled                bool   `json:"enabled,omitempty"`
112         PublicClient           bool   `json:"publicClient,omitempty"`
113         RootURL                string `json:"rootUrl,omitempty"`
114         ServiceAccountsEnabled bool   `json:"serviceAccountsEnabled,omitempty"`
115 }
116
117 func (km *KeycloakManager) AddClient(clientId string, realm string) error {
118         data := url.Values{"grant_type": {"password"}, "username": {km.admin.User}, "password": {km.admin.Password}, "client_id": {"admin-cli"}}
119         token, err := km.GetToken("master", data)
120         if err != nil {
121                 log.Errorf("error wrong credentials or url %v\n", err)
122                 return err
123         }
124
125         createClientUrl := km.keycloakServerUrl + "/admin/realms/" + realm + "/clients"
126         newClient := Client{
127                 ClientID:               clientId,
128                 Enabled:                true,
129                 ServiceAccountsEnabled: true,
130                 BearerOnly:             false,
131                 PublicClient:           false,
132         }
133
134         body, _ := json.Marshal(newClient)
135         var headers = map[string]string{"Content-Type": "application/json", "Authorization": "Bearer " + token.AccessToken}
136         if error := restclient.Post(createClientUrl, body, headers, km.client); error != nil {
137                 log.Errorf("error with http request: %+v\n", err)
138                 return err
139         }
140
141         log.Info("Created new client")
142         return nil
143
144 }