-
Notifications
You must be signed in to change notification settings - Fork 0
/
confadmin.go
80 lines (71 loc) · 2.31 KB
/
confadmin.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
// Package confadmin provides an authentication service for admins configured
// in server configuration.
package confadmin
import (
"context"
"net/http"
"strconv"
"strings"
"github.com/go-kivik/kivik/v3"
"github.com/go-kivik/kivik/v3/errors"
"github.com/go-kivik/kivikd/v3/authdb"
"github.com/go-kivik/kivikd/v3/conf"
)
type confadmin struct {
*conf.Conf
}
var _ authdb.UserStore = &confadmin{}
// New returns a new confadmin authentication service provider.
func New(c *conf.Conf) authdb.UserStore {
return &confadmin{c}
}
func (c *confadmin) Validate(ctx context.Context, username, password string) (*authdb.UserContext, error) {
derivedKey, salt, iterations, err := c.getKeySaltIter(username)
if err != nil {
if kivik.StatusCode(err) == http.StatusNotFound {
return nil, errors.Status(http.StatusUnauthorized, "unauthorized")
}
return nil, errors.Wrap(err, "unrecognized password hash")
}
if !authdb.ValidatePBKDF2(password, salt, derivedKey, iterations) {
return nil, errors.Status(http.StatusUnauthorized, "unauthorized")
}
return &authdb.UserContext{
Name: username,
Roles: []string{"_admin"},
Salt: salt,
}, nil
}
const hashPrefix = "-" + authdb.SchemePBKDF2 + "-"
func (c *confadmin) getKeySaltIter(username string) (key, salt string, iterations int, err error) {
confName := "admins." + username
if !c.IsSet(confName) {
return "", "", 0, errors.Status(http.StatusNotFound, "user not found")
}
hash := c.GetString(confName)
if !strings.HasPrefix(hash, hashPrefix) {
return "", "", 0, errors.New("unrecognized password scheme")
}
parts := strings.Split(strings.TrimPrefix(hash, hashPrefix), ",")
if len(parts) != 3 {
return "", "", 0, errors.New("unrecognized hash format")
}
if iterations, err = strconv.Atoi(parts[2]); err != nil {
return "", "", 0, errors.New("unrecognized has format")
}
return parts[0], parts[1], iterations, nil
}
func (c *confadmin) UserCtx(ctx context.Context, username string) (*authdb.UserContext, error) {
_, salt, _, err := c.getKeySaltIter(username)
if err != nil {
if kivik.StatusCode(err) == http.StatusNotFound {
return nil, errors.Status(http.StatusNotFound, "user does not exist")
}
return nil, errors.Wrap(err, "unrecognized password hash")
}
return &authdb.UserContext{
Name: username,
Roles: []string{"_admin"},
Salt: salt,
}, nil
}