Raw
  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}