forked from baron/baron-sso
사용자 상태 세분화
This commit is contained in:
@@ -2580,6 +2580,9 @@ func (h *AuthHandler) authenticatePasswordLogin(ctx context.Context, loginID, pa
|
||||
slog.Error("Failed to resolve kratos identity after login", "loginID", loginID, "error", resolveErr)
|
||||
return nil, fmt.Errorf("failed to resolve user identity")
|
||||
}
|
||||
if err := h.ensureUserActivityAllowed(ctx, subject); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authInfo.Subject = subject
|
||||
return authInfo, nil
|
||||
@@ -2598,9 +2601,30 @@ func passwordLoginErrorSpec(err error) (int, string, string) {
|
||||
if strings.Contains(err.Error(), "failed to resolve user identity") {
|
||||
return fiber.StatusInternalServerError, "internal_error", "Failed to resolve user identity"
|
||||
}
|
||||
if strings.Contains(err.Error(), "cannot perform Baron activity") {
|
||||
return fiber.StatusForbidden, "user_status_forbidden", "This user status cannot sign in"
|
||||
}
|
||||
return fiber.StatusUnauthorized, "password_or_email_mismatch", "Invalid credentials"
|
||||
}
|
||||
|
||||
func (h *AuthHandler) ensureUserActivityAllowed(ctx context.Context, userID string) error {
|
||||
if h == nil || h.UserRepo == nil || strings.TrimSpace(userID) == "" {
|
||||
return nil
|
||||
}
|
||||
user, err := h.UserRepo.FindByID(ctx, userID)
|
||||
if err != nil || user == nil {
|
||||
return nil
|
||||
}
|
||||
if !domain.IsBaronActivityAllowedStatus(user.Status) {
|
||||
return fmt.Errorf("user status %s cannot perform Baron activity", domain.NormalizeUserStatus(user.Status))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isUserActivityForbiddenError(err error) bool {
|
||||
return err != nil && strings.Contains(err.Error(), "cannot perform Baron activity")
|
||||
}
|
||||
|
||||
func headlessAssertionAudiences(c *fiber.Ctx) []string {
|
||||
if c == nil {
|
||||
return nil
|
||||
@@ -4522,6 +4546,9 @@ func (h *AuthHandler) formatPhoneForStorage(phone string) string {
|
||||
func (h *AuthHandler) GetMe(c *fiber.Ctx) error {
|
||||
profile, err := h.resolveCurrentProfile(c)
|
||||
if err != nil {
|
||||
if isUserActivityForbiddenError(err) {
|
||||
return errorJSON(c, fiber.StatusForbidden, "This user status cannot perform Baron activity")
|
||||
}
|
||||
return errorJSON(c, fiber.StatusUnauthorized, err.Error())
|
||||
}
|
||||
return c.JSON(profile)
|
||||
@@ -6198,6 +6225,9 @@ func (h *AuthHandler) AcceptOidcLoginRequest(c *fiber.Ctx) error {
|
||||
if err != nil || subject == "" {
|
||||
return fiber.NewError(fiber.StatusUnauthorized, "Authentication required")
|
||||
}
|
||||
if err := h.ensureUserActivityAllowed(c.Context(), subject); err != nil {
|
||||
return fiber.NewError(fiber.StatusForbidden, "This user status cannot sign in")
|
||||
}
|
||||
c.Locals("user_id", subject)
|
||||
approvedSessionID := strings.TrimSpace(req.ApprovedSessionID)
|
||||
if approvedSessionID == "" {
|
||||
@@ -7472,6 +7502,9 @@ func (h *AuthHandler) getHydraProfile(ctx context.Context, token string) (*domai
|
||||
slog.Warn("Hydra token session validation failed", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
if err := h.ensureUserActivityAllowed(ctx, intro.Subject); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
slog.Info("Hydra token introspected", "subject", intro.Subject, "client_id", intro.ClientID)
|
||||
|
||||
@@ -7655,6 +7688,9 @@ func (h *AuthHandler) getKratosProfile(sessionToken string) (*domain.UserProfile
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := h.ensureUserActivityAllowed(context.Background(), identityID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h.applySessionInfoFromWhoami(
|
||||
h.mapKratosIdentityToProfile(identityID, traits),
|
||||
authenticatedAt,
|
||||
@@ -7667,6 +7703,9 @@ func (h *AuthHandler) getKratosProfileWithCookie(cookie string) (*domain.UserPro
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := h.ensureUserActivityAllowed(context.Background(), identityID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h.applySessionInfoFromWhoami(
|
||||
h.mapKratosIdentityToProfile(identityID, traits),
|
||||
authenticatedAt,
|
||||
@@ -7699,6 +7738,9 @@ func (h *AuthHandler) UpdateMe(c *fiber.Ctx) error {
|
||||
if err != nil {
|
||||
return errorJSON(c, fiber.StatusUnauthorized, "Invalid session")
|
||||
}
|
||||
if err := h.ensureUserActivityAllowed(c.Context(), identityID); err != nil {
|
||||
return errorJSON(c, fiber.StatusForbidden, "This user status cannot perform Baron activity")
|
||||
}
|
||||
|
||||
currentPhone, _ := traits["phone_number"].(string)
|
||||
newPhoneStorage := h.formatPhoneForStorage(req.Phone)
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
josejwt "github.com/go-jose/go-jose/v4/jwt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// --- Mocks ---
|
||||
@@ -159,6 +160,61 @@ func newHeadlessPasswordLoginTestApp(h *AuthHandler) *fiber.App {
|
||||
return app
|
||||
}
|
||||
|
||||
type passwordLoginUserRepo struct {
|
||||
usersByID map[string]domain.User
|
||||
}
|
||||
|
||||
func (r *passwordLoginUserRepo) Create(ctx context.Context, user *domain.User) error { return nil }
|
||||
func (r *passwordLoginUserRepo) Update(ctx context.Context, user *domain.User) error { return nil }
|
||||
func (r *passwordLoginUserRepo) FindByEmail(ctx context.Context, email string) (*domain.User, error) {
|
||||
return nil, errors.New("not found")
|
||||
}
|
||||
func (r *passwordLoginUserRepo) FindByID(ctx context.Context, id string) (*domain.User, error) {
|
||||
if r != nil {
|
||||
if user, ok := r.usersByID[id]; ok {
|
||||
return &user, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("not found")
|
||||
}
|
||||
func (r *passwordLoginUserRepo) FindByIDs(ctx context.Context, ids []string) ([]domain.User, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (r *passwordLoginUserRepo) ListByTenant(ctx context.Context, tenantID string) ([]domain.User, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (r *passwordLoginUserRepo) List(ctx context.Context, offset, limit int, search string, tenantSlug string) ([]domain.User, int64, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
func (r *passwordLoginUserRepo) CountByTenant(ctx context.Context, tenantID string) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
func (r *passwordLoginUserRepo) CountByTenantIDs(ctx context.Context, tenantIDs []string) (map[string]int64, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (r *passwordLoginUserRepo) CountByCompanyCodes(ctx context.Context, codes []string) (map[string]int64, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (r *passwordLoginUserRepo) FindByTenantIDs(ctx context.Context, tenantIDs []string) ([]domain.User, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (r *passwordLoginUserRepo) FindByCompanyCodes(ctx context.Context, codes []string) ([]domain.User, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (r *passwordLoginUserRepo) Delete(ctx context.Context, id string) error { return nil }
|
||||
func (r *passwordLoginUserRepo) UpdateUserLoginIDs(ctx context.Context, userID string, loginIDs []domain.UserLoginID) error {
|
||||
return nil
|
||||
}
|
||||
func (r *passwordLoginUserRepo) GetUserLoginIDs(ctx context.Context, userID string) ([]domain.UserLoginID, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (r *passwordLoginUserRepo) IsLoginIDTaken(ctx context.Context, loginID string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
func (r *passwordLoginUserRepo) FindTenantIDByLoginID(ctx context.Context, loginID string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func mustHeadlessRSAJWK(t *testing.T) (*rsa.PrivateKey, map[string]any) {
|
||||
t.Helper()
|
||||
|
||||
@@ -1947,6 +2003,88 @@ func TestPasswordLogin_NoOIDC_Success(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPasswordLogin_ArchivedUserRejected(t *testing.T) {
|
||||
mockIdp := new(MockIdentityProvider)
|
||||
mockIdp.On("SignIn", "archived@example.com", "password").Return(&domain.AuthInfo{
|
||||
SessionToken: &domain.Token{JWT: "archived-jwt"},
|
||||
Subject: "archived-user-id",
|
||||
}, nil)
|
||||
|
||||
mockKratos := new(MockKratosAdminService)
|
||||
mockKratos.On("FindIdentityIDByIdentifier", mock.Anything, "archived@example.com").Return("archived-user-id", nil)
|
||||
|
||||
h := &AuthHandler{
|
||||
IdpProvider: mockIdp,
|
||||
KratosAdmin: mockKratos,
|
||||
Hydra: service.NewHydraAdminService(),
|
||||
UserRepo: &passwordLoginUserRepo{usersByID: map[string]domain.User{
|
||||
"archived-user-id": {
|
||||
ID: "archived-user-id",
|
||||
Email: "archived@example.com",
|
||||
Name: "Archived User",
|
||||
Status: domain.UserStatusArchived,
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
app := newAuthLoginTestApp(h)
|
||||
body, _ := json.Marshal(map[string]string{
|
||||
"loginId": "archived@example.com",
|
||||
"password": "password",
|
||||
})
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/auth/login", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf("request failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusForbidden {
|
||||
t.Fatalf("expected 403, got %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureUserActivityAllowedByStatus(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
status string
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "active allowed", status: domain.UserStatusActive},
|
||||
{name: "temporary leave allowed", status: domain.UserStatusTemporaryLeave},
|
||||
{name: "baron guest allowed", status: domain.UserStatusBaronGuest},
|
||||
{name: "suspended rejected", status: domain.UserStatusSuspended, wantErr: true},
|
||||
{name: "preboarding rejected", status: domain.UserStatusPreboarding, wantErr: true},
|
||||
{name: "extended leave rejected", status: domain.UserStatusExtendedLeave, wantErr: true},
|
||||
{name: "archived rejected", status: domain.UserStatusArchived, wantErr: true},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
h := &AuthHandler{
|
||||
UserRepo: &passwordLoginUserRepo{usersByID: map[string]domain.User{
|
||||
"user-id": {
|
||||
ID: "user-id",
|
||||
Email: "user@example.com",
|
||||
Name: "User",
|
||||
Status: tc.status,
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
err := h.ensureUserActivityAllowed(context.Background(), "user-id")
|
||||
|
||||
if tc.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPasswordLogin_InvalidCredentials_ReturnsCode(t *testing.T) {
|
||||
mockIdp := new(MockIdentityProvider)
|
||||
mockIdp.On("SignIn", "user@example.com", "wrong-password").Return(nil, errors.New("비밀번호가 일치하지 않습니다"))
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -415,6 +416,7 @@ func (h *TenantHandler) ExportTenantsCSV(c *fiber.Ctx) error {
|
||||
return errorJSON(c, fiber.StatusServiceUnavailable, err.Error())
|
||||
}
|
||||
tenants := filterTenantCSVDescendants(allTenants, parentID)
|
||||
sortTenantsByInputOrder(tenants)
|
||||
|
||||
var buf bytes.Buffer
|
||||
writer := csv.NewWriter(&buf)
|
||||
@@ -483,6 +485,15 @@ func (h *TenantHandler) ExportTenantsCSV(c *fiber.Ctx) error {
|
||||
return c.Send(buf.Bytes())
|
||||
}
|
||||
|
||||
func sortTenantsByInputOrder(tenants []domain.Tenant) {
|
||||
sort.SliceStable(tenants, func(i, j int) bool {
|
||||
if tenants[i].CreatedAt.Equal(tenants[j].CreatedAt) {
|
||||
return tenants[i].ID < tenants[j].ID
|
||||
}
|
||||
return tenants[i].CreatedAt.Before(tenants[j].CreatedAt)
|
||||
})
|
||||
}
|
||||
|
||||
func filterTenantCSVDescendants(tenants []domain.Tenant, parentID string) []domain.Tenant {
|
||||
parentID = strings.TrimSpace(parentID)
|
||||
if parentID == "" {
|
||||
@@ -2231,7 +2242,7 @@ func (h *TenantHandler) loadOrgContextMembers(ctx context.Context, tenantIDs, te
|
||||
users := append(usersByID, usersBySlug...)
|
||||
users = append(users, usersByAppointment...)
|
||||
for _, user := range users {
|
||||
if seen[user.ID] || user.Status != domain.UserStatusActive {
|
||||
if seen[user.ID] || !domain.IsOrgVisibleUserStatus(user.Status) {
|
||||
continue
|
||||
}
|
||||
assignments := mapOrgContextMemberAssignments(user, tenantByID, tenantBySlug, includeUserIDs)
|
||||
|
||||
@@ -616,6 +616,66 @@ func TestTenantHandler_GetOrgContextJSONDefaultsToHanmacFamilyForApiKey(t *testi
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
{
|
||||
ID: "user-archived",
|
||||
Email: "archived@example.com",
|
||||
Name: "보관 사용자",
|
||||
Status: domain.UserStatusArchived,
|
||||
TenantID: parent("dept-platform"),
|
||||
CompanyCode: "platform",
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
{
|
||||
ID: "user-suspended",
|
||||
Email: "suspended@example.com",
|
||||
Name: "정지 사용자",
|
||||
Status: domain.UserStatusSuspended,
|
||||
TenantID: parent("dept-platform"),
|
||||
CompanyCode: "platform",
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
{
|
||||
ID: "user-temporary-leave",
|
||||
Email: "temporary-leave@example.com",
|
||||
Name: "단기휴무 사용자",
|
||||
Status: domain.UserStatusTemporaryLeave,
|
||||
TenantID: parent("dept-platform"),
|
||||
CompanyCode: "platform",
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
{
|
||||
ID: "user-preboarding",
|
||||
Email: "preboarding@example.com",
|
||||
Name: "입사대기 사용자",
|
||||
Status: domain.UserStatusPreboarding,
|
||||
TenantID: parent("dept-platform"),
|
||||
CompanyCode: "platform",
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
{
|
||||
ID: "user-baron-guest",
|
||||
Email: "baron-guest@example.com",
|
||||
Name: "Baron Guest",
|
||||
Status: domain.UserStatusBaronGuest,
|
||||
TenantID: parent("dept-platform"),
|
||||
CompanyCode: "platform",
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
{
|
||||
ID: "user-extended-leave",
|
||||
Email: "extended-leave@example.com",
|
||||
Name: "장기휴직 사용자",
|
||||
Status: domain.UserStatusExtendedLeave,
|
||||
TenantID: parent("dept-platform"),
|
||||
CompanyCode: "platform",
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
}
|
||||
usersBySlug := []domain.User{
|
||||
{ID: "user-sso-member", Email: "member@example.com", Name: "SSO 구성원", Status: domain.UserStatusActive, CompanyCode: "sso", Grade: "선임", CreatedAt: now, UpdatedAt: now},
|
||||
@@ -668,7 +728,7 @@ func TestTenantHandler_GetOrgContextJSONDefaultsToHanmacFamilyForApiKey(t *testi
|
||||
require.NotContains(t, got, "users")
|
||||
deptPlatform := tenantsPayload[2].(map[string]any)
|
||||
platformMembers := deptPlatform["members"].([]any)
|
||||
require.Len(t, platformMembers, 1)
|
||||
require.Len(t, platformMembers, 3)
|
||||
firstUser := platformMembers[0].(map[string]any)
|
||||
require.NotContains(t, firstUser, "id")
|
||||
require.NotContains(t, firstUser, "phone")
|
||||
@@ -703,6 +763,12 @@ func TestTenantHandler_GetOrgContextJSONDefaultsToHanmacFamilyForApiKey(t *testi
|
||||
require.NotContains(t, toJSONString(t, got), "directUserIds")
|
||||
require.NotContains(t, toJSONString(t, got), "private-team")
|
||||
require.NotContains(t, toJSONString(t, got), "root-other")
|
||||
require.NotContains(t, toJSONString(t, got), "archived@example.com")
|
||||
require.Contains(t, toJSONString(t, got), "suspended@example.com")
|
||||
require.Contains(t, toJSONString(t, got), "temporary-leave@example.com")
|
||||
require.NotContains(t, toJSONString(t, got), "preboarding@example.com")
|
||||
require.NotContains(t, toJSONString(t, got), "baron-guest@example.com")
|
||||
require.NotContains(t, toJSONString(t, got), "extended-leave@example.com")
|
||||
}
|
||||
|
||||
func TestTenantHandler_GetOrgContextJSONIncludesUserIDsOnlyWhenRequested(t *testing.T) {
|
||||
@@ -963,6 +1029,37 @@ func TestTenantHandler_ExportTenantsCSV_OmitsIDsAndUsesParentSlug(t *testing.T)
|
||||
mockSvc.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestTenantHandler_ExportTenantsCSV_OrdersByInputOrder(t *testing.T) {
|
||||
app := fiber.New()
|
||||
mockSvc := new(MockTenantService)
|
||||
h := &TenantHandler{Service: mockSvc}
|
||||
|
||||
app.Get("/tenants/export", h.ExportTenantsCSV)
|
||||
|
||||
oldest := time.Date(2026, 1, 2, 9, 0, 0, 0, time.UTC)
|
||||
middle := oldest.Add(time.Hour)
|
||||
newest := oldest.Add(2 * time.Hour)
|
||||
tenants := []domain.Tenant{
|
||||
{ID: "newest", Name: "Newest Tenant", Type: domain.TenantTypeCompany, Slug: "newest", CreatedAt: newest},
|
||||
{ID: "middle", Name: "Middle Tenant", Type: domain.TenantTypeCompany, Slug: "middle", CreatedAt: middle},
|
||||
{ID: "oldest", Name: "Oldest Tenant", Type: domain.TenantTypeCompany, Slug: "oldest", CreatedAt: oldest},
|
||||
}
|
||||
|
||||
mockSvc.On("ListTenants", mock.Anything, 10000, 0, "").Return(tenants, int64(len(tenants)), nil)
|
||||
|
||||
req := httptest.NewRequest("GET", "/tenants/export?includeIds=true", nil)
|
||||
resp, _ := app.Test(req)
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
lines := strings.Split(strings.TrimSpace(string(body)), "\n")
|
||||
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
require.Len(t, lines, 4)
|
||||
assert.Contains(t, lines[1], "oldest,Oldest Tenant")
|
||||
assert.Contains(t, lines[2], "middle,Middle Tenant")
|
||||
assert.Contains(t, lines[3], "newest,Newest Tenant")
|
||||
mockSvc.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestTenantHandler_ExportTenantsCSV_FiltersDescendantsByParentIDWithIDs(t *testing.T) {
|
||||
app := fiber.New()
|
||||
mockSvc := new(MockTenantService)
|
||||
|
||||
@@ -1644,10 +1644,9 @@ func (h *UserHandler) BulkUpdateUsers(c *fiber.Ctx) error {
|
||||
|
||||
state := identity.State
|
||||
if req.Status != nil {
|
||||
if *req.Status == "active" {
|
||||
state = "active"
|
||||
} else {
|
||||
state = "inactive"
|
||||
state = normalizeKratosState(req.Status)
|
||||
if state == "" {
|
||||
state = identity.State
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1667,7 +1666,7 @@ func (h *UserHandler) BulkUpdateUsers(c *fiber.Ctx) error {
|
||||
localUser.Role = *req.Role
|
||||
}
|
||||
if req.Status != nil {
|
||||
localUser.Status = *req.Status
|
||||
localUser.Status = normalizeStatus(*req.Status)
|
||||
}
|
||||
if req.Department != nil {
|
||||
localUser.Department = *req.Department
|
||||
@@ -2610,20 +2609,7 @@ func formatTime(value time.Time) string {
|
||||
}
|
||||
|
||||
func normalizeStatus(state string) string {
|
||||
state = strings.ToLower(strings.TrimSpace(state))
|
||||
if state == "blocked" {
|
||||
return domain.UserStatusInactive
|
||||
}
|
||||
if state == domain.UserStatusInactive ||
|
||||
state == domain.UserStatusSuspended ||
|
||||
state == domain.UserStatusLeaveOfAbsence ||
|
||||
state == domain.UserStatusActive {
|
||||
return state
|
||||
}
|
||||
if state == "" {
|
||||
return domain.UserStatusActive
|
||||
}
|
||||
return state
|
||||
return domain.NormalizeUserStatus(state)
|
||||
}
|
||||
|
||||
func normalizeKratosState(status *string) string {
|
||||
@@ -2637,9 +2623,13 @@ func normalizeKratosState(status *string) string {
|
||||
if value == domain.UserStatusActive {
|
||||
return domain.UserStatusActive
|
||||
}
|
||||
if value == domain.UserStatusInactive ||
|
||||
value == domain.UserStatusSuspended ||
|
||||
value == domain.UserStatusLeaveOfAbsence {
|
||||
normalized := domain.NormalizeUserStatus(value)
|
||||
if normalized == domain.UserStatusPreboarding ||
|
||||
normalized == domain.UserStatusSuspended ||
|
||||
normalized == domain.UserStatusTemporaryLeave ||
|
||||
normalized == domain.UserStatusBaronGuest ||
|
||||
normalized == domain.UserStatusExtendedLeave ||
|
||||
normalized == domain.UserStatusArchived {
|
||||
return domain.UserStatusInactive
|
||||
}
|
||||
return ""
|
||||
|
||||
@@ -1017,7 +1017,7 @@ func TestUserHandler_BulkUpdateUsers(t *testing.T) {
|
||||
assert.True(t, results[0].(map[string]interface{})["success"].(bool))
|
||||
assert.Len(t, worksmobile.upserts, 1)
|
||||
assert.Equal(t, "u-1", worksmobile.upserts[0].ID)
|
||||
assert.Equal(t, domain.UserStatusInactive, worksmobile.upserts[0].Status)
|
||||
assert.Equal(t, domain.UserStatusPreboarding, worksmobile.upserts[0].Status)
|
||||
})
|
||||
|
||||
t.Run("Fail - Super admin cannot assign tenant or RP admin roles", func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user