forked from sensu/sensu-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
user_store.go
165 lines (138 loc) · 3.94 KB
/
user_store.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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package etcd
import (
"context"
"fmt"
"path"
"github.com/coreos/etcd/clientv3"
"github.com/gogo/protobuf/proto"
"github.com/sensu/sensu-go/backend/authentication/bcrypt"
"github.com/sensu/sensu-go/backend/store"
"github.com/sensu/sensu-go/types"
)
const usersPathPrefix = "users"
func getUserPath(id string) string {
return path.Join(store.Root, usersPathPrefix, id)
}
// GetUsersPath gets the path of the user store.
func GetUsersPath(ctx context.Context, id string) string {
return path.Join(store.Root, usersPathPrefix, id)
}
// AuthenticateUser authenticates a User by username and password.
func (s *Store) AuthenticateUser(ctx context.Context, username, password string) (*types.User, error) {
user, err := s.GetUser(ctx, username)
if user == nil {
return nil, fmt.Errorf("user %s does not exist", username)
} else if err != nil {
return nil, err
}
if user.Disabled {
return nil, fmt.Errorf("user %s is disabled", username)
}
ok := bcrypt.CheckPassword(user.Password, password)
if !ok {
return nil, fmt.Errorf("wrong password for user %s", username)
}
return user, nil
}
// CreateUser creates a new user
func (s *Store) CreateUser(u *types.User) error {
userBytes, err := proto.Marshal(u)
if err != nil {
return err
}
// We need to prepare a transaction to verify the version of the key
// corresponding to the user in etcd in order to ensure we only put the key
// if it does not exist
cmp := clientv3.Compare(clientv3.Version(getUserPath(u.Username)), "=", 0)
req := clientv3.OpPut(getUserPath(u.Username), string(userBytes))
res, err := s.client.Txn(context.TODO()).If(cmp).Then(req).Commit()
if err != nil {
return err
}
if !res.Succeeded {
return fmt.Errorf("user %s already exists", u.Username)
}
return nil
}
// DeleteUser deletes a User.
// NOTE:
// Store probably shouldn't be responsible for deleting the token;
// business logic.
func (s *Store) DeleteUser(ctx context.Context, user *types.User) error {
// Mark it as disabled
user.Disabled = true
// Marshal the user struct
userBytes, err := proto.Marshal(user)
if err != nil {
return err
}
// Construct the list of operations to make in the transaction
userKey := getUserPath(user.Username)
txn := s.client.Txn(ctx).
// Ensure that the key exists
If(clientv3.Compare(clientv3.CreateRevision(userKey), ">", 0)).
// If key exists, delete user & any access token from allow list
Then(
clientv3.OpPut(userKey, string(userBytes)),
clientv3.OpDelete(
getTokenPath(user.Username, ""),
clientv3.WithPrefix(),
),
)
res, serr := txn.Commit()
if serr != nil {
return err
}
if !res.Succeeded {
logger.
WithField("username", user.Username).
Info("given user was not already persisted")
}
return nil
}
// GetUser gets a User.
func (s *Store) GetUser(ctx context.Context, username string) (*types.User, error) {
resp, err := s.client.Get(ctx, getUserPath(username), clientv3.WithLimit(1))
if err != nil {
return nil, err
}
if len(resp.Kvs) != 1 {
return nil, nil
}
user := &types.User{}
err = unmarshal(resp.Kvs[0].Value, user)
if err != nil {
return nil, err
}
return user, nil
}
// GetUsers retrieves all enabled users
func (s *Store) GetUsers() ([]*types.User, error) {
allUsers, err := s.GetAllUsers(&store.SelectionPredicate{})
if err != nil {
return allUsers, err
}
var users []*types.User
for _, user := range allUsers {
// Verify that the user is not disabled
if !user.Disabled {
users = append(users, user)
}
}
return users, nil
}
// GetAllUsers retrieves all users
func (s *Store) GetAllUsers(pred *store.SelectionPredicate) ([]*types.User, error) {
users := []*types.User{}
err := List(context.Background(), s.client, GetUsersPath, &users, pred)
return users, err
}
// UpdateUser updates a User.
func (s *Store) UpdateUser(u *types.User) error {
bytes, err := proto.Marshal(u)
if err != nil {
return err
}
_, err = s.client.Put(context.TODO(), getUserPath(u.Username), string(bytes))
return err
}