feat: complete registration invitation and auth redesign
This commit is contained in:
@@ -354,17 +354,17 @@ func (m *MockUsersPersistenceInterface) EXPECT() *MockUsersPersistenceInterfaceM
|
||||
}
|
||||
|
||||
// AddAuthRecord mocks base method.
|
||||
func (m *MockUsersPersistenceInterface) AddAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, scheme, unique string, secret []byte, expires time.Time) error {
|
||||
func (m *MockUsersPersistenceInterface) AddAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, secret []byte, expires time.Time) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "AddAuthRecord", tenantID, uid, authLvl, scheme, unique, secret, expires)
|
||||
ret := m.ctrl.Call(m, "AddAuthRecord", tenantID, uid, authLvl, secret, expires)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// AddAuthRecord indicates an expected call of AddAuthRecord.
|
||||
func (mr *MockUsersPersistenceInterfaceMockRecorder) AddAuthRecord(tenantID, uid, authLvl, scheme, unique, secret, expires interface{}) *gomock.Call {
|
||||
func (mr *MockUsersPersistenceInterfaceMockRecorder) AddAuthRecord(tenantID, uid, authLvl, secret, expires interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddAuthRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).AddAuthRecord), tenantID, uid, authLvl, scheme, unique, secret, expires)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddAuthRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).AddAuthRecord), tenantID, uid, authLvl, secret, expires)
|
||||
}
|
||||
|
||||
// ConfirmCred mocks base method.
|
||||
@@ -396,18 +396,18 @@ func (mr *MockUsersPersistenceInterfaceMockRecorder) Create(tenantID, user, priv
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).Create), tenantID, user, private)
|
||||
}
|
||||
|
||||
// DelAuthRecords mocks base method.
|
||||
func (m *MockUsersPersistenceInterface) DelAuthRecords(tenantID types.TenantID, uid types.Uid, scheme string) error {
|
||||
// DeleteAuthRecord mocks base method.
|
||||
func (m *MockUsersPersistenceInterface) DeleteAuthRecord(tenantID types.TenantID, uid types.Uid) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DelAuthRecords", tenantID, uid, scheme)
|
||||
ret := m.ctrl.Call(m, "DeleteAuthRecord", tenantID, uid)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DelAuthRecords indicates an expected call of DelAuthRecords.
|
||||
func (mr *MockUsersPersistenceInterfaceMockRecorder) DelAuthRecords(tenantID, uid, scheme interface{}) *gomock.Call {
|
||||
// DeleteAuthRecord indicates an expected call of DeleteAuthRecord.
|
||||
func (mr *MockUsersPersistenceInterfaceMockRecorder) DeleteAuthRecord(tenantID, uid interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DelAuthRecords", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).DelAuthRecords), tenantID, uid, scheme)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAuthRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).DeleteAuthRecord), tenantID, uid)
|
||||
}
|
||||
|
||||
// DelCred mocks base method.
|
||||
@@ -532,6 +532,21 @@ func (mr *MockUsersPersistenceInterfaceMockRecorder) GetAll(tenantID interface{}
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAll", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).GetAll), varargs...)
|
||||
}
|
||||
|
||||
// GetByPhone mocks base method.
|
||||
func (m *MockUsersPersistenceInterface) GetByPhone(tenantID types.TenantID, phone string) (types.Uid, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetByPhone", tenantID, phone)
|
||||
ret0, _ := ret[0].(types.Uid)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetByPhone indicates an expected call of GetByPhone.
|
||||
func (mr *MockUsersPersistenceInterfaceMockRecorder) GetByPhone(tenantID, phone interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByPhone", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).GetByPhone), tenantID, phone)
|
||||
}
|
||||
|
||||
// GetAllCreds mocks base method.
|
||||
func (m *MockUsersPersistenceInterface) GetAllCreds(tenantID types.TenantID, id types.Uid, method string, validatedOnly bool) ([]types.Credential, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -548,9 +563,9 @@ func (mr *MockUsersPersistenceInterfaceMockRecorder) GetAllCreds(tenantID, id, m
|
||||
}
|
||||
|
||||
// GetAuthRecord mocks base method.
|
||||
func (m *MockUsersPersistenceInterface) GetAuthRecord(tenantID types.TenantID, user types.Uid, scheme string) (string, auth.Level, []byte, time.Time, error) {
|
||||
func (m *MockUsersPersistenceInterface) GetAuthRecord(tenantID types.TenantID, user types.Uid) (string, auth.Level, []byte, time.Time, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetAuthRecord", tenantID, user, scheme)
|
||||
ret := m.ctrl.Call(m, "GetAuthRecord", tenantID, user)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(auth.Level)
|
||||
ret2, _ := ret[2].([]byte)
|
||||
@@ -560,15 +575,15 @@ func (m *MockUsersPersistenceInterface) GetAuthRecord(tenantID types.TenantID, u
|
||||
}
|
||||
|
||||
// GetAuthRecord indicates an expected call of GetAuthRecord.
|
||||
func (mr *MockUsersPersistenceInterfaceMockRecorder) GetAuthRecord(tenantID, user, scheme interface{}) *gomock.Call {
|
||||
func (mr *MockUsersPersistenceInterfaceMockRecorder) GetAuthRecord(tenantID, user interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).GetAuthRecord), tenantID, user, scheme)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).GetAuthRecord), tenantID, user)
|
||||
}
|
||||
|
||||
// GetAuthUniqueRecord mocks base method.
|
||||
func (m *MockUsersPersistenceInterface) GetAuthUniqueRecord(tenantID types.TenantID, scheme, unique string) (types.Uid, auth.Level, []byte, time.Time, error) {
|
||||
// GetAuthByLogin mocks base method.
|
||||
func (m *MockUsersPersistenceInterface) GetAuthByLogin(tenantID types.TenantID, login string) (types.Uid, auth.Level, []byte, time.Time, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetAuthUniqueRecord", tenantID, scheme, unique)
|
||||
ret := m.ctrl.Call(m, "GetAuthByLogin", tenantID, login)
|
||||
ret0, _ := ret[0].(types.Uid)
|
||||
ret1, _ := ret[1].(auth.Level)
|
||||
ret2, _ := ret[2].([]byte)
|
||||
@@ -577,10 +592,10 @@ func (m *MockUsersPersistenceInterface) GetAuthUniqueRecord(tenantID types.Tenan
|
||||
return ret0, ret1, ret2, ret3, ret4
|
||||
}
|
||||
|
||||
// GetAuthUniqueRecord indicates an expected call of GetAuthUniqueRecord.
|
||||
func (mr *MockUsersPersistenceInterfaceMockRecorder) GetAuthUniqueRecord(tenantID, scheme, unique interface{}) *gomock.Call {
|
||||
// GetAuthByLogin indicates an expected call of GetAuthByLogin.
|
||||
func (mr *MockUsersPersistenceInterfaceMockRecorder) GetAuthByLogin(tenantID, login interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthUniqueRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).GetAuthUniqueRecord), tenantID, scheme, unique)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthByLogin", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).GetAuthByLogin), tenantID, login)
|
||||
}
|
||||
|
||||
// GetByCred mocks base method.
|
||||
@@ -723,17 +738,17 @@ func (mr *MockUsersPersistenceInterfaceMockRecorder) Update(tenantID, uid, updat
|
||||
}
|
||||
|
||||
// UpdateAuthRecord mocks base method.
|
||||
func (m *MockUsersPersistenceInterface) UpdateAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, scheme, unique string, secret []byte, expires time.Time) error {
|
||||
func (m *MockUsersPersistenceInterface) UpdateAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, username string, secret []byte, expires time.Time) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateAuthRecord", tenantID, uid, authLvl, scheme, unique, secret, expires)
|
||||
ret := m.ctrl.Call(m, "UpdateAuthRecord", tenantID, uid, authLvl, username, secret, expires)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpdateAuthRecord indicates an expected call of UpdateAuthRecord.
|
||||
func (mr *MockUsersPersistenceInterfaceMockRecorder) UpdateAuthRecord(tenantID, uid, authLvl, scheme, unique, secret, expires interface{}) *gomock.Call {
|
||||
func (mr *MockUsersPersistenceInterfaceMockRecorder) UpdateAuthRecord(tenantID, uid, authLvl, username, secret, expires interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAuthRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).UpdateAuthRecord), tenantID, uid, authLvl, scheme, unique, secret, expires)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAuthRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).UpdateAuthRecord), tenantID, uid, authLvl, username, secret, expires)
|
||||
}
|
||||
|
||||
// UpdateLastSeen mocks base method.
|
||||
|
||||
+172
-28
@@ -137,6 +137,24 @@ type tenantsMapper struct{}
|
||||
// Tenants is the tenant registry access object.
|
||||
var Tenants TenantsPersistenceInterface
|
||||
|
||||
// RegistrationConfigPersistenceInterface exposes global registration policy.
|
||||
type RegistrationConfigPersistenceInterface interface {
|
||||
SMSEnabled() (bool, error)
|
||||
}
|
||||
|
||||
type registrationConfigMapper struct{}
|
||||
|
||||
// RegistrationConfig reads registration policy from the control database.
|
||||
var RegistrationConfig RegistrationConfigPersistenceInterface
|
||||
|
||||
func (registrationConfigMapper) SMSEnabled() (bool, error) {
|
||||
configAdp, ok := adp.(adapter.RegistrationConfigAdapter)
|
||||
if !ok {
|
||||
return true, types.ErrUnsupported
|
||||
}
|
||||
return configAdp.RegistrationSMSEnabled()
|
||||
}
|
||||
|
||||
// GetByCode resolves a tenant by its public enterprise code.
|
||||
func (tenantsMapper) GetByCode(code string) (*types.Tenant, error) {
|
||||
tenantAdp, ok := adp.(adapter.TenantAdapter)
|
||||
@@ -368,13 +386,14 @@ func (s storeObj) DbStats() func() any {
|
||||
// UsersPersistenceInterface is an interface which defines methods for persistent storage of user records.
|
||||
type UsersPersistenceInterface interface {
|
||||
Create(tenantID types.TenantID, user *types.User, private any) (*types.User, error)
|
||||
GetAuthRecord(tenantID types.TenantID, user types.Uid, scheme string) (string, auth.Level, []byte, time.Time, error)
|
||||
GetAuthUniqueRecord(tenantID types.TenantID, scheme, unique string) (types.Uid, auth.Level, []byte, time.Time, error)
|
||||
AddAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, scheme, unique string, secret []byte, expires time.Time) error
|
||||
UpdateAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, scheme, unique string, secret []byte, expires time.Time) error
|
||||
DelAuthRecords(tenantID types.TenantID, uid types.Uid, scheme string) error
|
||||
GetAuthRecord(tenantID types.TenantID, user types.Uid) (string, auth.Level, []byte, time.Time, error)
|
||||
GetAuthByLogin(tenantID types.TenantID, login string) (types.Uid, auth.Level, []byte, time.Time, error)
|
||||
AddAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, secret []byte, expires time.Time) error
|
||||
UpdateAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, username string, secret []byte, expires time.Time) error
|
||||
DeleteAuthRecord(tenantID types.TenantID, uid types.Uid) error
|
||||
Get(tenantID types.TenantID, uid types.Uid) (*types.User, error)
|
||||
GetAll(tenantID types.TenantID, uid ...types.Uid) ([]types.User, error)
|
||||
GetByPhone(tenantID types.TenantID, phone string) (types.Uid, error)
|
||||
GetByCred(tenantID types.TenantID, method, value string) (types.Uid, error)
|
||||
Delete(tenantID types.TenantID, id types.Uid, hard bool) error
|
||||
UpdateLastSeen(tenantID types.TenantID, uid types.Uid, userAgent string, when time.Time) error
|
||||
@@ -455,61 +474,50 @@ func (usersMapper) Create(tenantID types.TenantID, user *types.User, private any
|
||||
|
||||
// GetAuthRecord takes a user ID and a authentication scheme name, fetches unique scheme-dependent identifier and
|
||||
// authentication secret.
|
||||
func (usersMapper) GetAuthRecord(tenantID types.TenantID, user types.Uid, scheme string) (string, auth.Level, []byte, time.Time, error) {
|
||||
func (usersMapper) GetAuthRecord(tenantID types.TenantID, user types.Uid) (string, auth.Level, []byte, time.Time, error) {
|
||||
tenantAdp, err := tenantAuthAdapter(tenantID)
|
||||
if err != nil {
|
||||
return "", auth.LevelNone, nil, time.Time{}, err
|
||||
}
|
||||
unique, authLvl, secret, expires, err := tenantAdp.TenantAuthGetRecord(tenantID, user, scheme)
|
||||
if err == nil {
|
||||
parts := strings.Split(unique, ":")
|
||||
if len(parts) > 1 {
|
||||
unique = parts[1]
|
||||
} else {
|
||||
err = types.ErrInternal
|
||||
}
|
||||
}
|
||||
|
||||
return unique, authLvl, secret, expires, err
|
||||
return tenantAdp.TenantAuthGetRecord(tenantID, user)
|
||||
}
|
||||
|
||||
// GetAuthUniqueRecord takes a unique identifier and a authentication scheme name, fetches user ID and
|
||||
// authentication secret.
|
||||
func (usersMapper) GetAuthUniqueRecord(tenantID types.TenantID, scheme, unique string) (types.Uid, auth.Level, []byte, time.Time, error) {
|
||||
// GetAuthByLogin fetches a Basic authentication record by username or phone.
|
||||
func (usersMapper) GetAuthByLogin(tenantID types.TenantID, login string) (types.Uid, auth.Level, []byte, time.Time, error) {
|
||||
tenantAdp, err := tenantAuthAdapter(tenantID)
|
||||
if err != nil {
|
||||
return types.ZeroUid, auth.LevelNone, nil, time.Time{}, err
|
||||
}
|
||||
return tenantAdp.TenantAuthGetUniqueRecord(tenantID, scheme+":"+unique)
|
||||
return tenantAdp.TenantAuthGetByLogin(tenantID, login)
|
||||
}
|
||||
|
||||
// AddAuthRecord creates a new authentication record for the given user.
|
||||
func (usersMapper) AddAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, scheme, unique string, secret []byte,
|
||||
func (usersMapper) AddAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, secret []byte,
|
||||
expires time.Time) error {
|
||||
tenantAdp, err := tenantAuthAdapter(tenantID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tenantAdp.TenantAuthAddRecord(tenantID, uid, scheme, scheme+":"+unique, authLvl, secret, expires)
|
||||
return tenantAdp.TenantAuthAddRecord(tenantID, uid, authLvl, secret, expires)
|
||||
}
|
||||
|
||||
// UpdateAuthRecord updates authentication record with a new secret and expiration time.
|
||||
func (usersMapper) UpdateAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, scheme, unique string,
|
||||
func (usersMapper) UpdateAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, username string,
|
||||
secret []byte, expires time.Time) error {
|
||||
tenantAdp, err := tenantAuthAdapter(tenantID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tenantAdp.TenantAuthUpdRecord(tenantID, uid, scheme, scheme+":"+unique, authLvl, secret, expires)
|
||||
return tenantAdp.TenantAuthUpdateRecord(tenantID, uid, username, authLvl, secret, expires)
|
||||
}
|
||||
|
||||
// DelAuthRecords deletes user's auth records of the given scheme.
|
||||
func (usersMapper) DelAuthRecords(tenantID types.TenantID, uid types.Uid, scheme string) error {
|
||||
// DeleteAuthRecord deletes the user's Basic authentication record.
|
||||
func (usersMapper) DeleteAuthRecord(tenantID types.TenantID, uid types.Uid) error {
|
||||
tenantAdp, err := tenantAuthAdapter(tenantID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tenantAdp.TenantAuthDelScheme(tenantID, uid, scheme)
|
||||
return tenantAdp.TenantAuthDeleteRecord(tenantID, uid)
|
||||
}
|
||||
|
||||
// Get returns a user object for the given user ID or nil if the user is not found.
|
||||
@@ -552,6 +560,16 @@ func (usersMapper) GetByCred(tenantID types.TenantID, method, value string) (typ
|
||||
return tenantAdp.TenantUserGetByCred(tenantID, method, value)
|
||||
}
|
||||
|
||||
// GetByPhone resolves an account by the phone identity stored on im_users.
|
||||
// It intentionally does not require a completed tel credential.
|
||||
func (usersMapper) GetByPhone(tenantID types.TenantID, phone string) (types.Uid, error) {
|
||||
tenantAdp, err := tenantBusinessAdapter(tenantID)
|
||||
if err != nil {
|
||||
return types.ZeroUid, err
|
||||
}
|
||||
return tenantAdp.TenantUserGetByPhone(tenantID, phone)
|
||||
}
|
||||
|
||||
// Delete deletes user records.
|
||||
func (usersMapper) Delete(tenantID types.TenantID, id types.Uid, hard bool) error {
|
||||
tenantAdp, err := tenantBusinessAdapter(tenantID)
|
||||
@@ -3062,6 +3080,25 @@ type verificationMapper struct{}
|
||||
// Verifications is a singleton for product verification challenges.
|
||||
var Verifications VerificationPersistenceInterface
|
||||
|
||||
// RegistrationInvitePersistenceInterface manages tenant-scoped registration invitations.
|
||||
type RegistrationInvitePersistenceInterface interface {
|
||||
Create(tenantID types.TenantID, code *types.RegistrationInviteCode) error
|
||||
CreateBatch(tenantID types.TenantID, batch *types.RegistrationInviteBatch, codes []types.RegistrationInviteCode) error
|
||||
GetByCode(tenantID types.TenantID, code string) (*types.RegistrationInviteCode, error)
|
||||
List(tenantID types.TenantID, opts types.RegistrationInviteListOptions) (*types.RegistrationInvitePage, error)
|
||||
GetBatch(tenantID types.TenantID, batchID int64) (*types.RegistrationInviteBatch, error)
|
||||
GetBatchByIdempotency(tenantID types.TenantID, key string) (*types.RegistrationInviteBatch, error)
|
||||
Revoke(tenantID types.TenantID, codeID int64, now time.Time) error
|
||||
RevokeBatch(tenantID types.TenantID, batchID int64, now time.Time) (int, error)
|
||||
RecordUse(tenantID types.TenantID, code string, uid types.Uid, requestID string, now time.Time) error
|
||||
SyncUsage(tenantID types.TenantID, codeID int64) error
|
||||
}
|
||||
|
||||
type registrationInviteMapper struct{}
|
||||
|
||||
// RegistrationInvites is the tenant-scoped registration invitation store.
|
||||
var RegistrationInvites RegistrationInvitePersistenceInterface
|
||||
|
||||
// UserSecurityPersistenceInterface defines user security state used to
|
||||
// invalidate previously issued login tokens.
|
||||
type UserSecurityPersistenceInterface interface {
|
||||
@@ -3267,6 +3304,111 @@ func (verificationMapper) Consume(tenantID types.TenantID, challengeID, tokenHas
|
||||
return tenantAdp.TenantVerificationConsume(tenantID, challengeID, tokenHash, consumedAt)
|
||||
}
|
||||
|
||||
func tenantRegistrationInviteAdapter(tenantID types.TenantID) (adapter.TenantRegistrationInviteAdapter, error) {
|
||||
if !tenantID.IsValid() {
|
||||
return nil, types.ErrMalformed
|
||||
}
|
||||
if router, ok := adp.(adapter.TenantRoutedAdapter); ok {
|
||||
business, _, err := router.TenantAdapterFor(tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if inviteAdp, ok := business.(adapter.TenantRegistrationInviteAdapter); ok {
|
||||
return inviteAdp, nil
|
||||
}
|
||||
}
|
||||
if inviteAdp, ok := adp.(adapter.TenantRegistrationInviteAdapter); ok {
|
||||
return inviteAdp, nil
|
||||
}
|
||||
return nil, types.ErrUnsupported
|
||||
}
|
||||
|
||||
func (registrationInviteMapper) Create(tenantID types.TenantID, code *types.RegistrationInviteCode) error {
|
||||
if code == nil {
|
||||
return types.ErrMalformed
|
||||
}
|
||||
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return inviteAdp.TenantRegistrationInviteCreate(tenantID, code)
|
||||
}
|
||||
|
||||
func (registrationInviteMapper) CreateBatch(tenantID types.TenantID, batch *types.RegistrationInviteBatch, codes []types.RegistrationInviteCode) error {
|
||||
if batch == nil || len(codes) == 0 {
|
||||
return types.ErrMalformed
|
||||
}
|
||||
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return inviteAdp.TenantRegistrationInviteCreateBatch(tenantID, batch, codes)
|
||||
}
|
||||
|
||||
func (registrationInviteMapper) GetByCode(tenantID types.TenantID, code string) (*types.RegistrationInviteCode, error) {
|
||||
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return inviteAdp.TenantRegistrationInviteGetByCode(tenantID, code)
|
||||
}
|
||||
|
||||
func (registrationInviteMapper) List(tenantID types.TenantID, opts types.RegistrationInviteListOptions) (*types.RegistrationInvitePage, error) {
|
||||
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return inviteAdp.TenantRegistrationInviteList(tenantID, opts)
|
||||
}
|
||||
|
||||
func (registrationInviteMapper) GetBatch(tenantID types.TenantID, batchID int64) (*types.RegistrationInviteBatch, error) {
|
||||
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return inviteAdp.TenantRegistrationInviteBatchGet(tenantID, batchID)
|
||||
}
|
||||
|
||||
func (registrationInviteMapper) GetBatchByIdempotency(tenantID types.TenantID, key string) (*types.RegistrationInviteBatch, error) {
|
||||
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return inviteAdp.TenantRegistrationInviteBatchGetByIdempotency(tenantID, key)
|
||||
}
|
||||
|
||||
func (registrationInviteMapper) Revoke(tenantID types.TenantID, codeID int64, now time.Time) error {
|
||||
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return inviteAdp.TenantRegistrationInviteRevoke(tenantID, codeID, now)
|
||||
}
|
||||
|
||||
func (registrationInviteMapper) RevokeBatch(tenantID types.TenantID, batchID int64, now time.Time) (int, error) {
|
||||
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return inviteAdp.TenantRegistrationInviteBatchRevoke(tenantID, batchID, now)
|
||||
}
|
||||
|
||||
func (registrationInviteMapper) RecordUse(tenantID types.TenantID, code string, uid types.Uid, requestID string, now time.Time) error {
|
||||
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return inviteAdp.TenantRegistrationInviteRecordUse(tenantID, code, uid, requestID, now)
|
||||
}
|
||||
|
||||
func (registrationInviteMapper) SyncUsage(tenantID types.TenantID, codeID int64) error {
|
||||
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return inviteAdp.TenantRegistrationInviteSyncUsage(tenantID, codeID)
|
||||
}
|
||||
|
||||
// Get reads a persistent cache entry.
|
||||
func (pcacheMapper) Get(tenantID types.TenantID, key string) (string, error) {
|
||||
tenantAdp, err := tenantBusinessAdapter(tenantID)
|
||||
@@ -3310,6 +3452,7 @@ func SetTestUidGenerator(g types.UidGenerator) {
|
||||
func init() {
|
||||
Store = storeObj{}
|
||||
Tenants = tenantsMapper{}
|
||||
RegistrationConfig = registrationConfigMapper{}
|
||||
Users = usersMapper{}
|
||||
Topics = topicsMapper{}
|
||||
Subs = subsMapper{}
|
||||
@@ -3330,6 +3473,7 @@ func init() {
|
||||
Files = fileMapper{}
|
||||
PCache = pcacheMapper{}
|
||||
Verifications = verificationMapper{}
|
||||
RegistrationInvites = registrationInviteMapper{}
|
||||
Security = userSecurityMapper{}
|
||||
Relations = relationsMapper{}
|
||||
Favorites = favoritesMapper{}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInviteInvalid = errors.New("registration invite invalid")
|
||||
ErrInviteExpired = errors.New("registration invite expired")
|
||||
ErrInviteRevoked = errors.New("registration invite revoked")
|
||||
ErrInviteIdempotencyConflict = errors.New("registration invite idempotency conflict")
|
||||
)
|
||||
|
||||
type RegistrationInviteStatus string
|
||||
|
||||
const (
|
||||
RegistrationInviteActive RegistrationInviteStatus = "active"
|
||||
RegistrationInviteExpired RegistrationInviteStatus = "expired"
|
||||
RegistrationInviteRevoked RegistrationInviteStatus = "revoked"
|
||||
)
|
||||
|
||||
type RegistrationInviteBatch struct {
|
||||
ID int64 `db:"id" json:"batch_id"`
|
||||
TenantID TenantID `db:"tenant_id" json:"tenant_id"`
|
||||
Quantity int `db:"quantity" json:"quantity"`
|
||||
IdempotencyKey string `db:"idempotency_key" json:"-"`
|
||||
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
|
||||
CreatedBy int64 `db:"created_by" json:"created_by"`
|
||||
Remark string `db:"remark" json:"remark,omitempty"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
ActiveCount int `db:"active_count" json:"active_count"`
|
||||
UsageCount int `db:"usage_count" json:"usage_count"`
|
||||
ExpiredCount int `db:"expired_count" json:"expired_count"`
|
||||
RevokedCount int `db:"revoked_count" json:"revoked_count"`
|
||||
}
|
||||
|
||||
type RegistrationInviteCode struct {
|
||||
ID int64 `db:"id" json:"id"`
|
||||
TenantID TenantID `db:"tenant_id" json:"tenant_id"`
|
||||
BatchID *int64 `db:"batch_id" json:"batch_id,omitempty"`
|
||||
Code string `db:"code" json:"code"`
|
||||
Status RegistrationInviteStatus `db:"status" json:"status"`
|
||||
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
|
||||
UsageCount int `db:"usage_count" json:"usage_count"`
|
||||
LastUsedAt *time.Time `db:"last_used_at" json:"last_used_at,omitempty"`
|
||||
CreatedBy int64 `db:"created_by" json:"created_by"`
|
||||
Remark string `db:"remark" json:"remark,omitempty"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
type RegistrationInviteListOptions struct {
|
||||
Code string
|
||||
Status RegistrationInviteStatus
|
||||
BatchID int64
|
||||
CreatedBy int64
|
||||
CreatedAtStart *time.Time
|
||||
CreatedAtEnd *time.Time
|
||||
ExpiresAtStart *time.Time
|
||||
ExpiresAtEnd *time.Time
|
||||
Offset int
|
||||
Limit int
|
||||
}
|
||||
|
||||
type RegistrationInvitePage struct {
|
||||
Items []RegistrationInviteCode
|
||||
Total int
|
||||
}
|
||||
@@ -521,6 +521,8 @@ type User struct {
|
||||
UserAgent string
|
||||
// Canonical mobile number used for registration and account recovery.
|
||||
Phone string
|
||||
// Primary login account. Ordinary accounts are lowercase ASCII names; phone accounts use E.164.
|
||||
Username string
|
||||
|
||||
Public any
|
||||
Trusted any
|
||||
|
||||
Reference in New Issue
Block a user