forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
login.go
154 lines (134 loc) · 3.98 KB
/
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
143
144
145
146
147
148
149
150
151
152
153
154
package login
import (
"html/template"
"net/http"
"strings"
"github.com/golang/glog"
"github.com/openshift/origin/pkg/auth/authenticator"
"github.com/openshift/origin/pkg/auth/oauth/handlers"
"github.com/openshift/origin/pkg/auth/server/csrf"
)
type PasswordAuthenticator interface {
authenticator.Password
handlers.AuthenticationSuccessHandler
}
type LoginFormRenderer interface {
Render(form LoginForm, w http.ResponseWriter, req *http.Request)
}
type LoginForm struct {
Action string
Error string
Values LoginFormValues
}
type LoginFormValues struct {
Then string
CSRF string
Username string
Password string
}
type Login struct {
csrf csrf.CSRF
auth PasswordAuthenticator
render LoginFormRenderer
}
func NewLogin(csrf csrf.CSRF, auth PasswordAuthenticator, render LoginFormRenderer) *Login {
return &Login{
csrf: csrf,
auth: auth,
render: render,
}
}
// Install registers the login handler into a mux. It is expected that the
// provided prefix will serve all operations. Path MUST NOT end in a slash.
func (l *Login) Install(mux Mux, paths ...string) {
for _, path := range paths {
path = strings.TrimRight(path, "/")
mux.HandleFunc(path, l.ServeHTTP)
}
}
func (l *Login) ServeHTTP(w http.ResponseWriter, req *http.Request) {
switch req.Method {
case "GET":
l.handleLoginForm(w, req)
case "POST":
l.handleLogin(w, req)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
func (l *Login) handleLoginForm(w http.ResponseWriter, req *http.Request) {
uri, err := getBaseURL(req)
if err != nil {
glog.Errorf("Unable to generate base URL: %v", err)
http.Error(w, "Unable to determine URL", http.StatusInternalServerError)
return
}
form := LoginForm{
Action: uri.String(),
}
if then := req.URL.Query().Get("then"); then != "" {
// TODO: sanitize 'then'
form.Values.Then = then
}
switch req.URL.Query().Get("reason") {
case "":
break
case "user required":
form.Error = "Login is required. Please try again."
case "token expired":
form.Error = "Could not check CSRF token. Please try again."
case "access denied":
form.Error = "Invalid login or password. Please try again."
default:
form.Error = "An unknown error has occurred. Please try again."
}
csrf, err := l.csrf.Generate(w, req)
if err != nil {
glog.Errorf("Unable to generate CSRF token: %v", err)
}
form.Values.CSRF = csrf
l.render.Render(form, w, req)
}
func (l *Login) handleLogin(w http.ResponseWriter, req *http.Request) {
if ok, err := l.csrf.Check(req, req.FormValue("csrf")); !ok || err != nil {
glog.Errorf("Unable to check CSRF token: %v", err)
failed("token expired", w, req)
return
}
then := req.FormValue("then")
user, password := req.FormValue("username"), req.FormValue("password")
if user == "" {
failed("user required", w, req)
return
}
context, ok, err := l.auth.AuthenticatePassword(user, password)
if err != nil {
glog.Errorf("Unable to authenticate password: %v", err)
failed("unknown error", w, req)
return
}
if !ok {
failed("access denied", w, req)
return
}
l.auth.AuthenticationSucceeded(context, then, w, req)
}
var DefaultLoginFormRenderer = loginTemplateRenderer{}
type loginTemplateRenderer struct{}
func (r loginTemplateRenderer) Render(form LoginForm, w http.ResponseWriter, req *http.Request) {
w.Header().Add("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
if err := loginTemplate.Execute(w, form); err != nil {
glog.Errorf("Unable to render login template: %v", err)
}
}
var loginTemplate = template.Must(template.New("loginForm").Parse(`
{{ if .Error }}<div class="message">{{ .Error }}</div>{{ end }}
<form action="{{ .Action }}" method="POST">
<input type="hidden" name="then" value="{{ .Values.Then }}">
<input type="hidden" name="csrf" value="{{ .Values.CSRF }}">
<label>Login: <input type="text" name="username" value="{{ .Values.Username }}"></label>
<label>Password: <input type="password" name="password" value=""></label>
<input type="submit" value="Login">
</form>
`))