feat: validate personal and enterprise auth tenants
This commit is contained in:
@@ -152,6 +152,10 @@ type MsgClientAcc struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
// Registration mobile number. An 11-digit mainland China number takes precedence over Username.
|
||||
Phone string `json:"phone,omitempty"`
|
||||
// Registration type: 0 enterprise, 1 personal. Required when creating a new account.
|
||||
RegistrationType *int `json:"registration_type,omitempty"`
|
||||
// Tenant code submitted by client for enterprise registration. Personal registration keeps it empty.
|
||||
TenantCode string `json:"tenant_code,omitempty"`
|
||||
// Deprecated compatibility field. New clients put a five-letter invitation
|
||||
// code in the shared verification input and submit it as the tel cred response.
|
||||
InviteCode string `json:"invite_code,omitempty"`
|
||||
@@ -173,6 +177,10 @@ type MsgClientLogin struct {
|
||||
Scheme string `json:"scheme,omitempty"`
|
||||
// Shared secret
|
||||
Secret []byte `json:"secret"`
|
||||
// Login type: 0 enterprise, 1 personal. Required for password login.
|
||||
LoginType *int `json:"login_type,omitempty"`
|
||||
// Tenant code submitted by client for enterprise login. Personal login keeps it empty or default.
|
||||
TenantCode string `json:"tenant_code,omitempty"`
|
||||
// Credntials being verified (email or phone or captcha etc.)
|
||||
Cred []MsgCredClient `json:"cred,omitempty"`
|
||||
}
|
||||
|
||||
+63
-5
@@ -63,6 +63,12 @@ const (
|
||||
imReasonReviewUnavailable = "CONTENT_REVIEW_UNAVAILABLE"
|
||||
)
|
||||
|
||||
const (
|
||||
imDefaultTenantCode = "default"
|
||||
registrationTypeEnterprise = 0
|
||||
registrationTypePersonal = 1
|
||||
)
|
||||
|
||||
func reviewIMHTTPText(wrt http.ResponseWriter, req *http.Request, principal *imPrincipal, scene, topic, text string) bool {
|
||||
text = strings.TrimSpace(text)
|
||||
if text == "" {
|
||||
@@ -291,10 +297,11 @@ type imTenantEntryFeedbackRequest struct {
|
||||
}
|
||||
|
||||
type imVerificationCreateRequest struct {
|
||||
TenantCode string `json:"tenant_code,omitempty"`
|
||||
Scene string `json:"scene"`
|
||||
Phone string `json:"phone"`
|
||||
Country string `json:"country,omitempty"`
|
||||
TenantCode string `json:"tenant_code,omitempty"`
|
||||
RegistrationType *int `json:"registration_type,omitempty"`
|
||||
Scene string `json:"scene"`
|
||||
Phone string `json:"phone"`
|
||||
Country string `json:"country,omitempty"`
|
||||
}
|
||||
|
||||
type imVerificationVerifyRequest struct {
|
||||
@@ -979,7 +986,13 @@ func serveIMVerificationCodes(wrt http.ResponseWriter, req *http.Request) {
|
||||
writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "phone"})
|
||||
return
|
||||
}
|
||||
tenant, ok := resolveIMHTTPTenant(wrt, req, body.TenantCode)
|
||||
var tenant *types.Tenant
|
||||
var ok bool
|
||||
if scene == "register" {
|
||||
tenant, ok = resolveIMHTTPRegistrationTenant(wrt, req, body)
|
||||
} else {
|
||||
tenant, ok = resolveIMHTTPTenant(wrt, req, body.TenantCode)
|
||||
}
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@@ -9891,6 +9904,51 @@ func resolveIMHTTPTenant(wrt http.ResponseWriter, req *http.Request, fallbackCod
|
||||
return tenant, true
|
||||
}
|
||||
|
||||
func resolveIMHTTPRegistrationTenant(wrt http.ResponseWriter, req *http.Request, body imVerificationCreateRequest) (*types.Tenant, bool) {
|
||||
if body.RegistrationType == nil {
|
||||
writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "registration_type"})
|
||||
return nil, false
|
||||
}
|
||||
|
||||
headerCode := strings.TrimSpace(req.Header.Get("X-Tenant-Code"))
|
||||
bodyCode := strings.TrimSpace(body.TenantCode)
|
||||
var tenantCode string
|
||||
|
||||
switch *body.RegistrationType {
|
||||
case registrationTypePersonal:
|
||||
if bodyCode != "" {
|
||||
writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "tenant_code"})
|
||||
return nil, false
|
||||
}
|
||||
if headerCode != "" && !strings.EqualFold(headerCode, imDefaultTenantCode) {
|
||||
writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "tenant_code"})
|
||||
return nil, false
|
||||
}
|
||||
tenantCode = imDefaultTenantCode
|
||||
case registrationTypeEnterprise:
|
||||
if bodyCode == "" {
|
||||
bodyCode = headerCode
|
||||
} else if headerCode != "" && !strings.EqualFold(headerCode, bodyCode) {
|
||||
writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "tenant_code"})
|
||||
return nil, false
|
||||
}
|
||||
if bodyCode == "" || len(bodyCode) > 64 {
|
||||
writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "tenant_code"})
|
||||
return nil, false
|
||||
}
|
||||
if strings.EqualFold(bodyCode, imDefaultTenantCode) {
|
||||
writeIMError(wrt, req, http.StatusNotFound, 30010, imReasonTenantUnavailable, "tenant unavailable", nil)
|
||||
return nil, false
|
||||
}
|
||||
tenantCode = bodyCode
|
||||
default:
|
||||
writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "registration_type"})
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return resolveIMHTTPTenant(wrt, req, tenantCode)
|
||||
}
|
||||
|
||||
func writeIMSuccess(wrt http.ResponseWriter, req *http.Request, data any) {
|
||||
writeIMJSON(wrt, http.StatusOK, imHTTPResponse{
|
||||
Code: 0,
|
||||
|
||||
+55
-6
@@ -1736,7 +1736,7 @@ func TestServeIMVerificationCodesCreatesChallenge(t *testing.T) {
|
||||
}()
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/im/auth/verification-codes",
|
||||
bytes.NewBufferString(`{"scene":"register","phone":"+8613800138000"}`))
|
||||
bytes.NewBufferString(`{"scene":"register","registration_type":0,"tenant_code":"acme","phone":"+8613800138000"}`))
|
||||
req.Header.Set("X-MeetingIM-APIKey", makeTestAPIKey(t))
|
||||
req.Header.Set("X-Tenant-Code", "acme")
|
||||
rec := httptest.NewRecorder()
|
||||
@@ -1786,7 +1786,7 @@ func TestServeIMVerificationCodesSkipsDeliveryWhenRegistrationSMSDisabled(t *tes
|
||||
}()
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/im/auth/verification-codes",
|
||||
bytes.NewBufferString(`{"scene":"register","phone":"+8613800138000"}`))
|
||||
bytes.NewBufferString(`{"scene":"register","registration_type":0,"tenant_code":"acme","phone":"+8613800138000"}`))
|
||||
req.Header.Set("X-MeetingIM-APIKey", makeTestAPIKey(t))
|
||||
req.Header.Set("X-Tenant-Code", "acme")
|
||||
rec := httptest.NewRecorder()
|
||||
@@ -1798,6 +1798,55 @@ func TestServeIMVerificationCodesSkipsDeliveryWhenRegistrationSMSDisabled(t *tes
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeIMVerificationCodesPersonalUsesDefaultTenant(t *testing.T) {
|
||||
oldTenants := store.Tenants
|
||||
oldVerifications := store.Verifications
|
||||
oldSender := sendIMVerificationCode
|
||||
store.Tenants = testTenantStore{tenant: &types.Tenant{ID: 1, Code: imDefaultTenantCode, State: types.TenantStateActive}}
|
||||
verifications := &testVerificationStore{}
|
||||
store.Verifications = verifications
|
||||
sendIMVerificationCode = func(_ types.TenantID, _, _, _, _ string) error {
|
||||
t.Fatal("registration SMS sender must not be called for fixed-code registration")
|
||||
return nil
|
||||
}
|
||||
defer func() {
|
||||
store.Tenants = oldTenants
|
||||
store.Verifications = oldVerifications
|
||||
sendIMVerificationCode = oldSender
|
||||
}()
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/im/auth/verification-codes",
|
||||
bytes.NewBufferString(`{"scene":"register","registration_type":1,"phone":"+8613800138000"}`))
|
||||
req.Header.Set("X-MeetingIM-APIKey", makeTestAPIKey(t))
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
serveIMVerificationCodes(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK || verifications.created == nil {
|
||||
t.Fatalf("status=%d body=%s created=%#v", rec.Code, rec.Body.String(), verifications.created)
|
||||
}
|
||||
if verifications.created.TenantID != 1 {
|
||||
t.Fatalf("tenant: got %d, want 1", verifications.created.TenantID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeIMVerificationCodesRejectsEnterpriseDefaultTenant(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/im/auth/verification-codes",
|
||||
bytes.NewBufferString(`{"scene":"register","registration_type":0,"tenant_code":"default","phone":"+8613800138000"}`))
|
||||
req.Header.Set("X-MeetingIM-APIKey", makeTestAPIKey(t))
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
serveIMVerificationCodes(rec, req)
|
||||
|
||||
if rec.Code != http.StatusNotFound {
|
||||
t.Fatalf("status: got %d, want %d; body=%s", rec.Code, http.StatusNotFound, rec.Body.String())
|
||||
}
|
||||
resp := decodeIMHTTPResponse(t, rec)
|
||||
if resp.Reason != imReasonTenantUnavailable {
|
||||
t.Fatalf("reason: got %q, want %q", resp.Reason, imReasonTenantUnavailable)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeIMPasswordResetCodeFindsPhoneWithoutTelCredential(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
@@ -1883,7 +1932,7 @@ func TestServeIMVerificationCodesKeepsChallengeWhenDeliveryUnavailable(t *testin
|
||||
|
||||
func TestServeIMVerificationCodesRequiresTenantCode(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/im/auth/verification-codes",
|
||||
bytes.NewBufferString(`{"scene":"register","phone":"+8613800138000"}`))
|
||||
bytes.NewBufferString(`{"scene":"register","registration_type":0,"phone":"+8613800138000"}`))
|
||||
req.Header.Set("X-MeetingIM-APIKey", makeTestAPIKey(t))
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
@@ -1922,7 +1971,7 @@ func TestServeIMVerificationCodesRateLimited(t *testing.T) {
|
||||
}()
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/im/auth/verification-codes",
|
||||
bytes.NewBufferString(`{"scene":"register","phone":"+8613800138000"}`))
|
||||
bytes.NewBufferString(`{"scene":"register","registration_type":0,"tenant_code":"acme","phone":"+8613800138000"}`))
|
||||
req.Header.Set("X-MeetingIM-APIKey", makeTestAPIKey(t))
|
||||
req.Header.Set("X-Tenant-Code", "acme")
|
||||
rec := httptest.NewRecorder()
|
||||
@@ -8242,12 +8291,12 @@ func TestIMMessageTypeDraftyEntities(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "legacy flat file_id content still works",
|
||||
msg: &types.Message{Content: map[string]any{"file_id": "file-1", "mime": "image/png"}},
|
||||
msg: &types.Message{Content: map[string]any{"file_id": "file-1", "mime": "image/png"}},
|
||||
want: "image",
|
||||
},
|
||||
{
|
||||
name: "legacy flat url content still works",
|
||||
msg: &types.Message{Content: map[string]any{"url": "https://example.com"}},
|
||||
msg: &types.Message{Content: map[string]any{"url": "https://example.com"}},
|
||||
want: "link",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1143,6 +1143,12 @@ func (s *Session) login(msg *ClientComMessage) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateLoginTenant(s, msg); err != nil {
|
||||
logs.Warn.Println("s.login: invalid login tenant", err, s.sid)
|
||||
s.queueOut(decodeStoreError(types.ErrPolicy, msg.Id, msg.Timestamp, loginTenantErrorParams(err)))
|
||||
return
|
||||
}
|
||||
|
||||
handler := store.Store.GetLogicalAuthHandler(msg.Login.Scheme)
|
||||
if handler == nil {
|
||||
recordSessionLoginFailure(s, msg.Login.Scheme, msg.Login.Secret, nil, msg.Id, types.ErrUnsupported, msg.Timestamp)
|
||||
|
||||
+60
-16
@@ -19,6 +19,10 @@ type testTenantStore struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func ptrInt(v int) *int {
|
||||
return &v
|
||||
}
|
||||
|
||||
type testLoginDeviceStore struct {
|
||||
devices map[types.Uid][]types.DeviceDef
|
||||
updates int
|
||||
@@ -365,9 +369,11 @@ func TestDispatchLogin(t *testing.T) {
|
||||
|
||||
msg := &ClientComMessage{
|
||||
Login: &MsgClientLogin{
|
||||
Id: "123",
|
||||
Scheme: "basic",
|
||||
Secret: []byte(secret),
|
||||
Id: "123",
|
||||
Scheme: "basic",
|
||||
Secret: []byte(secret),
|
||||
LoginType: ptrInt(registrationTypeEnterprise),
|
||||
TenantCode: "test",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -425,7 +431,7 @@ func TestDispatchLoginRejectsAuthenticatorTenantMismatch(t *testing.T) {
|
||||
|
||||
s := &Session{send: make(chan any, 1), ver: 16, tenantID: 1, tenantCode: "acme"}
|
||||
s.login(&ClientComMessage{
|
||||
Login: &MsgClientLogin{Id: "login-1", Scheme: "basic", Secret: secret},
|
||||
Login: &MsgClientLogin{Id: "login-1", Scheme: "basic", Secret: secret, LoginType: ptrInt(registrationTypeEnterprise), TenantCode: "acme"},
|
||||
Timestamp: time.Now(),
|
||||
})
|
||||
|
||||
@@ -438,6 +444,40 @@ func TestDispatchLoginRejectsAuthenticatorTenantMismatch(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDispatchLoginRejectsEnterpriseDefaultTenant(t *testing.T) {
|
||||
s := &Session{send: make(chan any, 1), ver: 16, tenantID: 1, tenantCode: imDefaultTenantCode}
|
||||
s.login(&ClientComMessage{
|
||||
Login: &MsgClientLogin{Id: "login-1", Scheme: "basic", Secret: []byte("alice:password"), LoginType: ptrInt(registrationTypeEnterprise), TenantCode: imDefaultTenantCode},
|
||||
Timestamp: time.Now(),
|
||||
})
|
||||
|
||||
resp := (<-s.send).(*ServerComMessage)
|
||||
if resp.Ctrl == nil || resp.Ctrl.Code != http.StatusUnprocessableEntity {
|
||||
t.Fatalf("unexpected response: %+v", resp)
|
||||
}
|
||||
params, _ := resp.Ctrl.Params.(map[string]any)
|
||||
if params["reason"] != "TENANT_CODE_FORBIDDEN" {
|
||||
t.Fatalf("unexpected params: %+v", resp.Ctrl.Params)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDispatchLoginRequiresTypeForBasic(t *testing.T) {
|
||||
s := &Session{send: make(chan any, 1), ver: 16, tenantID: 1, tenantCode: imDefaultTenantCode}
|
||||
s.login(&ClientComMessage{
|
||||
Login: &MsgClientLogin{Id: "login-1", Scheme: "basic", Secret: []byte("alice:password")},
|
||||
Timestamp: time.Now(),
|
||||
})
|
||||
|
||||
resp := (<-s.send).(*ServerComMessage)
|
||||
if resp.Ctrl == nil || resp.Ctrl.Code != http.StatusUnprocessableEntity {
|
||||
t.Fatalf("unexpected response: %+v", resp)
|
||||
}
|
||||
params, _ := resp.Ctrl.Params.(map[string]any)
|
||||
if params["reason"] != "LOGIN_TYPE_REQUIRED" {
|
||||
t.Fatalf("unexpected params: %+v", resp.Ctrl.Params)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublishSysRequiresRoot(t *testing.T) {
|
||||
s := &Session{
|
||||
send: make(chan any, 1),
|
||||
@@ -1420,12 +1460,14 @@ func TestDispatchAccNew(t *testing.T) {
|
||||
public := "public name"
|
||||
msg := &ClientComMessage{
|
||||
Acc: &MsgClientAcc{
|
||||
Id: "123",
|
||||
User: "newXYZ",
|
||||
Scheme: "basic",
|
||||
Username: "alice",
|
||||
Phone: "13800138000",
|
||||
Secret: []byte(secret),
|
||||
Id: "123",
|
||||
User: "newXYZ",
|
||||
Scheme: "basic",
|
||||
Username: "alice",
|
||||
Phone: "13800138000",
|
||||
RegistrationType: ptrInt(registrationTypeEnterprise),
|
||||
TenantCode: "test",
|
||||
Secret: []byte(secret),
|
||||
Cred: []MsgCredClient{{
|
||||
Method: "tel", Value: "+8613800138000", Response: "AbCdE",
|
||||
Params: map[string]any{"challenge_id": "challenge-1"},
|
||||
@@ -1540,12 +1582,14 @@ func TestDispatchAccNewUsernameOnlyDoesNotRequireInvite(t *testing.T) {
|
||||
|
||||
msg := &ClientComMessage{
|
||||
Acc: &MsgClientAcc{
|
||||
Id: "username-only",
|
||||
User: "new",
|
||||
Scheme: "basic",
|
||||
Username: "alice",
|
||||
Secret: []byte(secret),
|
||||
Desc: &MsgSetDesc{Public: map[string]any{"fn": "Alice"}},
|
||||
Id: "username-only",
|
||||
User: "new",
|
||||
Scheme: "basic",
|
||||
Username: "alice",
|
||||
RegistrationType: ptrInt(registrationTypeEnterprise),
|
||||
TenantCode: "test",
|
||||
Secret: []byte(secret),
|
||||
Desc: &MsgSetDesc{Public: map[string]any{"fn": "Alice"}},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
+124
@@ -43,6 +43,12 @@ func replyCreateUser(s *Session, msg *ClientComMessage, rec *auth.Rec) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateAccountRegistrationTenant(s, msg); err != nil {
|
||||
logs.Warn.Println("create user: invalid registration tenant fields", err, "sid=", s.sid)
|
||||
s.queueOut(decodeStoreError(types.ErrPolicy, msg.Id, msg.Timestamp, registrationTenantErrorParams(err)))
|
||||
return
|
||||
}
|
||||
|
||||
account, phone, secretAccount, phoneRegistration, err := resolveRegistrationAccount(msg.Acc.Username, msg.Acc.Phone)
|
||||
if err != nil || !registrationSecretMatches(msg.Acc.Secret, secretAccount) {
|
||||
logs.Warn.Println("create user: invalid registration account fields, sid=", s.sid)
|
||||
@@ -382,6 +388,124 @@ func resolveRegistrationAccount(username, phone string) (account, canonicalPhone
|
||||
return username, "", username, false, nil
|
||||
}
|
||||
|
||||
var (
|
||||
errRegistrationTypeRequired = errors.New("registration_type required")
|
||||
errRegistrationTypeInvalid = errors.New("registration_type invalid")
|
||||
errRegistrationTenantForbidden = errors.New("registration tenant forbidden")
|
||||
errRegistrationTenantRequired = errors.New("registration tenant required")
|
||||
errRegistrationTenantMismatch = errors.New("registration tenant mismatch")
|
||||
errLoginTypeRequired = errors.New("login_type required")
|
||||
errLoginTypeInvalid = errors.New("login_type invalid")
|
||||
)
|
||||
|
||||
func validateAccountRegistrationTenant(s *Session, msg *ClientComMessage) error {
|
||||
if msg == nil || msg.Acc == nil || msg.Acc.RegistrationType == nil {
|
||||
return errRegistrationTypeRequired
|
||||
}
|
||||
tenantCode := strings.TrimSpace(msg.Acc.TenantCode)
|
||||
switch *msg.Acc.RegistrationType {
|
||||
case registrationTypePersonal:
|
||||
if tenantCode != "" {
|
||||
return errRegistrationTenantForbidden
|
||||
}
|
||||
if !strings.EqualFold(s.tenantCode, imDefaultTenantCode) {
|
||||
return errRegistrationTenantMismatch
|
||||
}
|
||||
case registrationTypeEnterprise:
|
||||
if tenantCode == "" {
|
||||
return errRegistrationTenantRequired
|
||||
}
|
||||
if strings.EqualFold(tenantCode, imDefaultTenantCode) {
|
||||
return errRegistrationTenantForbidden
|
||||
}
|
||||
if !strings.EqualFold(s.tenantCode, tenantCode) {
|
||||
return errRegistrationTenantMismatch
|
||||
}
|
||||
default:
|
||||
return errRegistrationTypeInvalid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func registrationTenantErrorParams(err error) map[string]any {
|
||||
params := map[string]any{"what": "registration_type", "reason": "REGISTRATION_TYPE_INVALID"}
|
||||
switch {
|
||||
case errors.Is(err, errRegistrationTypeRequired):
|
||||
params["reason"] = "REGISTRATION_TYPE_REQUIRED"
|
||||
case errors.Is(err, errRegistrationTypeInvalid):
|
||||
params["reason"] = "REGISTRATION_TYPE_INVALID"
|
||||
case errors.Is(err, errRegistrationTenantRequired):
|
||||
params["what"] = "tenant_code"
|
||||
params["reason"] = "TENANT_CODE_REQUIRED"
|
||||
case errors.Is(err, errRegistrationTenantForbidden):
|
||||
params["what"] = "tenant_code"
|
||||
params["reason"] = "TENANT_CODE_FORBIDDEN"
|
||||
case errors.Is(err, errRegistrationTenantMismatch):
|
||||
params["what"] = "tenant_code"
|
||||
params["reason"] = "TENANT_CODE_MISMATCH"
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
func validateLoginTenant(s *Session, msg *ClientComMessage) error {
|
||||
if msg == nil || msg.Login == nil {
|
||||
return nil
|
||||
}
|
||||
if msg.Login.Scheme != "basic" && msg.Login.LoginType == nil {
|
||||
return nil
|
||||
}
|
||||
if msg.Login.LoginType == nil {
|
||||
return errLoginTypeRequired
|
||||
}
|
||||
|
||||
tenantCode := strings.TrimSpace(msg.Login.TenantCode)
|
||||
switch *msg.Login.LoginType {
|
||||
case registrationTypePersonal:
|
||||
if tenantCode != "" && !strings.EqualFold(tenantCode, imDefaultTenantCode) {
|
||||
return errRegistrationTenantForbidden
|
||||
}
|
||||
if !strings.EqualFold(s.tenantCode, imDefaultTenantCode) {
|
||||
return errRegistrationTenantMismatch
|
||||
}
|
||||
case registrationTypeEnterprise:
|
||||
if tenantCode == "" {
|
||||
tenantCode = s.tenantCode
|
||||
}
|
||||
if tenantCode == "" {
|
||||
return errRegistrationTenantRequired
|
||||
}
|
||||
if strings.EqualFold(tenantCode, imDefaultTenantCode) {
|
||||
return errRegistrationTenantForbidden
|
||||
}
|
||||
if !strings.EqualFold(s.tenantCode, tenantCode) {
|
||||
return errRegistrationTenantMismatch
|
||||
}
|
||||
default:
|
||||
return errLoginTypeInvalid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func loginTenantErrorParams(err error) map[string]any {
|
||||
params := map[string]any{"what": "login_type", "reason": "LOGIN_TYPE_INVALID"}
|
||||
switch {
|
||||
case errors.Is(err, errLoginTypeRequired):
|
||||
params["reason"] = "LOGIN_TYPE_REQUIRED"
|
||||
case errors.Is(err, errLoginTypeInvalid):
|
||||
params["reason"] = "LOGIN_TYPE_INVALID"
|
||||
case errors.Is(err, errRegistrationTenantRequired):
|
||||
params["what"] = "tenant_code"
|
||||
params["reason"] = "TENANT_CODE_REQUIRED"
|
||||
case errors.Is(err, errRegistrationTenantForbidden):
|
||||
params["what"] = "tenant_code"
|
||||
params["reason"] = "TENANT_CODE_FORBIDDEN"
|
||||
case errors.Is(err, errRegistrationTenantMismatch):
|
||||
params["what"] = "tenant_code"
|
||||
params["reason"] = "TENANT_CODE_MISMATCH"
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
func validateRegistrationUsername(username string) error {
|
||||
if username == "" || len(username) > 10 {
|
||||
return types.ErrPolicy
|
||||
|
||||
Reference in New Issue
Block a user