forked from kataras/iris
-
Notifications
You must be signed in to change notification settings - Fork 0
/
basicauth.go
132 lines (111 loc) · 3.42 KB
/
basicauth.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
// Package basicauth provides http basic authentication via middleware. See _examples/authentication/basicauth
package basicauth
// test file: ../../_examples/authentication/basicauth/main_test.go
import (
"encoding/base64"
"strconv"
"time"
"github.com/kataras/iris"
"github.com/kataras/iris/context"
)
type (
encodedUser struct {
HeaderValue string
Username string
logged bool
expires time.Time
}
encodedUsers []encodedUser
basicAuthMiddleware struct {
config Config
// these are filled from the config.Users map at the startup
auth encodedUsers
realmHeaderValue string
// The below can be removed but they are here because on the future we may add dynamic options for those two fields,
// it is a bit faster to check the b.$bool as well.
expireEnabled bool // if the config.Expires is a valid date, default is disabled.
askHandlerEnabled bool // if the config.OnAsk is not nil, defaults to false.
}
)
//
// New accepts basicauth.Config and returns a new Handler
// which will ask the client for basic auth (username, password),
// validate that and if valid continues to the next handler, otherwise
// throws a StatusUnauthorized http error code.
func New(c Config) context.Handler {
config := DefaultConfig()
if c.Realm != "" {
config.Realm = c.Realm
}
config.Users = c.Users
config.Expires = c.Expires
config.OnAsk = c.OnAsk
b := &basicAuthMiddleware{config: config}
b.init()
return b.Serve
}
// Default accepts only the users and returns a new Handler
// which will ask the client for basic auth (username, password),
// validate that and if valid continues to the next handler, otherwise
// throws a StatusUnauthorized http error code.
func Default(users map[string]string) context.Handler {
c := DefaultConfig()
c.Users = users
return New(c)
}
func (b *basicAuthMiddleware) init() {
// pass the encoded users from the user's config's Users value
b.auth = make(encodedUsers, 0, len(b.config.Users))
for k, v := range b.config.Users {
fullUser := k + ":" + v
header := "Basic " + base64.StdEncoding.EncodeToString([]byte(fullUser))
b.auth = append(b.auth, encodedUser{HeaderValue: header, Username: k, logged: false, expires: DefaultExpireTime})
}
// set the auth realm header's value
b.realmHeaderValue = "Basic realm=" + strconv.Quote(b.config.Realm)
b.expireEnabled = b.config.Expires > 0
b.askHandlerEnabled = b.config.OnAsk != nil
}
func (b *basicAuthMiddleware) findAuth(headerValue string) (auth *encodedUser, found bool) {
if len(headerValue) == 0 {
return
}
for _, user := range b.auth {
if user.HeaderValue == headerValue {
auth = &user
found = true
break
}
}
return
}
func (b *basicAuthMiddleware) askForCredentials(ctx context.Context) {
ctx.Header("WWW-Authenticate", b.realmHeaderValue)
ctx.StatusCode(iris.StatusUnauthorized)
if b.askHandlerEnabled {
b.config.OnAsk(ctx)
}
}
// Serve the actual middleware
func (b *basicAuthMiddleware) Serve(ctx context.Context) {
auth, found := b.findAuth(ctx.GetHeader("Authorization"))
if !found {
b.askForCredentials(ctx)
ctx.StopExecution()
return
// don't continue to the next handler
}
// all ok
if b.expireEnabled {
if auth.logged == false {
auth.expires = time.Now().Add(b.config.Expires)
auth.logged = true
}
if time.Now().After(auth.expires) {
b.askForCredentials(ctx) // ask for authentication again
ctx.StopExecution()
return
}
}
ctx.Next() // continue
}