1package web
2
3import (
4 "context"
5 "errors"
6 "strconv"
7 "time"
8)
9
10const (
11 AdminUsersPerPage = 20
12 AdminMembersPerPage = 25
13 AdminGroupsPerPage = 25
14 AdminInvitationsPerPage = 25
15 AdminListBadges = 5
16 AdminDetailBadges = 10
17)
18
19var ErrAdminNotFound = errors.New("web: no such user, group or invitation")
20
21var (
22 ErrAdminConfirmCode = errors.New("web: the confirmation code does not match")
23 ErrAdminConfirmSpent = errors.New("web: too many wrong confirmation codes")
24)
25
26type AdminPendingAccount struct {
27 Username string
28 Spent bool
29 ExpiresAt time.Time
30}
31
32type AdminProblem int
33
34const (
35 AdminUsernameGrammar AdminProblem = iota + 1
36 AdminUsernameTaken
37 AdminDisplayNameInvalid
38 AdminGroupNameGrammar
39 AdminGroupNameTaken
40 AdminDescriptionInvalid
41 AdminUnknownUser
42 AdminUnknownGroup
43 AdminAlreadyMember
44 AdminNotMember
45 AdminPrimaryGroup
46 AdminSelf
47 AdminLastAdmin
48 AdminReferencedGroup
49 AdminInvitationClosed
50 AdminGoogleNotLinked
51 AdminResetLinkDisabled
52 AdminResetLinkPending
53 AdminLabelInvalid
54 AdminExpiryInvalid
55 AdminAdminGroupGrant
56 AdminBatchSize
57 AdminUserPending
58 AdminUnknownBatch
59 AdminSSHKeyDisabled
60 AdminSSHKeyPending
61)
62
63var adminProblemNames = map[AdminProblem]string{
64 AdminUsernameGrammar: "the username does not follow the username grammar",
65 AdminUsernameTaken: "the username is taken",
66 AdminDisplayNameInvalid: "the display name is not valid",
67 AdminGroupNameGrammar: "the group name does not follow the group name grammar",
68 AdminGroupNameTaken: "the group name is taken",
69 AdminDescriptionInvalid: "the description is not valid",
70 AdminUnknownUser: "no user has that username",
71 AdminUnknownGroup: "no group has that name",
72 AdminAlreadyMember: "the user is already a member of the group",
73 AdminNotMember: "the user is not a member of the group",
74 AdminPrimaryGroup: "the primary group's membership cannot be changed",
75 AdminSelf: "an administrator cannot do this to their own account",
76 AdminLastAdmin: "the admin group would have no enabled member",
77 AdminReferencedGroup: "the group is named in the configuration",
78 AdminInvitationClosed: "the invitation is no longer pending",
79 AdminGoogleNotLinked: "the user has no linked Google account",
80 AdminResetLinkDisabled: "a reset link cannot be issued to a disabled account",
81 AdminResetLinkPending: "a reset link cannot be issued to an account waiting for its confirmation",
82 AdminLabelInvalid: "a label is empty or not valid",
83 AdminExpiryInvalid: "the expiry is not one of the offered choices",
84 AdminAdminGroupGrant: "an invitation cannot grant the admin group",
85 AdminBatchSize: "the batch is empty or larger than the cap",
86 AdminUserPending: "the account is waiting for its confirmation",
87 AdminUnknownBatch: "no invitation batch has that id",
88 AdminSSHKeyDisabled: "a disabled account takes no new SSH key",
89 AdminSSHKeyPending: "an account waiting for its confirmation takes no SSH key",
90}
91
92func (p AdminProblem) Error() string {
93 if s, ok := adminProblemNames[p]; ok {
94 return "web: admin: " + s
95 }
96 return "web: unknown admin problem " + strconv.Itoa(int(p))
97}
98
99type AdminAuditEntry struct {
100 At time.Time
101 Actor string
102 Action string
103 Subject string
104 NewSession bool
105}
106
107type AdminDashboard struct {
108 Users int
109 DisabledUsers int
110 Groups int
111 PendingInvitations int
112 LiveSessions int
113 Recent []AdminAuditEntry
114}
115
116type AdminUserRow struct {
117 ID string
118 Username string
119 DisplayName string
120 Disabled bool
121 Pending bool
122 Groups []string
123 GroupCount int
124}
125
126type AdminUserList struct {
127 Users []AdminUserRow
128 Total int
129}
130
131type AdminGroupRef struct {
132 ID string
133 Name string
134 Primary bool
135}
136
137type AdminGoogleLink struct {
138 LinkedAt time.Time
139}
140
141type AdminUser struct {
142 ID string
143 Username string
144 DisplayName string
145 UIDNumber int
146 Disabled bool
147 Pending bool
148 Admin bool
149 CreatedAt time.Time
150 LastLoginAt *time.Time
151 Groups []AdminGroupRef
152 GroupCount int
153 Google *AdminGoogleLink
154 Sessions int
155 RefreshTokens int
156 Activity []AdminAuditEntry
157 SSHKeys []SSHKey
158}
159
160type AdminMemberships struct {
161 Username string
162 Groups []AdminGroupRef
163 Total int
164 Candidates []string
165}
166
167type AdminGroupRow struct {
168 ID string
169 Name string
170 Description string
171 Members int
172}
173
174type AdminGroupList struct {
175 Groups []AdminGroupRow
176 Total int
177}
178
179type AdminMember struct {
180 ID string
181 Username string
182 DisplayName string
183 Pending bool
184}
185
186type AdminGroup struct {
187 ID string
188 Name string
189 Description string
190 GIDNumber int
191 Primary bool
192 Referenced bool
193 Members []AdminMember
194 Total int
195 Disabled []AdminMember
196 DisabledTotal int
197}
198
199type AdminInvitationState int
200
201const (
202 AdminInvitationPending AdminInvitationState = iota + 1
203 AdminInvitationUsed
204 AdminInvitationRevoked
205 AdminInvitationExpired
206)
207
208type AdminInvitation struct {
209 ID string
210 CreatedBy string
211 CreatedAt time.Time
212 ExpiresAt time.Time
213 State AdminInvitationState
214 Label string
215 UsedBy string
216 UsedByID string
217 UsedByPending bool
218 UsedByDeleted bool
219 Batch string
220 Groups []AdminInvitationGroup
221}
222
223type AdminInvitationGroup struct {
224 Name string
225 Deleted bool
226}
227
228type AdminInvitationList struct {
229 Invitations []AdminInvitation
230 Total int
231}
232
233var AdminInvitationExpiries = []time.Duration{24 * time.Hour, 7 * 24 * time.Hour, 30 * 24 * time.Hour}
234
235const AdminInvitationExpiryDefault = 7 * 24 * time.Hour
236
237type AdminNewInvitations struct {
238 Labels []string
239 GroupIDs []string
240 GroupNames []string
241 Expiry time.Duration
242}
243
244type AdminIssuedInvitation struct {
245 Label string
246 URL string
247}
248
249type AdminCreatedInvitations struct {
250 Invitations []AdminIssuedInvitation
251 ExpiresAt time.Time
252 Groups []string
253}
254
255type AdminGroupChoice struct {
256 ID string
257 Name string
258}
259
260type AdminGroupChoices struct {
261 Groups []AdminGroupChoice
262 More bool
263 Defaults []string
264}
265
266type AdminNewUser struct {
267 Username string
268 DisplayName string
269}
270
271type AdminProfile struct {
272 DisplayName string
273}
274
275type AdminNewGroup struct {
276 Name string
277 Description string
278}
279
280type AdminBackend interface {
281 Dashboard(ctx context.Context) (AdminDashboard, error)
282 ListUsers(ctx context.Context, query string, page int) (AdminUserList, error)
283 User(ctx context.Context, id string) (AdminUser, error)
284 CreateUser(ctx context.Context, actor string, u AdminNewUser) (string, error)
285 UpdateProfile(ctx context.Context, actor, id string, p AdminProfile) error
286 DisableUser(ctx context.Context, actor, id string) error
287 EnableUser(ctx context.Context, actor, id string) error
288 RevokeAllSessions(ctx context.Context, actor, id string) error
289 UnlinkGoogle(ctx context.Context, actor, id string) error
290 DeleteUser(ctx context.Context, actor, id string) error
291 Memberships(ctx context.Context, userID string, page int) (AdminMemberships, error)
292 AddMembership(ctx context.Context, actor, userID, groupName string) error
293 AddMember(ctx context.Context, actor, groupID, username string) error
294 RemoveMembership(ctx context.Context, actor, userID, groupID string) error
295 ListGroups(ctx context.Context, page int) (AdminGroupList, error)
296 Group(ctx context.Context, id string, page int) (AdminGroup, error)
297 GroupCandidates(ctx context.Context, groupID, query string) ([]AdminMember, error)
298 CreateGroup(ctx context.Context, actor string, g AdminNewGroup) (string, error)
299 DescribeGroup(ctx context.Context, actor, id, description string) error
300 DeleteGroup(ctx context.Context, actor, id string) error
301 ListInvitations(ctx context.Context, page int) (AdminInvitationList, error)
302 Invitation(ctx context.Context, id string) (AdminInvitation, error)
303 CreateInvitations(ctx context.Context, actor string, n AdminNewInvitations) (AdminCreatedInvitations, error)
304 InvitationGroupChoices(ctx context.Context) (AdminGroupChoices, error)
305 PendingAccount(ctx context.Context, id string) (AdminPendingAccount, error)
306 ConfirmAccount(ctx context.Context, actor, id, code string) error
307 RejectAccount(ctx context.Context, actor, id string) error
308 RevokeInvitation(ctx context.Context, actor, id string) error
309 RevokeInvitationBatch(ctx context.Context, actor, batch string) error
310 IssueResetLink(ctx context.Context, actor, id string) (AdminResetLink, error)
311 AddSSHKey(ctx context.Context, actor, id, key, label string) (SSHKey, error)
312 RemoveSSHKey(ctx context.Context, actor, id, keyID string) (SSHKey, error)
313 Audit(ctx context.Context, q AdminAuditQuery) (AdminAuditPage, error)
314}