mirror of
https://github.com/warmbly/warmbly.git
synced 2026-10-04 00:02:05 +00:00
* feat: excuse one named account from the emailed login code, so a vendor reviewer who cannot read this instance's mail can sign in without turning codes off for everyone, with the reason recorded beside it and every run of warmblyctl status naming the accounts that hold one * feat: create and manage tester accounts from the admin panel, so letting a reviewer in is a form rather than a shell, with the password shown once and every live exemption listed on one page because forgetting one is the way this goes wrong * fix: give tester management its own permission bit rather than borrowing ban_users, create the account and its exemption in one transaction so no invisible orphan survives a failure, require an accountable operator on the CLI grant, stop a halted row scan reading as the whole exempt list, and show a failed query as an error instead of as no testers * chore: re-run CI after the aggregator tripped on a cancelled job from the branch update, with every underlying job green * chore: retrigger CI, the previous run sat queued indefinitely while other branches ran * feat: roll back a half-created tester when its workspace step fails and backfill the manage-testers bit onto admins already holding every other permission, so the address is not left taken by an unusable account and the new routes are not 403 for the existing admin * feat: make the 000151 manage-testers backfill one-way, because clearing bit 22 on the way down would also revoke it from an admin granted it explicitly afterwards and the up migration would not restore that
66 lines
2.4 KiB
Go
66 lines
2.4 KiB
Go
package models
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type User struct {
|
|
ID uuid.UUID `json:"id"`
|
|
|
|
FirstName string `json:"first_name"`
|
|
LastName string `json:"last_name"`
|
|
Email string `json:"email"`
|
|
AvatarURL *string `json:"avatar_url,omitempty"`
|
|
Roles []uuid.UUID `json:"roles"`
|
|
|
|
ReferralSource *string `json:"referral_source"`
|
|
OnboardingCompletedAt *time.Time `json:"onboarding_completed_at"`
|
|
|
|
MaxOrganizations int `json:"max_organizations"`
|
|
FreeTrialUsed bool `json:"free_trial_used"`
|
|
|
|
// Seconds an instant send is held before it actually leaves, so
|
|
// the user can still cancel it. Bounds live in config
|
|
// (UndoSendSecondsMin/Max); default 30.
|
|
UndoSendSeconds int `json:"undo_send_seconds"`
|
|
|
|
// Platform admin access, populated on the /auth/me load. AdminPermissions
|
|
// is the raw bitmask from users.admin_permissions; IsAdmin is the derived
|
|
// "has any admin permission" flag the admin app gates on.
|
|
AdminPermissions AdminPermission `json:"admin_permissions"`
|
|
IsAdmin bool `json:"is_admin"`
|
|
|
|
// Set when the user has scheduled their own account for deletion.
|
|
// While these are populated the account is "pending deletion" and
|
|
// gets hard-deleted at DeletionScheduledFor unless cancelled.
|
|
DeletionScheduledAt *time.Time `json:"deletion_scheduled_at,omitempty"`
|
|
DeletionScheduledFor *time.Time `json:"deletion_scheduled_for,omitempty"`
|
|
|
|
// The workspace's label registries, for the organization the session
|
|
// currently has selected. Always serialized as arrays (never null) so the
|
|
// frontend can iterate without optional-chaining every access. Populated
|
|
// by the /auth/me handler after the base user load.
|
|
Folders []Group `json:"folders"`
|
|
Tags []Group `json:"tags"`
|
|
Categories []Group `json:"categories"`
|
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// IsPendingDeletion reports whether the user has a pending account deletion.
|
|
func (u *User) IsPendingDeletion() bool {
|
|
return u.DeletionScheduledFor != nil
|
|
}
|
|
|
|
// LoginCodeExemption is one account excused from the emailed login code, as
|
|
// the instance check and the operator CLI report it.
|
|
type LoginCodeExemption struct {
|
|
UserID uuid.UUID `json:"user_id"`
|
|
Email string `json:"email"`
|
|
Reason *string `json:"reason,omitempty"`
|
|
GrantedAt *time.Time `json:"granted_at,omitempty"`
|
|
}
|