...
1
2
3
4
5
6
7
8
9
10
11
12
13 package auth
14
15 import (
16 "context"
17 "crypto/rand"
18 "math/big"
19 "net/http"
20 "sync"
21
22 internal "github.com/go-kivik/kivik/v4/int/errors"
23 )
24
25
26 type UserStore interface {
27
28
29
30 Validate(ctx context.Context, username, password string) (user *UserContext, err error)
31
32
33
34 UserCtx(ctx context.Context, username string) (user *UserContext, err error)
35 }
36
37
38 type MemoryUserStore struct {
39 users sync.Map
40 }
41
42 var _ UserStore = (*MemoryUserStore)(nil)
43
44 type memoryUser struct {
45 Salt string
46 Password string
47 Roles []string
48 }
49
50
51 func NewMemoryUserStore() *MemoryUserStore {
52 return &MemoryUserStore{}
53 }
54
55
56
57 func (s *MemoryUserStore) AddUser(username, password string, roles []string) error {
58 salt, err := generateSalt()
59 if err != nil {
60 return err
61 }
62 _, loaded := s.users.LoadOrStore(username, &memoryUser{
63 Salt: salt,
64 Password: password,
65 Roles: roles,
66 })
67 if loaded {
68 return &internal.Error{Status: http.StatusConflict, Message: "User already exists"}
69 }
70 return nil
71 }
72
73 func generateSalt() (string, error) {
74 const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
75 const n = 16
76 ret := make([]byte, n)
77 for i := 0; i < n; i++ {
78 num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
79 if err != nil {
80 return "", err
81 }
82 ret[i] = letters[num.Int64()]
83 }
84
85 return string(ret), nil
86 }
87
88
89 func (s *MemoryUserStore) DeleteUser(username string) {
90 s.users.Delete(username)
91 }
92
93 var (
94 errNotFound = &internal.Error{Status: http.StatusNotFound, Message: "User not found"}
95 errUnauthorized = &internal.Error{Status: http.StatusUnauthorized, Message: "Invalid username or password"}
96 )
97
98
99 func (s *MemoryUserStore) Validate(_ context.Context, username, password string) (*UserContext, error) {
100 user, ok := s.users.Load(username)
101 if !ok {
102 return nil, errNotFound
103 }
104 if user.(*memoryUser).Password != password {
105 return nil, errUnauthorized
106 }
107 return &UserContext{
108 Name: username,
109 Salt: user.(*memoryUser).Salt,
110 Roles: user.(*memoryUser).Roles,
111 }, nil
112 }
113
114
115 func (s *MemoryUserStore) UserCtx(_ context.Context, username string) (*UserContext, error) {
116 user, ok := s.users.Load(username)
117 if !ok {
118 return nil, errNotFound
119 }
120 return &UserContext{
121 Name: username,
122 Salt: user.(*memoryUser).Salt,
123 Roles: user.(*memoryUser).Roles,
124 }, nil
125 }
126
View as plain text