-
Notifications
You must be signed in to change notification settings - Fork 12
/
user.go
213 lines (168 loc) · 5.46 KB
/
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
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
213
/*
* Copyright 2017 Kopano and its licensors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package identifier
import (
"context"
"errors"
"time"
"github.com/dgrijalva/jwt-go"
"stash.kopano.io/kc/konnect"
"stash.kopano.io/kc/konnect/identifier/backends"
"stash.kopano.io/kc/konnect/identity"
)
// A IdentifiedUser is a user with meta data.
type IdentifiedUser struct {
sub string
backend backends.Backend
username string
email string
emailVerified bool
displayName string
familyName string
givenName string
id int64
uid string
sessionRef *string
claims map[string]interface{}
logonAt time.Time
}
// Subject returns the associated users subject field. The subject is the main
// authentication identifier of the user.
func (u *IdentifiedUser) Subject() string {
return u.sub
}
// Email returns the associated users email field.
func (u *IdentifiedUser) Email() string {
return u.email
}
// EmailVerified returns trye if the associated users email field was verified.
func (u *IdentifiedUser) EmailVerified() bool {
return u.emailVerified
}
// Name returns the associated users name field. This is the display name of
// the accociated user.
func (u *IdentifiedUser) Name() string {
return u.displayName
}
// FamilyName returns the associated users family name field.
func (u *IdentifiedUser) FamilyName() string {
return u.familyName
}
// GivenName returns the associated users given name field.
func (u *IdentifiedUser) GivenName() string {
return u.givenName
}
// ID returns the associated users numeric user id. If it is 0, it means that
// this user does not have a numeric ID. Do not use this field to identify a
// user - always use the subject instead. The numeric ID is kept for compatibilty
// with systems which require user identification to be numeric.
func (u *IdentifiedUser) ID() int64 {
return u.id
}
// UniqueID returns the accociated users unique user id. When empty, then this
// user does not have a unique ID. This field can be used for unique user mapping
// to external systems which use the same authentication source as Konnect. The
// value depends entirely on the identifier backend.
func (u *IdentifiedUser) UniqueID() string {
return u.uid
}
// Username returns the accociated users username. This might be different or
// the same as the subject, depending on the backend in use. If can also be
// empty, which means that the accociated user does not have a username.
func (u *IdentifiedUser) Username() string {
return u.username
}
// Claims returns extra claims of the accociated user.
func (u *IdentifiedUser) Claims() jwt.MapClaims {
claims := make(map[string]interface{})
claims[konnect.IdentifiedUsernameClaim] = u.Username()
claims[konnect.IdentifiedDisplayNameClaim] = u.Name()
for k, v := range u.claims {
claims[k] = v
}
return jwt.MapClaims(claims)
}
// ScopedClaims returns scope bound extra claims of the accociated user.
func (u *IdentifiedUser) ScopedClaims(authorizedScopes map[string]bool) jwt.MapClaims {
if u.backend == nil {
return nil
}
claims := u.backend.UserClaims(u.Subject(), authorizedScopes)
return jwt.MapClaims(claims)
}
// LoggedOn returns true if the accociated user has a logonAt time set.
func (u *IdentifiedUser) LoggedOn() (bool, time.Time) {
return !u.logonAt.IsZero(), u.logonAt
}
// SessionRef returns the accociated users underlaying session reference.
func (u *IdentifiedUser) SessionRef() *string {
return u.sessionRef
}
// BackendName returns the accociated users underlaying backend name.
func (u *IdentifiedUser) BackendName() string {
return u.backend.Name()
}
func (i *Identifier) logonUser(ctx context.Context, audience, username, password string) (*IdentifiedUser, error) {
success, subject, sessionRef, claims, err := i.backend.Logon(ctx, audience, username, password)
if err != nil {
return nil, err
}
if !success {
return nil, nil
}
user := &IdentifiedUser{
sub: *subject,
username: username,
backend: i.backend,
sessionRef: sessionRef,
claims: claims,
}
return user, nil
}
func (i *Identifier) resolveUser(ctx context.Context, username string) (*IdentifiedUser, error) {
u, err := i.backend.ResolveUserByUsername(ctx, username)
if err != nil {
return nil, err
}
// Construct user from resolved result.
user := &IdentifiedUser{
sub: u.Subject(),
username: u.Username(),
backend: i.backend,
claims: u.BackendClaims(),
}
return user, nil
}
func (i *Identifier) updateUser(ctx context.Context, user *IdentifiedUser) error {
var userID string
identityClaims := user.Claims()
if userIDString, ok := identityClaims[konnect.IdentifiedUserIDClaim]; ok {
userID = userIDString.(string)
}
if userID == "" {
return errors.New("no id claim in user identity claims")
}
u, err := i.backend.GetUser(ctx, userID, user.sessionRef)
if err != nil {
return err
}
if uwp, ok := u.(identity.UserWithProfile); ok {
user.displayName = uwp.Name()
}
user.backend = i.backend
return nil
}