forked from openshift/origin
/
basicauthpassword.go
106 lines (88 loc) · 2.8 KB
/
basicauthpassword.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
package basicauthpassword
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"github.com/golang/glog"
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user"
authapi "github.com/openshift/origin/pkg/auth/api"
"github.com/openshift/origin/pkg/auth/authenticator"
)
// Authenticator uses basic auth to make a request to a JSON-returning URL.
// A 401 status indicate failed auth.
// A non-200 status or the presence of an "error" key with a non-empty
// value indicates an error:
// {"error":"Error message"}
// A 200 status with an "id" key indicates success:
// {"id":"userid"}
// A successful response may also include name and/or email:
// {"id":"userid", "name": "User Name", "email":"user@example.com"}
type Authenticator struct {
url string
mapper authapi.UserIdentityMapper
}
// RemoteUserData holds user data returned from a remote basic-auth protected endpoint.
// These field names can not be changed unless external integrators are also updated.
type RemoteUserData struct {
ID string
Name string
Email string
}
// RemoteError holds error data returned from a remote authentication request
type RemoteError struct {
Error string
}
// New returns an authenticator which will make a basic auth call to the given url.
func New(url string, mapper authapi.UserIdentityMapper) authenticator.Password {
return &Authenticator{url, mapper}
}
func (a *Authenticator) AuthenticatePassword(username, password string) (user.Info, bool, error) {
req, err := http.NewRequest("GET", a.url, nil)
if err != nil {
return nil, false, err
}
req.SetBasicAuth(username, password)
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, false, err
}
if resp.StatusCode == http.StatusUnauthorized {
return nil, false, nil
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, false, err
}
remoteError := RemoteError{}
json.Unmarshal(body, &remoteError)
if remoteError.Error != "" {
return nil, false, errors.New(remoteError.Error)
}
if resp.StatusCode != http.StatusOK {
return nil, false, fmt.Errorf("An error occurred while authenticating (%d)", resp.StatusCode)
}
remoteUserData := RemoteUserData{}
err = json.Unmarshal(body, &remoteUserData)
if err != nil {
return nil, false, err
}
if len(remoteUserData.ID) == 0 {
return nil, false, errors.New("Could not retrieve user data")
}
identity := &authapi.DefaultUserIdentityInfo{
UserName: username,
Extra: map[string]string{
"name": remoteUserData.Name,
"email": remoteUserData.Email,
},
}
user, err := a.mapper.UserFor(identity)
glog.V(4).Infof("Got userIdentityMapping: %#v", user)
if err != nil {
return nil, false, fmt.Errorf("Error creating or updating mapping for: %#v due to %v", identity, err)
}
return user, true, nil
}