CI: Migrate Sonar Scan job to GHA
[nonrtric.git] / auth-token-fetch / main.go
1 // -
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
21 package main
22
23 import (
24         "crypto/tls"
25         "crypto/x509"
26         "encoding/json"
27         "io/ioutil"
28         "net/http"
29         "net/url"
30         "time"
31
32         "os"
33
34         log "github.com/sirupsen/logrus"
35 )
36
37 type JwtToken struct {
38         Access_token string
39         Expires_in   int
40         Token_type   string
41 }
42
43 type Context struct {
44         Running bool
45         Config  *Config
46 }
47
48 func NewContext(config *Config) *Context {
49         return &Context{
50                 Running: true,
51                 Config:  config,
52         }
53 }
54
55 // @title Auth token fetcher
56 // @version 1.1.0
57
58 // @license.name  Apache 2.0
59 // @license.url   http://www.apache.org/licenses/LICENSE-2.0.html
60
61 func main() {
62         configuration := NewConfig()
63         log.SetLevel(configuration.LogLevel)
64
65         log.Debug("Using configuration: ", configuration)
66         start(NewContext(configuration))
67
68         keepAlive()
69 }
70
71 func start(context *Context) {
72         log.Debug("Initializing")
73         if err := validateConfiguration(context.Config); err != nil {
74                 log.Fatalf("Stopping due to error: %v", err)
75         }
76
77         cert := loadCertificate(context.Config.CertPath, context.Config.KeyPath)
78         caCerts := loadCaCerts(context.Config.CACertsPath)
79
80         webClient := CreateHttpClient(cert, caCerts, 10*time.Second)
81
82         go periodicRefreshIwtToken(webClient, context)
83 }
84
85 func periodicRefreshIwtToken(webClient *http.Client, context *Context) {
86         for context.Running {
87                 jwtToken, err := fetchJwtToken(webClient, context.Config)
88                 if check(err) {
89                         saveAccessToken(jwtToken, context.Config)
90                 }
91                 delayTime := calcDelayTime(jwtToken, err, context.Config)
92                 log.WithFields(log.Fields{"seconds": delayTime.Seconds()}).Debug("Sleeping")
93                 time.Sleep(delayTime)
94         }
95 }
96
97 func calcDelayTime(token JwtToken, e error, confing *Config) time.Duration {
98         if e != nil {
99                 return time.Second * 1
100         }
101         remains := token.Expires_in - confing.RefreshMarginSeconds
102         if remains < 1 {
103                 remains = 1
104         }
105         return time.Second * time.Duration(remains)
106 }
107
108 func check(e error) bool {
109         if e != nil {
110                 log.Errorf("Failure reason: %v", e)
111                 return false
112         }
113         return true
114 }
115
116 func saveAccessToken(token JwtToken, configuration *Config) {
117         log.WithFields(log.Fields{"file": configuration.AuthTokenOutputFileName}).Debug("Saving access token")
118         data := []byte(token.Access_token)
119         err := os.WriteFile(configuration.AuthTokenOutputFileName, data, 0644)
120         check(err)
121 }
122
123 func fetchJwtToken(webClient *http.Client, configuration *Config) (JwtToken, error) {
124         log.WithFields(log.Fields{"url": configuration.AuthServiceUrl}).Debug("Fetching token")
125         var jwt JwtToken
126         var err error
127         resp, err := webClient.PostForm(configuration.AuthServiceUrl,
128                 url.Values{"client_secret": {configuration.ClientSecret}, "grant_type": {configuration.GrantType}, "client_id": {configuration.ClientId}})
129
130         if check(err) {
131                 var body []byte
132                 defer resp.Body.Close()
133                 body, err = ioutil.ReadAll(resp.Body)
134                 if check(err) {
135                         err = json.Unmarshal([]byte(body), &jwt)
136                 }
137         }
138         return jwt, err
139 }
140
141 func loadCertificate(certPath string, keyPath string) tls.Certificate {
142         log.WithFields(log.Fields{"certPath": certPath, "keyPath": keyPath}).Debug("Loading cert")
143         cert, err := tls.LoadX509KeyPair(certPath, keyPath)
144         if check(err) {
145                 return cert
146         } else {
147                 log.Fatalf("cannot create x509 keypair from cert file %s and key file %s due to: %v", certPath, keyPath, err)
148                 return tls.Certificate{}
149         }
150 }
151
152 func loadCaCerts(caCertsPath string) *x509.CertPool {
153         var err error
154         if caCertsPath == "" {
155                 return nil
156         }
157         caCert, err := ioutil.ReadFile(caCertsPath)
158         check(err)
159         caCertPool := x509.NewCertPool()
160         caCertPool.AppendCertsFromPEM(caCert)
161         return caCertPool
162 }
163
164 func keepAlive() {
165         channel := make(chan int)
166         <-channel
167 }