-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathauth.go
175 lines (164 loc) · 4.6 KB
/
auth.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
package kitwalk
import (
"context"
"net/http"
"net/http/cookiejar"
"strings"
)
const (
contentTypeHead = "Content-Type"
contentTypeVal = "application/x-www-form-urlencoded"
)
// Auth is an interface for http.Client
type Auth interface {
LoginWith(client *http.Client) error
SetupWith(config Config) error
LoginAs(username string, password string) error
}
// User is an user belonging to the authentication destination
type User struct {
Username string
Password string
}
// SamlAuthenticator has Config and User. This struct implement Auth interface.
type SamlAuthenticator struct {
User *User
Config Config
ctx context.Context
}
func (c *SamlAuthenticator) auth(client *http.Client, resp *http.Response) error {
var (
err error
tmpResp = resp
)
// When Web Storage confirmation page appear, skip it.
if isContinueRequired(resp.Body) {
param := strings.NewReader(c.Config.ShibbolethPassConfirmationParams.Encode())
crReq, err := http.NewRequest(http.MethodPost, tmpResp.Request.URL.String(), param)
if err != nil {
return err
}
crReq = crReq.WithContext(c.ctx)
crReq.Header.Add(contentTypeHead, contentTypeVal)
tmpResp, err = client.Do(crReq)
if err != nil {
return err
}
defer tmpResp.Body.Close()
}
// Post auth info to auth page
param := strings.NewReader(c.Config.ShibbolethHiddenParams.Encode())
authReq, err := http.NewRequest(http.MethodPost, tmpResp.Request.URL.String(), param)
if err != nil {
return err
}
authReq = authReq.WithContext(c.ctx)
authReq.Header.Add(contentTypeHead, contentTypeVal)
authResp, err := client.Do(authReq)
if err != nil {
return err
}
defer authResp.Body.Close()
// Extract SAML response
actionURL, data, err := parseSamlResp(authResp.Body)
if err != nil {
return err
}
// Redirect to target resource, and respond with target resource.
param = strings.NewReader(data.Encode())
authResReq, err := http.NewRequest(http.MethodPost, actionURL, param)
if err != nil {
return err
}
authResReq = authResReq.WithContext(c.ctx)
authResReq.Header.Add(contentTypeHead, contentTypeVal)
authResult, err := client.Do(authResReq)
if err != nil {
return err
}
defer authResult.Body.Close()
if authResult.Request.URL.Host == DefaultAuthDomain {
return &ShibbolethAuthError{errMsg: "Try to auth, but return login page yet."}
}
return nil
}
// LoginWith works with given http.Client to auth.
// The client store cookie information to be used for next authentication.
func (c *SamlAuthenticator) LoginWith(client *http.Client) error {
if client == nil {
client = http.DefaultClient
}
if client.Jar == nil {
jar, err := cookiejar.New(nil)
if err != nil {
return err
}
client.Jar = jar
}
getReq, err := http.NewRequest(http.MethodGet, ShibbolethLoginURL, nil)
if err != nil {
return err
}
getReq = getReq.WithContext(c.ctx)
resp, err := client.Do(getReq)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.Request.URL.Host == c.Config.ShibbolethAuthDomain {
err = c.auth(client, resp)
return err
}
return nil
}
// SetupWith attach given configuration to authenticator.
func (c *SamlAuthenticator) SetupWith(config Config) error {
// Set auth info
if config.ShibbolethHiddenParams == nil && config.ShibbolethPassConfirmationParams == nil {
return &ConfigDoesNotExists{}
}
authParams := config.ShibbolethHiddenParams
if len(authParams.Get(config.ShibbolethUsernameKey)) == 0 {
authParams.Add(config.ShibbolethUsernameKey, c.User.Username)
} else {
authParams.Set(config.ShibbolethUsernameKey, c.User.Username)
}
if len(authParams.Get(config.ShibbolethPasswordKey)) == 0 {
authParams.Add(config.ShibbolethPasswordKey, c.User.Password)
} else {
authParams.Set(config.ShibbolethPasswordKey, c.User.Password)
}
c.Config = config
return nil
}
// LoginAs switch user to authenticate with
func (c *SamlAuthenticator) LoginAs(username string, password string) error {
if err := isValidUsername(username); err != nil {
return err
}
user := &User{Username: username, Password: password}
c.User = user
err := c.SetupWith(c.Config)
if err != nil {
return err
}
return nil
}
// NewAuthenticator create new authenticator with given auth information.
func NewAuthenticator(ctx context.Context, username string, password string) (Auth, error) {
if err := isValidUsername(username); err != nil {
return nil, err
}
user := &User{Username: username, Password: password}
defaultConfig := GetDefaultConfig()
authenticator := &SamlAuthenticator{
User: user,
Config: *defaultConfig,
ctx: ctx,
}
err := authenticator.SetupWith(*defaultConfig)
if err != nil {
return nil, err
}
return authenticator, nil
}