...
1
2
3
4
5
6
7
8
9
10
11
12
13
14 package usersdb
15
16 import (
17 "context"
18 "crypto/sha1"
19 "errors"
20 "fmt"
21 "net/http"
22
23 "golang.org/x/crypto/pbkdf2"
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 )
29
30 type db struct {
31 *kivik.DB
32 }
33
34 var _ authdb.UserStore = &db{}
35
36
37 func New(userDB *kivik.DB) authdb.UserStore {
38 return &db{userDB}
39 }
40
41 type user struct {
42 Name string `json:"name"`
43 Roles []string `json:"roles"`
44 PasswordScheme string `json:"password_scheme,omitempty"`
45 Salt string `json:"salt,omitempty"`
46 Iterations int `json:"iterations,omitempty"`
47 DerivedKey string `json:"derived_key,omitempty"`
48 }
49
50 func (db *db) getUser(ctx context.Context, username string) (*user, error) {
51 var u user
52 if err := db.Get(ctx, kivik.UserPrefix+username, nil).ScanDoc(&u); err != nil {
53 return nil, err
54 }
55 return &u, nil
56 }
57
58 func (db *db) Validate(ctx context.Context, username, password string) (*authdb.UserContext, error) {
59 u, err := db.getUser(ctx, username)
60 if err != nil {
61 if kivik.HTTPStatus(err) == http.StatusNotFound {
62 err = &internal.Error{Status: http.StatusUnauthorized, Message: "unauthorized"}
63 }
64 return nil, err
65 }
66
67 switch u.PasswordScheme {
68 case "":
69 return nil, errors.New("no password scheme set for user")
70 case authdb.SchemePBKDF2:
71 default:
72 return nil, fmt.Errorf("unsupported password scheme: %s", u.PasswordScheme)
73 }
74 key := fmt.Sprintf("%x", pbkdf2.Key([]byte(password), []byte(u.Salt), u.Iterations, authdb.PBKDF2KeyLength, sha1.New))
75 if key != u.DerivedKey {
76 return nil, &internal.Error{Status: http.StatusUnauthorized, Message: "unauthorized"}
77 }
78 return &authdb.UserContext{
79 Name: u.Name,
80 Roles: u.Roles,
81 Salt: u.Salt,
82 }, nil
83 }
84
85 func (db *db) UserCtx(ctx context.Context, username string) (*authdb.UserContext, error) {
86 u, err := db.getUser(ctx, username)
87 if err != nil {
88 return nil, err
89 }
90 return &authdb.UserContext{
91 Name: u.Name,
92 Roles: u.Roles,
93 Salt: u.Salt,
94 }, nil
95 }
96
View as plain text