-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathauthenticator_basic.go
126 lines (106 loc) · 3.4 KB
/
authenticator_basic.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
package diecast
import (
"encoding/base64"
"fmt"
"net/http"
"net/url"
"github.com/ghetzel/go-stockutil/log"
"github.com/ghetzel/go-stockutil/pathutil"
"github.com/ghetzel/go-stockutil/sliceutil"
"github.com/ghetzel/go-stockutil/stringutil"
"github.com/ghetzel/go-stockutil/typeutil"
htpasswd "github.com/tg123/go-htpasswd"
)
type BasicAuthenticator struct {
config *AuthenticatorConfig
htpasswd []*htpasswd.File
credentials map[string]interface{}
realm string
}
func NewBasicAuthenticator(config *AuthenticatorConfig) (*BasicAuthenticator, error) {
var auth = &BasicAuthenticator{
config: config,
realm: config.O(`realm`, fmt.Sprintf("diecast/%v", ApplicationVersion)).String(),
}
var htpasswds = sliceutil.Stringify(sliceutil.Compact(config.O(`htpasswd`).Value))
if len(htpasswds) > 0 {
for _, filename := range htpasswds {
if ex, err := pathutil.ExpandUser(filename); err == nil {
if err := auth.AddPasswdFile(ex); err != nil {
return nil, err
}
} else {
return nil, err
}
}
}
auth.credentials = config.O(`credentials`).MapNative()
if len(auth.htpasswd) == 0 && len(auth.credentials) == 0 {
return nil, fmt.Errorf("Must specify at least one user database via the 'htpasswd' option")
} else {
return auth, nil
}
}
func (self *BasicAuthenticator) Name() string {
if self.config != nil && self.config.Name != `` {
return self.config.Name
} else {
return `BasicAuthenticator`
}
}
func (self *BasicAuthenticator) AddPasswdFile(filename string) error {
if htp, err := htpasswd.New(filename, htpasswd.DefaultSystems, func(err error) {
log.Warningf("BasicAuthenticator: %v", err)
}); err == nil {
self.htpasswd = append(self.htpasswd, htp)
return nil
} else {
return err
}
}
func (self *BasicAuthenticator) IsCallback(_ *url.URL) bool {
return false
}
func (self *BasicAuthenticator) Callback(w http.ResponseWriter, req *http.Request) {
}
func (self *BasicAuthenticator) Authenticate(w http.ResponseWriter, req *http.Request) bool {
if _, uppair := stringutil.SplitPair(req.Header.Get("Authorization"), ` `); uppair != `` {
if decoded, err := base64.StdEncoding.DecodeString(uppair); err == nil {
username, password := stringutil.SplitPair(string(decoded), `:`)
// match against any loaded htpasswd files
for _, htp := range self.htpasswd {
if htp.Match(username, password) {
return true
}
}
// match against statically-configured user:passhash pairs
for authUser, passhash := range self.credentials {
if username == authUser {
var ph = typeutil.String(passhash)
if enc, err := htpasswd.AcceptBcrypt(ph); err == nil && enc != nil {
return enc.MatchesPassword(password)
} else if enc, err := htpasswd.AcceptMd5(ph); err == nil && enc != nil {
return enc.MatchesPassword(password)
} else if enc, err := htpasswd.AcceptSha(ph); err == nil && enc != nil {
return enc.MatchesPassword(password)
} else if enc, err := htpasswd.AcceptSsha(ph); err == nil && enc != nil {
return enc.MatchesPassword(password)
}
}
}
} else {
log.Warningf("malformed authorization header")
}
w.WriteHeader(http.StatusForbidden)
w.Write([]byte(`Authorization Failed`))
return false
} else {
var wwwauth = `Basic`
if self.realm != `` {
wwwauth += ` realm=` + self.realm
}
w.Header().Set(`WWW-Authenticate`, wwwauth)
w.WriteHeader(http.StatusUnauthorized)
return false
}
}