Merge "Sample consumer to get kafka broker from ICS"
[nonrtric.git] / service-exposure / rapps-jwt.go
1 // -
2 //
3 //      ========================LICENSE_START=================================
4 //      O-RAN-SC
5 //      %%
6 //      Copyright (C) 2022-2023: Nordix Foundation
7 //      %%
8 //      Licensed under the Apache License, Version 2.0 (the "License");
9 //      you may not use this file except in compliance with the License.
10 //      You may obtain a copy of the License at
11 //
12 //           http://www.apache.org/licenses/LICENSE-2.0
13 //
14 //      Unless required by applicable law or agreed to in writing, software
15 //      distributed under the License is distributed on an "AS IS" BASIS,
16 //      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 //      See the License for the specific language governing permissions and
18 //      limitations under the License.
19 //      ========================LICENSE_END===================================
20 package main
21
22 import (
23         "context"
24         "crypto/tls"
25         "crypto/x509"
26         "encoding/json"
27         "flag"
28         "fmt"
29         "io/ioutil"
30         metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31         kubernetes "k8s.io/client-go/kubernetes"
32         "k8s.io/client-go/rest"
33         "net"
34         "net/http"
35         "net/url"
36         "rapps/utils/generatejwt"
37         "time"
38 )
39
40 type Jwttoken struct {
41         Access_token       string
42         Expires_in         int
43         Refresh_expires_in int
44         Refresh_token      string
45         Token_type         string
46         Not_before_policy  int
47         Session_state      string
48         Scope              string
49 }
50
51 var keycloakHost string
52 var keycloakPort string
53 var keycloakAlias string
54 var realmName string
55 var clientId string
56 var namespace string
57 var authenticator string
58 var tlsCrt string
59 var tlsKey string
60 var caCrt string
61 var healthy bool = true
62 var jwt Jwttoken
63
64 const (
65         scope                 = "email"
66         client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
67 )
68
69 func getToken(res http.ResponseWriter, req *http.Request) {
70         var resp = &http.Response{}
71         var err error
72         authenticator = req.Header.Get("authenticator")
73         clientId = req.Header.Get("client")
74         realmName = req.Header.Get("realm")
75         namespace = req.Header.Get("ns")
76         tlsCrt = req.Header.Get("tlsCrt")
77         tlsKey = req.Header.Get("tlsKey")
78         caCrt = req.Header.Get("caCrt")
79         keycloakUrl := "http://" + keycloakHost + ":" + keycloakPort + "/realms/" + realmName + "/protocol/openid-connect/token"
80         fmt.Printf("Making token request to %s\n", keycloakUrl)
81         res.Header().Set("Content-type", "application/json")
82         res.Header().Set("Authorization", "")
83
84         if authenticator == "client-jwt" {
85                 resp, err = getJwtToken(keycloakUrl, clientId)
86         } else if authenticator == "client-x509" {
87                 keycloakPort = "443"
88                 keycloakUrl := "https://" + keycloakAlias + ":" + keycloakPort + "/realms/" + realmName + "/protocol/openid-connect/token"
89                 resp, err = getx509Token(keycloakUrl, clientId, tlsCrt, tlsKey, caCrt)
90         } else {
91                 resp, err = getSecretToken(keycloakUrl, clientId)
92         }
93
94         if err != nil {
95                 fmt.Println(err)
96                 res.WriteHeader(http.StatusInternalServerError)
97                 res.Write([]byte(err.Error()))
98                 panic("Something wrong with the credentials or url ")
99         }
100
101         defer resp.Body.Close()
102         body, err := ioutil.ReadAll(resp.Body)
103         json.Unmarshal([]byte(body), &jwt)
104         fmt.Printf("Token: %s\n", jwt.Access_token)
105
106         res.Header().Set("Authorization", "Bearer "+jwt.Access_token)
107         res.WriteHeader(http.StatusOK)
108         res.Write([]byte("Successfully retrieved JWT access token"))
109 }
110
111 func getJwtToken(keycloakUrl, clientId string) (*http.Response, error) {
112         var resp = &http.Response{}
113         var err error
114         client_assertion := getClientAssertion()
115
116         if jwt.Refresh_token != "" {
117                 resp, err = http.PostForm(keycloakUrl, url.Values{"client_assertion_type": {client_assertion_type},
118                         "client_assertion": {client_assertion}, "grant_type": {"refresh_token"},
119                         "refresh_token": {jwt.Refresh_token}, "client_id": {clientId}, "scope": {scope}})
120         } else {
121                 resp, err = http.PostForm(keycloakUrl, url.Values{"client_assertion_type": {client_assertion_type},
122                         "client_assertion": {client_assertion}, "grant_type": {"client_credentials"},
123                         "client_id": {clientId}, "scope": {scope}})
124         }
125
126         return resp, err
127 }
128
129 func getClientAssertion() string {
130         aud := "https://keycloak:8443/realms/" + realmName
131         clientAssertion := generatejwt.CreateJWT(tlsKey, "", clientId, aud)
132         return clientAssertion
133 }
134
135 func getx509Token(keycloakUrl, clientId, tlsCrt, tlsKey, caCrt string) (*http.Response, error) {
136         var resp = &http.Response{}
137         var err error
138
139         client := getClient()
140         resp, err = client.PostForm(keycloakUrl, url.Values{"username": {""}, "password": {""}, "grant_type": {"password"}, "client_id": {clientId}, "scope": {scope}})
141
142         return resp, err
143 }
144
145 func getClient() *http.Client {
146         caCert, _ := ioutil.ReadFile(caCrt)
147         caCertPool := x509.NewCertPool()
148         caCertPool.AppendCertsFromPEM(caCert)
149
150         cert, _ := tls.LoadX509KeyPair(tlsCrt, tlsKey)
151
152         dialer := &net.Dialer{
153                 Timeout:   30 * time.Second,
154                 KeepAlive: 30 * time.Second,
155                 DualStack: true,
156         }
157
158         client := &http.Client{
159                 Transport: &http.Transport{
160                         DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
161                                 fmt.Println("address original =", addr)
162                                 if addr == keycloakAlias+":"+keycloakPort {
163                                         addr = keycloakHost + ":" + keycloakPort
164                                         fmt.Println("address modified =", addr)
165                                 }
166                                 return dialer.DialContext(ctx, network, addr)
167                         },
168                         TLSClientConfig: &tls.Config{
169                                 RootCAs:      caCertPool,
170                                 Certificates: []tls.Certificate{cert},
171                         },
172                 },
173         }
174         return client
175 }
176
177 func getSecretToken(keycloakUrl, clientId string) (*http.Response, error) {
178         var resp = &http.Response{}
179         var err error
180
181         secretName := clientId + "-secret"
182         clientSecret := getSecret(secretName)
183         resp, err = http.PostForm(keycloakUrl,
184                 url.Values{"client_secret": {clientSecret}, "grant_type": {"client_credentials"}, "client_id": {clientId}})
185
186         return resp, err
187 }
188
189 func getSecret(secretName string) string {
190         clientset := connectToK8s()
191         res, err := clientset.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{})
192         if err != nil {
193                 fmt.Println(err.Error())
194         }
195         return string(res.Data["client_secret"])
196 }
197
198 func connectToK8s() *kubernetes.Clientset {
199         config, err := rest.InClusterConfig()
200         if err != nil {
201                 fmt.Println("failed to create K8s config")
202         }
203
204         clientset, err := kubernetes.NewForConfig(config)
205         if err != nil {
206                 fmt.Println("Failed to create K8s clientset")
207         }
208
209         return clientset
210 }
211
212 func health(res http.ResponseWriter, req *http.Request) {
213         if healthy {
214                 res.WriteHeader(http.StatusOK)
215                 res.Write([]byte("healthy"))
216         } else {
217                 res.WriteHeader(http.StatusInternalServerError)
218                 res.Write([]byte("unhealthy"))
219         }
220 }
221
222 func main() {
223         flag.StringVar(&keycloakHost, "keycloakHost", "istio-ingressgateway.istio-system", "Keycloak Host")
224         flag.StringVar(&keycloakPort, "keycloakPort", "80", "Keycloak Port")
225         flag.StringVar(&keycloakAlias, "keycloakAlias", "keycloak.est.tech", "Keycloak URL Alias")
226         flag.Parse()
227
228         healthHandler := http.HandlerFunc(health)
229         http.Handle("/health", healthHandler)
230         tokenHandler := http.HandlerFunc(getToken)
231         http.Handle("/token", tokenHandler)
232         http.ListenAndServe(":8888", nil)
233
234         ioutil.WriteFile("init.txt", []byte("Initialization done."), 0644)
235 }