forked from michaelklishin/rabbit-hole
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathusers.go
212 lines (175 loc) · 6.09 KB
/
users.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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
package rabbithole
import (
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"encoding/json"
"math/rand"
"net/http"
"net/url"
)
// HashingAlgorithm represents a hashing algorithm used
// by RabbitMQ's an internal authentication backend.
type HashingAlgorithm string
func (algo HashingAlgorithm) String() string {
return string(algo)
}
const (
// HashingAlgorithmSHA256 sets password hashing algorithm to SHA-256.
HashingAlgorithmSHA256 HashingAlgorithm = "rabbit_password_hashing_sha256"
// HashingAlgorithmSHA512 sets password hashing algorithm to SHA-512.
HashingAlgorithmSHA512 HashingAlgorithm = "rabbit_password_hashing_sha512"
// HashingAlgorithmMD5 provided to support responses that include users created
// before RabbitMQ 3.6 and other legacy scenarios. This algorithm is
// deprecated.
HashingAlgorithmMD5 HashingAlgorithm = "rabbit_password_hashing_md5"
)
// UserInfo represents a user record. Only relevant when internal authentication
// backend is used.
type UserInfo struct {
Name string `json:"name"`
PasswordHash string `json:"password_hash"`
HashingAlgorithm HashingAlgorithm `json:"hashing_algorithm,omitempty"`
// Tags control permissions. Built-in tags: administrator, management, policymaker.
Tags string `json:"tags"`
}
// UserSettings represents properties of a user. Used to create users.
// Tags must be comma-separated.
type UserSettings struct {
Name string `json:"name"`
// Tags control permissions. Administrator grants full
// permissions, management grants management UI and HTTP API
// access, policymaker grants policy management permissions.
Tags string `json:"tags"`
// *never* returned by RabbitMQ. Set by the client
// to create/update a user. MK.
Password string `json:"password,omitempty"`
PasswordHash string `json:"password_hash,omitempty"`
HashingAlgorithm HashingAlgorithm `json:"hashing_algorithm,omitempty"`
}
//
// GET /api/users
//
// Example response:
// [{"name":"guest","password_hash":"8LYTIFbVUwi8HuV/dGlp2BYsD1I=","tags":"administrator"}]
// ListUsers returns a list of all users in a cluster.
func (c *Client) ListUsers() (rec []UserInfo, err error) {
req, err := newGETRequest(c, "users/")
if err != nil {
return []UserInfo{}, err
}
if err = executeAndParseRequest(c, req, &rec); err != nil {
return []UserInfo{}, err
}
return rec, nil
}
//
// GET /api/users/{name}
//
// GetUser returns information about individual user.
func (c *Client) GetUser(username string) (rec *UserInfo, err error) {
req, err := newGETRequest(c, "users/"+url.PathEscape(username))
if err != nil {
return nil, err
}
if err = executeAndParseRequest(c, req, &rec); err != nil {
return nil, err
}
return rec, nil
}
//
// PUT /api/users/{name}
//
// PutUser updates information about an individual user.
func (c *Client) PutUser(username string, info UserSettings) (res *http.Response, err error) {
body, err := json.Marshal(info)
if err != nil {
return nil, err
}
req, err := newRequestWithBody(c, "PUT", "users/"+url.PathEscape(username), body)
if err != nil {
return nil, err
}
if res, err = executeRequest(c, req); err != nil {
return nil, err
}
return res, nil
}
// PutUserWithoutPassword creates a passwordless user. Such users can only authenticate
// using an X.509 certificate or another authentication mechanism (or backend) that does not
// use passwords..
func (c *Client) PutUserWithoutPassword(username string, info UserSettings) (res *http.Response, err error) {
body, err := json.Marshal(UserInfo{Tags: info.Tags})
if err != nil {
return nil, err
}
req, err := newRequestWithBody(c, "PUT", "users/"+url.PathEscape(username), body)
if err != nil {
return nil, err
}
if res, err = executeRequest(c, req); err != nil {
return nil, err
}
return res, nil
}
//
// DELETE /api/users/{name}
//
// DeleteUser deletes a user by name.
func (c *Client) DeleteUser(username string) (res *http.Response, err error) {
req, err := newRequestWithBody(c, "DELETE", "users/"+url.PathEscape(username), nil)
if err != nil {
return nil, err
}
if res, err = executeRequest(c, req); err != nil {
return nil, err
}
return res, nil
}
//
// Password Hash generation
//
const characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
// GenerateSalt generates a password salt. Used to compute password hashes
// when creating or updating user information.
// See https://www.rabbitmq.com/passwords.html#computing-password-hash
// for details.
func GenerateSalt(n int) string {
bs := make([]byte, n)
for i := range bs {
bs[i] = characters[rand.Intn(len(characters))]
}
return string(bs)
}
// SaltedPasswordHashSHA256 is used to compute SHA-256 password hashes
// when creating or updating user information.
// See https://www.rabbitmq.com/passwords.html#computing-password-hash
// for details.
func SaltedPasswordHashSHA256(password string) (string, string) {
salt := GenerateSalt(4)
hashed := sha256.Sum256([]byte(salt + password))
return salt, string(hashed[:])
}
// Base64EncodedSaltedPasswordHashSHA256 produces a salted hash value expected by the HTTP API.
// See https://www.rabbitmq.com/passwords.html#computing-password-hash
// for details.
func Base64EncodedSaltedPasswordHashSHA256(password string) string {
salt, saltedHash := SaltedPasswordHashSHA256(password)
return base64.URLEncoding.EncodeToString([]byte(salt + saltedHash))
}
// SaltedPasswordHashSHA512 is used to compute SHA-512 password hashes
// when creating or updating user information.
// See https://www.rabbitmq.com/passwords.html#computing-password-hash
// for details.
func SaltedPasswordHashSHA512(password string) (string, string) {
salt := GenerateSalt(4)
hashed := sha512.Sum512([]byte(salt + password))
return salt, string(hashed[:])
}
// Base64EncodedSaltedPasswordHashSHA512 produces a salted hash value expected by the HTTP API.
// See https://www.rabbitmq.com/passwords.html#computing-password-hash
// for details.
func Base64EncodedSaltedPasswordHashSHA512(password string) string {
salt, saltedHash := SaltedPasswordHashSHA512(password)
return base64.URLEncoding.EncodeToString([]byte(salt + saltedHash))
}