-
Notifications
You must be signed in to change notification settings - Fork 0
/
standalone_user.go
143 lines (118 loc) · 3.99 KB
/
standalone_user.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
// Copyright (C) 2022 CYBERCRYPT
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package id
import (
"errors"
"github.com/cybercryptio/d1-lib/v2/crypto"
)
// Error returned if a user cannot be authenticated, e.g. if they provide a wrong password.
var ErrNotAuthenticated = errors.New("user not authenticated")
// User contains data about a user. Note: All fields need to be exported in order for gob to
// serialize them.
type User struct {
// Salt and password hash for the user's password.
SaltAndHash []byte
Scopes Scope
// A list of groups the user is a member of.
Groups map[string]struct{}
}
// SealedUser is an encrypted structure which contains data about a user.
type SealedUser struct {
// The user identifier.
UID string
Ciphertext []byte
WrappedKey []byte
}
var defaultPwdHasher = crypto.NewPasswordHasher()
// newUser creates a new user with a random password and the provided scopes.
func newUser(scopes ...Scope) (User, string, error) {
pwd, saltAndHash, err := defaultPwdHasher.GeneratePassword()
if err != nil {
return User{}, "", err
}
user := User{
SaltAndHash: saltAndHash,
Scopes: ScopeUnion(scopes...),
Groups: make(map[string]struct{}),
}
return user, pwd, nil
}
// authenticate authenticates the calling user with the provided password.
func (u *User) authenticate(password string) error {
if !defaultPwdHasher.Compare(password, u.SaltAndHash) {
return ErrNotAuthenticated
}
return nil
}
// changePassword authenticates the calling user with the provided password and generates a new
// password for the user. It updates the calling user in place and returns the new password.
func (u *User) changePassword(oldPassword string) (string, error) {
if err := u.authenticate(oldPassword); err != nil {
return "", err
}
newPwd, newSaltAndHash, err := defaultPwdHasher.GeneratePassword()
if err != nil {
return "", err
}
u.SaltAndHash = newSaltAndHash
return newPwd, nil
}
// seal encrypts the user.
func (u *User) seal(uid string, cryptor crypto.CryptorInterface) (SealedUser, error) {
wrappedKey, ciphertext, err := cryptor.Encrypt(u, uid)
if err != nil {
return SealedUser{}, err
}
return SealedUser{uid, ciphertext, wrappedKey}, nil
}
// addGroups appends the provided group IDs to the user's group list.
func (u *User) addGroups(gids ...string) {
for _, gid := range gids {
u.Groups[gid] = struct{}{}
}
}
// removeGroups removes the provided group IDs from the user's group list.
func (u *User) removeGroups(gids ...string) {
for _, gid := range gids {
delete(u.Groups, gid)
}
}
// containsGroups returns true if all provided group IDs are contained in the user's group list, and
// false otherwise.
func (u *User) containsGroups(gids ...string) bool {
for _, gid := range gids {
if _, exists := u.Groups[gid]; !exists {
return false
}
}
return true
}
// getGroups returns the set of group IDs contained in the user's group list.
func (u *User) getGroups() map[string]struct{} {
return u.Groups
}
// unseal decrypts the sealed user.
func (u *SealedUser) unseal(cryptor crypto.CryptorInterface) (User, error) {
plainUser := User{}
if err := cryptor.Decrypt(&plainUser, u.UID, u.WrappedKey, u.Ciphertext); err != nil {
return User{}, err
}
return plainUser, nil
}
// verify checks the integrity of the sealed user.
func (u *SealedUser) verify(cryptor crypto.CryptorInterface) bool {
_, err := u.unseal(cryptor)
return err == nil
}