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
|
package models
import (
"context"
"time"
"code.crute.us/mcrute/golib/db/mongodb"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
const userCol = "users"
type UserStore interface {
List(context.Context) ([]*User, error)
Get(context.Context, string) (*User, error) // Error on not found
Put(context.Context, *User) error
Delete(context.Context, *User) error
}
type AuthToken struct {
Kind string `json:"kind"`
Token string `json:"token"`
// Do not expose refresh tokens in JSON as they are long-lived tokens that
// are harder to invalidate and thus rather security sensitive.
RefreshToken string `json:"-"`
}
type User struct {
Username string `bson:"_id" json:"key_id"`
IsAdmin bool `json:"is_admin"`
IsService bool `json:"is_service"`
Keys map[string]*SessionKey `json:"keys,omitempty"` // kid -> key
AuthTokens map[string]*AuthToken `json:"auth_tokens,omitempty"` // kind -> token
Deleted *time.Time `json:"deleted,omitempty"`
}
// GCKeys garbage collects keys that are no longer valid
func (u *User) GCKeys() {
for k, v := range u.Keys {
if v.IsGarbage() {
delete(u.Keys, k)
}
}
}
// GetKey returns a key for a key ID. It will only return valid keys.
func (u *User) GetKey(kid string) *SessionKey {
if u.Keys != nil {
if k := u.Keys[kid]; k != nil && k.IsValid() {
return k
}
}
return nil
}
func (u *User) AddKey(k *SessionKey) {
if u.Keys == nil {
u.Keys = map[string]*SessionKey{}
}
u.Keys[k.KeyId] = k
}
func (u *User) AddToken(t *AuthToken) {
if u.AuthTokens == nil {
u.AuthTokens = map[string]*AuthToken{}
}
u.AuthTokens[t.Kind] = t
}
type MongoDbUserStore struct {
Db *mongodb.Mongo
// ReturnDeleted will allow all methods to return deleted items. By default
// items where the Deleted field is set will not be returned. This should
// be the common cast for most code using this store but in some Admin
// use-cases it would be useful to show deleted accounts.
ReturnDeleted bool
}
func (s *MongoDbUserStore) List(ctx context.Context) ([]*User, error) {
var out []*User
filter := bson.M{}
if !s.ReturnDeleted {
filter["deleted"] = primitive.Null{}
}
if err := s.Db.FindAllByFilter(ctx, userCol, filter, &out); err != nil {
return nil, err
}
return out, nil
}
func (s *MongoDbUserStore) Get(ctx context.Context, username string) (*User, error) {
var u User
filter := bson.M{"_id": username}
if !s.ReturnDeleted {
filter["deleted"] = primitive.Null{}
}
if err := s.Db.FindOneByFilter(ctx, userCol, filter, &u); err != nil {
return nil, err
}
return &u, nil
}
func (s *MongoDbUserStore) Put(ctx context.Context, u *User) error {
if err := s.Db.ReplaceOneById(ctx, userCol, u.Username, u); err != nil {
return err
}
return nil
}
func (s *MongoDbUserStore) Delete(ctx context.Context, u *User) error {
u, err := s.Get(ctx, u.Username)
if err != nil {
return err
}
now := time.Now()
u.Deleted = &now
return s.Put(ctx, u)
}
var _ UserStore = (*MongoDbUserStore)(nil)
|