blob: 5fc92f4598b8ae0aaeb8310217bcf018e5a48bb6 [file] [log] [blame]
ktimoney3570d5a2022-05-24 13:54:55 +01001// -
2// ========================LICENSE_START=================================
3// O-RAN-SC
4// %%
5// Copyright (C) 2022: 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
ktimoney6be05292022-03-02 12:53:14 +000021package main
22
23import (
24 "context"
25 "fmt"
26 "github.com/Nerzal/gocloak/v10"
ktimoneyf27b5132022-03-07 16:48:47 +000027 corev1 "k8s.io/api/core/v1"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 kubernetes "k8s.io/client-go/kubernetes"
30 "k8s.io/client-go/rest"
ktimoney6be05292022-03-02 12:53:14 +000031 "net/http"
ktimoney8ead72a2022-04-12 15:10:10 +010032 "strings"
33 "rapps/utils/pemtojwks"
ktimoney6be05292022-03-02 12:53:14 +000034)
35
ktimoneyf27b5132022-03-07 16:48:47 +000036const (
ktimoney8ead72a2022-04-12 15:10:10 +010037 namespace = "istio-nonrtric"
ktimoneyf27b5132022-03-07 16:48:47 +000038)
ktimoney6be05292022-03-02 12:53:14 +000039
ktimoneyf27b5132022-03-07 16:48:47 +000040func createClient(res http.ResponseWriter, req *http.Request) {
41 query := req.URL.Query()
42 realmName := query.Get("realm")
43 clientName := query.Get("name")
44 role := query.Get("role")
ktimoney6be05292022-03-02 12:53:14 +000045 var msg string
ktimoneyf27b5132022-03-07 16:48:47 +000046 msg, err := create(realmName, clientName, role)
ktimoney6be05292022-03-02 12:53:14 +000047 if err != nil {
48 msg = err.Error()
49 }
ktimoney8ead72a2022-04-12 15:10:10 +010050 if realmName != "x509" && realmName != "jwt" {
51 createSecret(msg, clientName, realmName, role, namespace)
52 }
ktimoneyf27b5132022-03-07 16:48:47 +000053 // create response binary data
54 data := []byte(msg) // slice of bytes
55 // write `data` to response
56 res.Write(data)
57}
58
59func removeClient(res http.ResponseWriter, req *http.Request) {
60 query := req.URL.Query()
61 realmName := query.Get("realm")
62 clientName := query.Get("name")
63 role := query.Get("role")
64
65 var msg string = "Removed keycloak " + clientName + " from " + realmName + " realm"
66 remove(realmName, clientName)
ktimoney8ead72a2022-04-12 15:10:10 +010067 if realmName != "x509" && realmName != "jwt" {
68 removeSecret(namespace, role)
69 }
ktimoney6be05292022-03-02 12:53:14 +000070 // create response binary data
71 data := []byte(msg) // slice of bytes
72 // write `data` to response
73 res.Write(data)
74}
75
76func main() {
ktimoneyf27b5132022-03-07 16:48:47 +000077 createHandler := http.HandlerFunc(createClient)
78 http.Handle("/create", createHandler)
79 removeHandler := http.HandlerFunc(removeClient)
80 http.Handle("/remove", removeHandler)
81 http.ListenAndServe(":9000", nil)
ktimoney6be05292022-03-02 12:53:14 +000082}
83
ktimoneyf27b5132022-03-07 16:48:47 +000084func create(realmName, clientName, clientRoleName string) (string, error) {
ktimoney6be05292022-03-02 12:53:14 +000085 client := gocloak.NewClient("http://keycloak.default:8080")
86 ctx := context.Background()
87 token, err := client.LoginAdmin(ctx, "admin", "admin", "master")
88 if err != nil {
89 return "", err
90 }
91
ktimoney6be05292022-03-02 12:53:14 +000092 _, err = client.GetRealm(ctx, token.AccessToken, realmName)
93 if err != nil {
94 realmRepresentation := gocloak.RealmRepresentation{
95 ID: gocloak.StringP(realmName),
96 Realm: gocloak.StringP(realmName),
97 DisplayName: gocloak.StringP(realmName),
98 Enabled: gocloak.BoolP(true),
99 }
100
101 realm, err := client.CreateRealm(ctx, token.AccessToken, realmRepresentation)
102 if err != nil {
103 return "", err
104 } else {
105 fmt.Println("Created realm", realm)
106 }
107 } else {
108 fmt.Println("Realm already exists", realmName)
109 }
110
ktimoney8ead72a2022-04-12 15:10:10 +0100111 flowAlias := "x509 direct grant"
112 flowId := ""
113 flows, err := client.GetAuthenticationFlows(ctx, token.AccessToken, realmName)
114 if err != nil {
115 fmt.Println("Oh no!, failed to get flows :(")
116 } else {
117 for _, flow := range flows {
118 if flow.Alias != nil && *flow.Alias == flowAlias {
119 flowId = *flow.ID
120 }
121 }
122 fmt.Println("Retrieved AuthenticationFlow id", flowId)
123 }
124
125 newClient1 := gocloak.Client{
ktimoneyf27b5132022-03-07 16:48:47 +0000126 ClientID: gocloak.StringP(clientName),
ktimoney6be05292022-03-02 12:53:14 +0000127 Enabled: gocloak.BoolP(true),
128 DirectAccessGrantsEnabled: gocloak.BoolP(true),
129 BearerOnly: gocloak.BoolP(false),
130 PublicClient: gocloak.BoolP(false),
131 ServiceAccountsEnabled: gocloak.BoolP(true),
132 ClientAuthenticatorType: gocloak.StringP("client-secret"),
ktimoneyf27b5132022-03-07 16:48:47 +0000133 DefaultClientScopes: &[]string{"email"},
ktimoney8ead72a2022-04-12 15:10:10 +0100134 Attributes: &map[string]string{"use.refresh.tokens": "true",
135 "client_credentials.use_refresh_token": "true"},
ktimoney6be05292022-03-02 12:53:14 +0000136 }
ktimoney8ead72a2022-04-12 15:10:10 +0100137
138 newClient2 := gocloak.Client{
139 ClientID: gocloak.StringP(clientName),
140 Enabled: gocloak.BoolP(true),
141 DirectAccessGrantsEnabled: gocloak.BoolP(true),
142 BearerOnly: gocloak.BoolP(false),
143 PublicClient: gocloak.BoolP(false),
144 ServiceAccountsEnabled: gocloak.BoolP(true),
145 ClientAuthenticatorType: gocloak.StringP("client-x509"),
146 DefaultClientScopes: &[]string{"openid", "profile", "email"},
147 Attributes: &map[string]string{"use.refresh.tokens": "true",
148 "client_credentials.use_refresh_token": "true",
149 "x509.subjectdn": ".*client@mail.com.*",
150 "x509.allow.regex.pattern.comparison": "true"},
151 AuthenticationFlowBindingOverrides: &map[string]string{"direct_grant": flowId},
152 }
153
154 jwksString := pemtojwks.CreateJWKS("/certs/client_pub.key", "public", "/certs/client.crt")
155 newClient3 := gocloak.Client{
156 ClientID: gocloak.StringP(clientName),
157 Enabled: gocloak.BoolP(true),
158 DirectAccessGrantsEnabled: gocloak.BoolP(true),
159 BearerOnly: gocloak.BoolP(false),
160 PublicClient: gocloak.BoolP(false),
161 ServiceAccountsEnabled: gocloak.BoolP(true),
162 ClientAuthenticatorType: gocloak.StringP("client-jwt"),
163 DefaultClientScopes: &[]string{"email"},
164 Attributes: &map[string]string{"token.endpoint.auth.signing.alg": "RS256",
165 "use.jwks.string": "true",
166 "jwks.string": jwksString,
ktimoney90fcec92022-04-29 15:46:50 +0100167 "use.refresh.tokens": "true",
168 "client_credentials.use_refresh_token": "true",
ktimoney8ead72a2022-04-12 15:10:10 +0100169 },
170 }
171
172 var newClient gocloak.Client
173 if strings.HasPrefix(clientName, "x509") {
174 newClient = newClient2
175 } else if strings.HasPrefix(clientName, "jwt") {
176 newClient = newClient3
177 } else {
178 newClient = newClient1
179 }
180
ktimoney6be05292022-03-02 12:53:14 +0000181 clientId, err := client.CreateClient(ctx, token.AccessToken, realmName, newClient)
182 if err != nil {
ktimoney8ead72a2022-04-12 15:10:10 +0100183 fmt.Println("Failed to create client", err)
ktimoney6be05292022-03-02 12:53:14 +0000184 return "", err
185 } else {
186 fmt.Println("Created realm client", clientId)
187 }
188
189 newClientRole := gocloak.Role{
ktimoneyf27b5132022-03-07 16:48:47 +0000190 Name: gocloak.StringP(clientRoleName),
ktimoney6be05292022-03-02 12:53:14 +0000191 }
ktimoneyf27b5132022-03-07 16:48:47 +0000192 clientRoleName, err = client.CreateClientRole(ctx, token.AccessToken, realmName, clientId, newClientRole)
ktimoney6be05292022-03-02 12:53:14 +0000193 if err != nil {
194 return "", err
195 } else {
196 fmt.Println("Created client role", clientRoleName)
197 }
198
199 user, err := client.GetClientServiceAccount(ctx, token.AccessToken, realmName, clientId)
200 if err != nil {
201 fmt.Println(err)
202 panic("Oh no!, failed to get client user :(")
203 } else {
204 fmt.Println("Service Account user", *user.Username)
205 }
206
ktimoney8ead72a2022-04-12 15:10:10 +0100207 if strings.HasPrefix(clientName, "x509") {
208 newUser := gocloak.User{
209 ID: gocloak.StringP(realmName + "user"),
210 Username: gocloak.StringP(realmName + "user"),
211 Email: gocloak.StringP("client@mail.com"),
212 Enabled: gocloak.BoolP(true),
213 }
214
215 realmUser, err := client.CreateUser(ctx, token.AccessToken, realmName, newUser)
216 if err != nil {
217 fmt.Println(err)
218 panic("Oh no!, failed to create user :(")
219 } else {
220 fmt.Println("Created new user", realmUser)
221 }
222 }
223
ktimoney6be05292022-03-02 12:53:14 +0000224 clientRole, err := client.GetClientRole(ctx, token.AccessToken, realmName, clientId, clientRoleName)
225 if err != nil {
226 fmt.Println(err)
227 panic("Oh no!, failed to get client role :(")
228 } else {
229 fmt.Println("Retrieved client role", clientRoleName)
230 }
231
232 clientRoles := []gocloak.Role{*clientRole}
233 err = client.AddClientRoleToUser(ctx, token.AccessToken, realmName, clientId, *user.ID, clientRoles)
234 if err != nil {
235 fmt.Println(err)
236 panic("Oh no!, failed to add client role to user :(")
237 } else {
238 fmt.Printf("Added %s to %s\n", *clientRole.Name, *user.Username)
239 }
240
241 clientroleMapper := gocloak.ProtocolMapperRepresentation{
ktimoney8ead72a2022-04-12 15:10:10 +0100242 ID: gocloak.StringP("Client Role " + clientName + " Mapper"),
243 Name: gocloak.StringP("Client Role " + clientName + " Mapper"),
ktimoney6be05292022-03-02 12:53:14 +0000244 Protocol: gocloak.StringP("openid-connect"),
245 ProtocolMapper: gocloak.StringP("oidc-usermodel-client-role-mapper"),
246 Config: &map[string]string{
247 "access.token.claim": "true",
248 "aggregate.attrs": "",
249 "claim.name": "clientRole",
250 "id.token.claim": "true",
251 "jsonType.label": "String",
252 "multivalued": "true",
253 "userinfo.token.claim": "true",
ktimoneyf27b5132022-03-07 16:48:47 +0000254 "usermodel.clientRoleMapping.clientId": clientName,
ktimoney6be05292022-03-02 12:53:14 +0000255 },
256 }
257 _, err = client.CreateClientProtocolMapper(ctx, token.AccessToken, realmName, clientId, clientroleMapper)
258 if err != nil {
259 fmt.Println(err)
260 panic("Oh no!, failed to add client roleampper to client :(")
261 } else {
262 fmt.Println("Client rolemapper added to client")
263 }
264
ktimoney8ead72a2022-04-12 15:10:10 +0100265 if strings.HasPrefix(clientName, "x509") {
266 clientRole := *newClient.ClientID + "." + clientRoleName
267
268 clientroleMapper := gocloak.ProtocolMapperRepresentation{
269 ID: gocloak.StringP("Hardcoded " + clientName + " Mapper"),
270 Name: gocloak.StringP("Hardcoded " + clientName + " Mapper"),
271 Protocol: gocloak.StringP("openid-connect"),
272 ProtocolMapper: gocloak.StringP("oidc-hardcoded-role-mapper"),
273 Config: &map[string]string{
274 "role": clientRole,
275 },
276 }
277 _, err = client.CreateClientProtocolMapper(ctx, token.AccessToken, realmName, clientId, clientroleMapper)
278 if err != nil {
279 return "", err
280 } else {
281 fmt.Println("Created hardcoded-role-mapper for ", clientRole)
282 }
283 }
284
ktimoney6be05292022-03-02 12:53:14 +0000285 _, err = client.RegenerateClientSecret(ctx, token.AccessToken, realmName, clientId)
286 if err != nil {
287 return "", err
288 }
289
290 cred, err := client.GetClientSecret(ctx, token.AccessToken, realmName, clientId)
291 if err != nil {
292 return "", err
293 } else {
294 fmt.Println("Generated client secret", *cred.Value)
295 }
296
297 return *cred.Value, nil
298}
ktimoneyf27b5132022-03-07 16:48:47 +0000299
300func connectToK8s() *kubernetes.Clientset {
301 config, err := rest.InClusterConfig()
302 if err != nil {
303 fmt.Println("failed to create K8s config")
304 }
305
306 clientset, err := kubernetes.NewForConfig(config)
307 if err != nil {
308 fmt.Println("Failed to create K8s clientset")
309 }
310
311 return clientset
312}
313
314func createSecret(clientSecret, clientName, realmName, role, namespace string) {
ktimoney8ead72a2022-04-12 15:10:10 +0100315 secretName := role + "-secret"
ktimoneyf27b5132022-03-07 16:48:47 +0000316 clientset := connectToK8s()
317 secrets := clientset.CoreV1().Secrets(namespace)
318 secret := &corev1.Secret{
319 ObjectMeta: metav1.ObjectMeta{
320 Name: secretName,
321 Namespace: namespace,
322 Labels: map[string]string{
ktimoney8ead72a2022-04-12 15:10:10 +0100323 "app": secretName,
324 },
ktimoneyf27b5132022-03-07 16:48:47 +0000325 },
326 Type: "Opaque",
327 StringData: map[string]string{"client_secret": clientSecret, "client_id": clientName, "realm": realmName},
328 }
329
330 _, err := secrets.Create(context.TODO(), secret, metav1.CreateOptions{})
331 if err != nil {
332 fmt.Println("Failed to create K8s secret.", err)
333 }
334
335 fmt.Println("Created K8s secret successfully")
336}
337
338func remove(realmName, clientName string) {
339 adminClient := gocloak.NewClient("http://192.168.49.2:31560")
340 ctx := context.Background()
341 token, err := adminClient.LoginAdmin(ctx, "admin", "admin", "master")
342 if err != nil {
343 fmt.Println(err)
344 }
345
346 clients, err := adminClient.GetClients(ctx, token.AccessToken, realmName,
347 gocloak.GetClientsParams{
348 ClientID: gocloak.StringP(clientName),
349 },
350 )
351 if err != nil {
352 panic("List clients failed:" + err.Error())
353 }
354 for _, client := range clients {
355 err = adminClient.DeleteClient(ctx, token.AccessToken, realmName, *client.ID)
356 if err != nil {
357 fmt.Println(err)
358 } else {
359 fmt.Println("Deleted client ", clientName)
360 }
361 }
ktimoney8ead72a2022-04-12 15:10:10 +0100362
363 userName := realmName + "user"
364 users, err := adminClient.GetUsers(ctx, token.AccessToken, realmName,
365 gocloak.GetUsersParams{
366 Username: gocloak.StringP(userName),
367 })
368 if err != nil {
369 panic("List users failed:" + err.Error())
370 }
371 for _, user := range users {
372 err = adminClient.DeleteUser(ctx, token.AccessToken, realmName, *user.ID)
373 if err != nil {
374 fmt.Println(err)
375 } else {
376 fmt.Println("Deleted user ", userName)
377 }
378 }
379
ktimoneyf27b5132022-03-07 16:48:47 +0000380}
381
382func removeSecret(namespace, role string) {
383 clientset := connectToK8s()
ktimoney8ead72a2022-04-12 15:10:10 +0100384 secretName := role + "-secret"
ktimoneyf27b5132022-03-07 16:48:47 +0000385 secrets := clientset.CoreV1().Secrets(namespace)
386 err := secrets.Delete(context.TODO(), secretName, metav1.DeleteOptions{})
387 if err != nil {
388 fmt.Println(err)
389 } else {
390 fmt.Println("Deleted Secret", secretName)
391 }
392}