forked from globalsign/hvclient
-
Notifications
You must be signed in to change notification settings - Fork 0
/
client_login.go
142 lines (115 loc) · 3.85 KB
/
client_login.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
Copyright (c) 2019-2021 GMO GlobalSign Pte. Ltd.
Licensed under the MIT License (the "License"); you may not use this file except
in compliance with the License. You may obtain a copy of the License at
https://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package hvclient
import (
"context"
"fmt"
"net/http"
"time"
)
// loginRequest is an HVCA POST /login request body.
type loginRequest struct {
APIKey string `json:"api_key"`
APISecret string `json:"api_secret"`
}
// loginResponse is an HVCA POST /login response body.
type loginResponse struct {
AccessToken string `json:"access_token"`
}
const (
// tokenLifetime is the assumed lifetime of an HVCA authentication token.
// The HVCA API appears to not return any information confirming the
// lifetime of the token, but at the time of writing the API documentation
// states it to be 10 minutes. We here set it to nine minutes just to
// leave some headroom.
tokenLifetime = time.Minute * 9
)
// HVCA API endpoints.
const (
endpointLogin = "/login"
)
// login logs into the HVCA server and stores the authentication token.
func (c *Client) login(ctx context.Context) error {
var req = loginRequest{
APIKey: c.Config.APIKey,
APISecret: c.Config.APISecret,
}
var resp loginResponse
var _, err = c.makeRequest(
ctx,
endpointLogin,
http.MethodPost,
req,
&resp,
)
if err != nil {
c.tokenReset()
return fmt.Errorf("failed to login: %w", err)
}
c.SetToken(resp.AccessToken)
return nil
}
// loginIfTokenHasExpired logs in if the stored authentication token has
// expired, or if there is no stored authentication token. To avoid
// unnecessary simultaneous re-logins, this method ensures only one goroutine
// at a time can perform a re-login operation via this method.
func (c *Client) loginIfTokenHasExpired(ctx context.Context) error {
// Do nothing if the token is not yet believed to be expired.
if !c.tokenHasExpired() {
return nil
}
// Token is believed to be expired, so lock the login mutex to ensure only
// one goroutine at a time can relogin. Note that it is perfectly safe for
// one goroutine to call login (which doesn't acquire the login mutex) while
// another calls this method (which does acquire it) - it's just somewhat
// inefficient. Also note that access to the token is sychronized using
// a different mutex, so attempting to acquire that mutex while holding
// this one won't cause a deadlock.
c.LoginMtx.Lock()
defer c.LoginMtx.Unlock()
// Check again if the token is believed to be expired, as another
// goroutine may have acquired the login mutex before we did.
if !c.tokenHasExpired() {
return nil
}
return c.login(ctx)
}
// tokenHasExpired returns true if the stored authentication token is believed
// to be expired (or if there is no stored authentication token), indicating
// that another login is required.
func (c *Client) tokenHasExpired() bool {
c.TokenMtx.RLock()
defer c.TokenMtx.RUnlock()
return time.Since(c.LastLogin) > tokenLifetime
}
// tokenReset clears the stored authentication token and the last login time.
func (c *Client) tokenReset() {
c.TokenMtx.Lock()
defer c.TokenMtx.Unlock()
c.Token = ""
c.LastLogin = time.Time{}
}
// SetToken sets the stored authentication token and sets the last login time
// to the current time.
func (c *Client) SetToken(token string) {
c.TokenMtx.Lock()
defer c.TokenMtx.Unlock()
c.Token = token
c.LastLogin = time.Now()
}
// GetToken performs a synchronized read of the stored authentication token.
func (c *Client) GetToken() string {
c.TokenMtx.RLock()
defer c.TokenMtx.RUnlock()
return c.Token
}
//