1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package confadmin
16
17 import (
18 "context"
19 "errors"
20 "fmt"
21 "net/http"
22 "strconv"
23 "strings"
24
25 "github.com/go-kivik/kivik/v4"
26 internal "github.com/go-kivik/kivik/v4/int/errors"
27 "github.com/go-kivik/kivik/v4/x/kivikd/authdb"
28 "github.com/go-kivik/kivik/v4/x/kivikd/conf"
29 )
30
31 type confadmin struct {
32 *conf.Conf
33 }
34
35 var _ authdb.UserStore = &confadmin{}
36
37
38 func New(c *conf.Conf) authdb.UserStore {
39 return &confadmin{c}
40 }
41
42 func (c *confadmin) Validate(_ context.Context, username, password string) (*authdb.UserContext, error) {
43 derivedKey, salt, iterations, err := c.getKeySaltIter(username)
44 if err != nil {
45 if kivik.HTTPStatus(err) == http.StatusNotFound {
46 return nil, &internal.Error{Status: http.StatusUnauthorized, Message: "unauthorized"}
47 }
48 return nil, fmt.Errorf("unrecognized password hash: %w", err)
49 }
50 if !authdb.ValidatePBKDF2(password, salt, derivedKey, iterations) {
51 return nil, &internal.Error{Status: http.StatusUnauthorized, Message: "unauthorized"}
52 }
53 return &authdb.UserContext{
54 Name: username,
55 Roles: []string{"_admin"},
56 Salt: salt,
57 }, nil
58 }
59
60 const hashPrefix = "-" + authdb.SchemePBKDF2 + "-"
61
62 func (c *confadmin) getKeySaltIter(username string) (key, salt string, iterations int, err error) {
63 confName := "admins." + username
64 if !c.IsSet(confName) {
65 return "", "", 0, &internal.Error{Status: http.StatusNotFound, Message: "user not found"}
66 }
67 hash := c.GetString(confName)
68 if !strings.HasPrefix(hash, hashPrefix) {
69 return "", "", 0, errors.New("unrecognized password scheme")
70 }
71 const partsWanted = 3
72 parts := strings.Split(strings.TrimPrefix(hash, hashPrefix), ",")
73 if len(parts) != partsWanted {
74 return "", "", 0, errors.New("unrecognized hash format")
75 }
76 if iterations, err = strconv.Atoi(parts[2]); err != nil {
77 return "", "", 0, errors.New("unrecognized has format")
78 }
79 return parts[0], parts[1], iterations, nil
80 }
81
82 func (c *confadmin) UserCtx(_ context.Context, username string) (*authdb.UserContext, error) {
83 _, salt, _, err := c.getKeySaltIter(username)
84 if err != nil {
85 if kivik.HTTPStatus(err) == http.StatusNotFound {
86 return nil, &internal.Error{Status: http.StatusNotFound, Message: "user does not exist"}
87 }
88 return nil, fmt.Errorf("unrecognized password hash: %w", err)
89 }
90 return &authdb.UserContext{
91 Name: username,
92 Roles: []string{"_admin"},
93 Salt: salt,
94 }, nil
95 }
96
View as plain text