-
Notifications
You must be signed in to change notification settings - Fork 5
/
userslogin.go
261 lines (214 loc) · 8.47 KB
/
userslogin.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
package illumioapi
import (
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"strconv"
"strings"
)
// ProductVersion represents the version of the product
type ProductVersion struct {
Build int `json:"build,omitempty"`
EngineeringInfo string `json:"engineering_info,omitempty"`
LongDisplay string `json:"long_display,omitempty"`
ReleaseInfo string `json:"release_info,omitempty"`
ShortDisplay string `json:"short_display,omitempty"`
Version string `json:"version,omitempty"`
}
// UserLogin represents a user logging in via password to get a session key
type UserLogin struct {
AuthUsername string `json:"auth_username,omitempty"`
FullName string `json:"full_name,omitempty"`
Href string `json:"href,omitempty"`
InactivityExpirationMinutes int `json:"inactivity_expiration_minutes,omitempty"`
LastLoginIPAddress string `json:"last_login_ip_address,omitempty"`
LastLoginOn string `json:"last_login_on,omitempty"`
ProductVersion *ProductVersion `json:"product_version,omitempty"`
SessionToken string `json:"session_token,omitempty"`
TimeZone string `json:"time_zone,omitempty"`
Type string `json:"type,omitempty"`
Orgs []*Org `json:"orgs,omitempty"`
Username string `json:"username,omitempty"` // Added for events
}
// Org is an an organization in a SaaS PCE
type Org struct {
Href string `json:"href"`
DisplayName string `json:"display_name"`
ID int `json:"org_id"`
}
// Authentication represents the response of the Authenticate API
type Authentication struct {
AuthToken string `json:"auth_token"`
}
// APIKey represents an API Key
type APIKey struct {
Href string `json:"href,omitempty"`
KeyID string `json:"key_id,omitempty"`
AuthUsername string `json:"auth_username,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Secret string `json:"secret,omitempty"`
}
// getAuthToken is a private method that produces a temporary auth token for a valid username (email) and password.
// The pce instance must have a FQDN and port
func (p *PCE) getAuthToken(username, password string) (Authentication, APIResponse, error) {
var api APIResponse
var err error
var auth Authentication
// Build the API URL
fqdn := pceSanitization(p.FQDN)
if strings.Contains(p.FQDN, "illum.io") && !strings.Contains(p.FQDN, "demo") {
fqdn = "login.illum.io"
}
if p.FQDN == "xpress1.ilabs.io" {
fqdn = "loginpce-dfdev1.ilabs.io"
}
// If there is an env variable set, use that
if os.Getenv("ILLUMIO_LOGIN_SERVER") != "" {
fqdn = os.Getenv("ILLUMIO_LOGIN_SERVER")
}
apiURL, err := url.Parse("https://" + fqdn + ":" + strconv.Itoa(p.Port) + "/api/v2/login_users/authenticate")
if err != nil {
return auth, api, fmt.Errorf("authenticate error - %s", err)
}
q := apiURL.Query()
q.Set("pce_fqdn", p.FQDN)
apiURL.RawQuery = q.Encode()
// Call the API - Use a PCE object since that's what apicall expects
api, err = apicall("POST", apiURL.String(), PCE{DisableTLSChecking: p.DisableTLSChecking, User: username, Key: password}, nil, false)
if err != nil {
return auth, api, fmt.Errorf("authenticate error - with login server %s - %s", fqdn, err)
}
// Marshal the response
json.Unmarshal([]byte(api.RespBody), &auth)
return auth, api, nil
}
// login is a private method that takes an auth token and returns UserLogin struct with a session token
// If the authToken is blank, the PCE struct must have API user and secret key and will get user information.
func (p *PCE) login(authToken string) (UserLogin, APIResponse, error) {
var login UserLogin
var response APIResponse
// Check if we have either authToken or API user and key
if authToken == "" && (p.User == "" || p.Key == "") {
return login, response, fmt.Errorf("either auth token or PCE User and Key must be provided")
}
// Build the API URL
apiURL, err := url.Parse("https://" + pceSanitization(p.FQDN) + ":" + strconv.Itoa(p.Port) + "/api/v2/users/login")
if err != nil {
return login, response, fmt.Errorf("login error - %s", err)
}
// Create HTTP client and request
client := &http.Client{}
if p.DisableTLSChecking {
client.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
}
req, err := http.NewRequest("GET", apiURL.String(), nil)
if err != nil {
return login, response, fmt.Errorf("login error - %s", err)
}
// Set headers
req.Header.Set("Content-Type", "application/json")
// If auth token is provided, set header. If auth token is empty, set user/key to receive org info
if authToken != "" {
req.Header.Set("Authorization", "Token token="+authToken)
} else {
req.SetBasicAuth(p.User, p.Key)
}
// Make HTTP Request
resp, err := client.Do(req)
if err != nil {
return login, response, fmt.Errorf("login error - %s", err)
}
// Process response
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return login, response, fmt.Errorf("login error - %s", err)
}
// Put relevant response info into struct
response.RespBody = string(data[:])
response.StatusCode = resp.StatusCode
response.Header = resp.Header
response.Request = resp.Request
// Check for a 200 response code
if strconv.Itoa(resp.StatusCode)[0:1] != "2" {
return login, response, errors.New("login error - http status code of " + strconv.Itoa(response.StatusCode))
}
// Put relevant response info into struct
json.Unmarshal(data, &login)
return login, response, nil
}
// Login authenticates to the PCE.
// Login will populate the User, Key, and Org fields in the PCE instance.
// Login will use a temporary session token that expires after 10 minutes.
// The ILLUMIO_LOGIN_SERVER environment variable can be used for specifying a login server
func (p *PCE) Login(user, password string) (UserLogin, []APIResponse, error) {
var apiResps []APIResponse
auth, a, err := p.getAuthToken(user, password)
apiResps = append(apiResps, a)
if err != nil {
return UserLogin{}, apiResps, fmt.Errorf("error - Authenticating to PCE - %s", err)
}
login, a, err := p.login(auth.AuthToken)
apiResps = append(apiResps, a)
if err != nil {
return login, apiResps, fmt.Errorf("error - Logging in to PCE - %s", err)
}
p.User = login.AuthUsername
p.Key = login.SessionToken
p.Org = login.Orgs[0].ID
return login, apiResps, nil
}
// LoginAPIKey authenticates to the PCE.
// Login will populate the User, Key, and Org fields in the PCE instance.
// LoginAPIKey will create a permanent API Key with the provided name and description fields.
// The ILLUMIO_LOGIN_SERVER environment variable can be used for specifying a login server.
func (p *PCE) LoginAPIKey(user, password, name, desc string) (UserLogin, []APIResponse, error) {
login, a, err := p.Login(user, password)
if err != nil {
return login, a, fmt.Errorf("LoginAPIKey - %s", err)
}
apiURL, err := url.Parse("https://" + p.FQDN + ":" + strconv.Itoa(p.Port) + "/api/v2/" + login.Href + "/api_keys")
if err != nil {
return login, a, fmt.Errorf("LoginAPIKey - %s", err)
}
// Create payload
postJSON, err := json.Marshal(APIKey{Name: name, Description: desc})
if err != nil {
return login, a, fmt.Errorf("LoginAPIKey - %s", err)
}
// Call the API
apiResp, err := apicall("POST", apiURL.String(), *p, postJSON, false)
if err != nil {
return login, append(a, apiResp), fmt.Errorf("LoginAPIKey - %s", err)
}
apiResp.ReqBody = string(postJSON)
// Marshal the response
var apiKey APIKey
json.Unmarshal([]byte(apiResp.RespBody), &apiKey)
p.User = apiKey.AuthUsername
p.Key = apiKey.Secret
return login, append(a, apiResp), nil
}
// GetAllAPIKeys gets all the APIKeys associated with a user
func (p *PCE) GetAllAPIKeys(userHref string) ([]APIKey, APIResponse, error) {
// Build the API URL
apiURL, err := url.Parse("https://" + p.FQDN + ":" + strconv.Itoa(p.Port) + "/api/v2" + userHref + "/api_keys")
if err != nil {
return []APIKey{}, APIResponse{}, fmt.Errorf("GetAllAPIKeys - %s", err)
}
// Call the API
apiResp, err := apicall("GET", apiURL.String(), *p, nil, false)
if err != nil {
return []APIKey{}, apiResp, fmt.Errorf("GetAllAPIKeys - %s", err)
}
// Marshal the response
var apiKeys []APIKey
json.Unmarshal([]byte(apiResp.RespBody), &apiKeys)
return apiKeys, apiResp, nil
}