forked from baron/baron-sso
dev 병합 code-check 오류 수정
This commit is contained in:
11
Makefile
11
Makefile
@@ -181,13 +181,8 @@ PLAYWRIGHT_CHROMIUM_COMPLETE := $(PLAYWRIGHT_BROWSERS_PATH)/chromium-1208/INSTAL
|
||||
PLAYWRIGHT_FIREFOX_COMPLETE := $(PLAYWRIGHT_BROWSERS_PATH)/firefox-1509/INSTALLATION_COMPLETE
|
||||
PLAYWRIGHT_WEBKIT_COMPLETE := $(PLAYWRIGHT_BROWSERS_PATH)/webkit-2248/INSTALLATION_COMPLETE
|
||||
|
||||
ifeq ($(CI),)
|
||||
PLAYWRIGHT_INSTALL_ALL := sh -c 'if [ -f "$(PLAYWRIGHT_CHROMIUM_COMPLETE)" ] && [ -f "$(PLAYWRIGHT_FIREFOX_COMPLETE)" ] && [ -f "$(PLAYWRIGHT_WEBKIT_COMPLETE)" ]; then echo "Playwright browsers already installed"; else npx playwright install; fi'
|
||||
PLAYWRIGHT_INSTALL_CHROMIUM := sh -c 'if [ -f "$(PLAYWRIGHT_CHROMIUM_COMPLETE)" ]; then echo "Playwright chromium already installed"; else npx playwright install chromium; fi'
|
||||
else
|
||||
PLAYWRIGHT_INSTALL_ALL := npx playwright install --with-deps
|
||||
PLAYWRIGHT_INSTALL_CHROMIUM := npx playwright install --with-deps chromium
|
||||
endif
|
||||
|
||||
.PHONY: code-check code-check-lint code-check-test-jobs code-check-i18n code-check-i18n-values code-check-go-lint code-check-sync-userfront-locales code-check-userfront-install code-check-userfront-lint code-check-front-lint code-check-backend-tests code-check-userfront-tests code-check-adminfront-tests code-check-devfront-tests code-check-userfront-e2e-tests
|
||||
|
||||
@@ -254,12 +249,12 @@ code-check-userfront-lint:
|
||||
code-check-front-lint:
|
||||
@echo "==> adminfront biome lint/format check"
|
||||
rm -rf adminfront/playwright-report adminfront/test-results
|
||||
cd adminfront && npm ci
|
||||
cd adminfront && npm ci --ignore-scripts
|
||||
cd adminfront && npx biome check . --formatter-enabled=false --organize-imports-enabled=false
|
||||
cd adminfront && npx biome check . --linter-enabled=false --organize-imports-enabled=false
|
||||
@echo "==> devfront biome lint/format check"
|
||||
rm -rf devfront/playwright-report devfront/test-results
|
||||
cd devfront && npm ci
|
||||
cd devfront && npm ci --ignore-scripts
|
||||
cd devfront && npx biome check . --formatter-enabled=false --organize-imports-enabled=false
|
||||
cd devfront && npx biome check . --linter-enabled=false --organize-imports-enabled=false
|
||||
|
||||
@@ -298,7 +293,7 @@ code-check-devfront-tests:
|
||||
@mkdir -p reports/devfront
|
||||
@rm -rf reports/devfront/playwright-report reports/devfront/test-results
|
||||
@status=0; \
|
||||
(cd devfront && npm ci) || status=$$?; \
|
||||
(cd devfront && npm ci --ignore-scripts) || status=$$?; \
|
||||
if [ $$status -eq 0 ]; then \
|
||||
(cd devfront && $(PLAYWRIGHT_INSTALL_ALL)) || status=$$?; \
|
||||
fi; \
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"baron-sso-backend/internal/domain"
|
||||
"baron-sso-backend/internal/service"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -13,6 +15,121 @@ import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// --- Mocks ---
|
||||
|
||||
type MockKratosAdminServiceForConsent struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockKratosAdminServiceForConsent) FindIdentityIDByIdentifier(ctx context.Context, identifier string) (string, error) {
|
||||
args := m.Called(ctx, identifier)
|
||||
return args.String(0), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockKratosAdminServiceForConsent) GetIdentity(ctx context.Context, id string) (*service.KratosIdentity, error) {
|
||||
args := m.Called(ctx, id)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).(*service.KratosIdentity), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockKratosAdminServiceForConsent) ListIdentities(ctx context.Context) ([]service.KratosIdentity, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockKratosAdminServiceForConsent) UpdateIdentity(ctx context.Context, identityID string, traits map[string]interface{}, state string) (*service.KratosIdentity, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockKratosAdminServiceForConsent) CreateIdentity(ctx context.Context, traits map[string]interface{}) (*service.KratosIdentity, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockKratosAdminServiceForConsent) DeleteIdentity(ctx context.Context, identityID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockKratosAdminServiceForConsent) UpdateIdentityPassword(ctx context.Context, identityID, newPassword string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockKratosAdminServiceForConsent) ListIdentitySessions(ctx context.Context, identityID string) ([]service.KratosSession, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockKratosAdminServiceForConsent) GetSession(ctx context.Context, sessionID string) (*service.KratosSession, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockKratosAdminServiceForConsent) DeleteSession(ctx context.Context, sessionID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockKratosAdminServiceForConsent) CreateUser(ctx context.Context, user *domain.BrokerUser, password string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
type MockTenantServiceForConsent struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockTenantServiceForConsent) GetTenant(ctx context.Context, id string) (*domain.Tenant, error) {
|
||||
args := m.Called(ctx, id)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).(*domain.Tenant), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockTenantServiceForConsent) GetTenantBySlug(ctx context.Context, slug string) (*domain.Tenant, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockTenantServiceForConsent) GetTenantByDomain(ctx context.Context, domainName string) (*domain.Tenant, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockTenantServiceForConsent) ListTenants(ctx context.Context, limit, offset int, parentID string) ([]domain.Tenant, int64, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
func (m *MockTenantServiceForConsent) RegisterTenant(ctx context.Context, name, slug, tenantType, description string, domains []string, parentID *string, creatorID string) (*domain.Tenant, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockTenantServiceForConsent) RequestRegistration(ctx context.Context, name, slug, description string, domainName string, adminEmail string) (*domain.Tenant, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockTenantServiceForConsent) ApproveTenant(ctx context.Context, id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockTenantServiceForConsent) ListManageableTenants(ctx context.Context, userID string) ([]domain.Tenant, error) {
|
||||
args := m.Called(ctx, userID)
|
||||
return args.Get(0).([]domain.Tenant), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockTenantServiceForConsent) ListJoinedTenants(ctx context.Context, userID string) ([]domain.Tenant, error) {
|
||||
args := m.Called(ctx, userID)
|
||||
return args.Get(0).([]domain.Tenant), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockTenantServiceForConsent) IsDomainAllowed(ctx context.Context, domainName string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (m *MockTenantServiceForConsent) ProvisionTenantByDomain(ctx context.Context, domainName string) (*domain.Tenant, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockTenantServiceForConsent) SetKetoService(keto service.KetoService) {}
|
||||
|
||||
func (m *MockTenantServiceForConsent) DeleteTenantsBulk(ctx context.Context, ids []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Test Helpers ---
|
||||
|
||||
func newConsentTestApp(h *AuthHandler) *fiber.App {
|
||||
@@ -100,11 +217,36 @@ func TestGetConsentRequest_AddsMandatoryTenantScope(t *testing.T) {
|
||||
http.DefaultClient = client
|
||||
defer func() { http.DefaultClient = origDefault }()
|
||||
|
||||
mockTenantSvc := &MockTenantServiceForConsent{}
|
||||
mockKratosAdmin := &MockKratosAdminServiceForConsent{}
|
||||
|
||||
// Mock profile resolution to allow tenant access
|
||||
mockKratosAdmin.On("GetIdentity", mock.Anything, "user-123").Return(&service.KratosIdentity{
|
||||
ID: "user-123",
|
||||
Traits: map[string]interface{}{
|
||||
"email": "user@example.com",
|
||||
},
|
||||
}, nil)
|
||||
|
||||
mockTenantSvc.On("GetTenant", mock.Anything, "tenant-allow").Return(&domain.Tenant{
|
||||
ID: "tenant-allow",
|
||||
Slug: "tenant-allow",
|
||||
Name: "Allowed Tenant",
|
||||
}, nil)
|
||||
|
||||
// Mock hydration calls
|
||||
mockTenantSvc.On("ListJoinedTenants", mock.Anything, mock.Anything).Return([]domain.Tenant{
|
||||
{ID: "tenant-allow", Slug: "tenant-allow", Name: "Allowed Tenant"},
|
||||
}, nil)
|
||||
mockTenantSvc.On("ListManageableTenants", mock.Anything, mock.Anything).Return([]domain.Tenant{}, nil)
|
||||
|
||||
h := &AuthHandler{
|
||||
Hydra: &service.HydraAdminService{
|
||||
AdminURL: "http://hydra.test",
|
||||
HTTPClient: client,
|
||||
},
|
||||
TenantService: mockTenantSvc,
|
||||
KratosAdmin: mockKratosAdmin,
|
||||
}
|
||||
app := newConsentTestApp(h)
|
||||
|
||||
@@ -163,16 +305,17 @@ func TestGetConsentRequest_Skip_AutoAccept(t *testing.T) {
|
||||
defer func() { http.DefaultClient = origDefault }()
|
||||
|
||||
consentRepo := &mockConsentRepo{}
|
||||
mockKratosAdmin := &MockKratosAdminServiceForConsent{}
|
||||
|
||||
h := &AuthHandler{
|
||||
Hydra: &service.HydraAdminService{
|
||||
AdminURL: "http://hydra.test",
|
||||
HTTPClient: client,
|
||||
},
|
||||
KratosAdmin: new(MockKratosAdminService), // Reusing MockKratosAdminService if defined or use MockKratosAdminServiceShared
|
||||
KratosAdmin: mockKratosAdmin,
|
||||
ConsentRepo: consentRepo,
|
||||
}
|
||||
h.KratosAdmin.(*MockKratosAdminService).On("GetIdentity", mock.Anything, "user-123").Return(&service.KratosIdentity{
|
||||
mockKratosAdmin.On("GetIdentity", mock.Anything, "user-123").Return(&service.KratosIdentity{
|
||||
ID: "user-123",
|
||||
Traits: map[string]interface{}{
|
||||
"email": "user@test.com",
|
||||
@@ -199,7 +342,8 @@ func TestAcceptConsentRequest_Normal(t *testing.T) {
|
||||
"requested_scope": []string{"openid", "profile"},
|
||||
"subject": "user-123",
|
||||
"client": map[string]interface{}{
|
||||
"client_id": "client-app",
|
||||
"client_id": "client-app",
|
||||
"client_name": "Test App",
|
||||
},
|
||||
}), nil
|
||||
}
|
||||
@@ -226,17 +370,18 @@ func TestAcceptConsentRequest_Normal(t *testing.T) {
|
||||
|
||||
auditRepo := &mockAuditRepo{}
|
||||
consentRepo := &mockConsentRepo{}
|
||||
mockKratosAdmin := &MockKratosAdminServiceForConsent{}
|
||||
|
||||
h := &AuthHandler{
|
||||
Hydra: &service.HydraAdminService{
|
||||
AdminURL: "http://hydra.test",
|
||||
HTTPClient: client,
|
||||
},
|
||||
KratosAdmin: new(MockKratosAdminService),
|
||||
KratosAdmin: mockKratosAdmin,
|
||||
AuditRepo: auditRepo,
|
||||
ConsentRepo: consentRepo,
|
||||
}
|
||||
h.KratosAdmin.(*MockKratosAdminService).On("GetIdentity", mock.Anything, "user-123").Return(&service.KratosIdentity{
|
||||
mockKratosAdmin.On("GetIdentity", mock.Anything, "user-123").Return(&service.KratosIdentity{
|
||||
ID: "user-123",
|
||||
Traits: map[string]interface{}{
|
||||
"email": "user@test.com",
|
||||
@@ -260,6 +405,8 @@ func TestAcceptConsentRequest_Normal(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAcceptConsentRequest_EnforcesMandatoryTenantScope(t *testing.T) {
|
||||
t.Setenv("APP_ENV", "dev")
|
||||
|
||||
var capturedGrantScopes []string
|
||||
|
||||
transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
||||
@@ -309,14 +456,16 @@ func TestAcceptConsentRequest_EnforcesMandatoryTenantScope(t *testing.T) {
|
||||
http.DefaultClient = client
|
||||
defer func() { http.DefaultClient = origDefault }()
|
||||
|
||||
mockKratosAdmin := &MockKratosAdminServiceForConsent{}
|
||||
|
||||
h := &AuthHandler{
|
||||
Hydra: &service.HydraAdminService{
|
||||
AdminURL: "http://hydra.test",
|
||||
HTTPClient: client,
|
||||
},
|
||||
KratosAdmin: new(MockKratosAdminService),
|
||||
KratosAdmin: mockKratosAdmin,
|
||||
}
|
||||
h.KratosAdmin.(*MockKratosAdminService).On("GetIdentity", mock.Anything, "user-123").Return(&service.KratosIdentity{
|
||||
mockKratosAdmin.On("GetIdentity", mock.Anything, "user-123").Return(&service.KratosIdentity{
|
||||
ID: "user-123",
|
||||
Traits: map[string]interface{}{
|
||||
"email": "user@test.com",
|
||||
|
||||
@@ -184,6 +184,7 @@ func TestGetConsentRequest_DeniesTenantAccess(t *testing.T) {
|
||||
app := fiber.New()
|
||||
app.Get("/api/v1/auth/consent", h.GetConsentRequest)
|
||||
|
||||
t.Setenv("APP_ENV", "dev")
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/auth/consent?consent_challenge=challenge-tenant", nil)
|
||||
req.Header.Set("X-Mock-Role", "user")
|
||||
req.Header.Set("X-Tenant-ID", "tenant-a")
|
||||
|
||||
@@ -13,47 +13,47 @@ import (
|
||||
|
||||
// Ory 계열(kratos/hydra) 공급자 문자열을 정규화하기 위한 매핑.
|
||||
var providerAliases = map[string]string{
|
||||
"ory": "ory",
|
||||
"hydra": "ory",
|
||||
"kratos": "ory",
|
||||
"ory-kratos": "ory",
|
||||
"ory_hydra": "ory",
|
||||
"ory_kratos": "ory",
|
||||
"ory": "ory",
|
||||
"hydra": "ory",
|
||||
"kratos": "ory",
|
||||
"ory-kratos": "ory",
|
||||
"ory_hydra": "ory",
|
||||
"ory_kratos": "ory",
|
||||
}
|
||||
|
||||
// getEnv는 환경 변수를 읽거나 대체 값을 반환하는 헬퍼 함수입니다.
|
||||
func getEnv(key, fallback string) string {
|
||||
if value, ok := os.LookupEnv(key); ok {
|
||||
return value
|
||||
}
|
||||
return fallback
|
||||
if value, ok := os.LookupEnv(key); ok {
|
||||
return value
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
// InitializeProvider는 환경 설정을 기반으로 IDP 공급자를 생성하고 반환합니다.
|
||||
// 이것은 IdentityProvider 인터페이스의 팩토리 역할을 합니다.
|
||||
func InitializeProvider() (domain.IdentityProvider, error) {
|
||||
rawProviders := getEnv("IDP_PROVIDER", "ory")
|
||||
providers := strings.Split(rawProviders, ",")
|
||||
slog.Info("Initializing IDP chain", "providers", rawProviders)
|
||||
rawProviders := getEnv("IDP_PROVIDER", "ory")
|
||||
providers := strings.Split(rawProviders, ",")
|
||||
slog.Info("Initializing IDP chain", "providers", rawProviders)
|
||||
|
||||
var initialized []domain.IdentityProvider
|
||||
for _, p := range providers {
|
||||
providerName := strings.TrimSpace(strings.ToLower(p))
|
||||
if canonical, ok := providerAliases[providerName]; ok {
|
||||
providerName = canonical
|
||||
}
|
||||
var initialized []domain.IdentityProvider
|
||||
for _, p := range providers {
|
||||
providerName := strings.TrimSpace(strings.ToLower(p))
|
||||
if canonical, ok := providerAliases[providerName]; ok {
|
||||
providerName = canonical
|
||||
}
|
||||
|
||||
switch providerName {
|
||||
case "ory":
|
||||
// Kratos/Hydra 주 공급자
|
||||
oryProvider := service.NewOryProvider()
|
||||
initialized = append(initialized, oryProvider)
|
||||
switch providerName {
|
||||
case "ory":
|
||||
// Kratos/Hydra 주 공급자
|
||||
oryProvider := service.NewOryProvider()
|
||||
initialized = append(initialized, oryProvider)
|
||||
|
||||
default:
|
||||
// 알 수 없는 공급자는 건너뛰고 다음 후보를 시도
|
||||
slog.Warn("Skipping unsupported IDP provider entry", "provider", providerName)
|
||||
}
|
||||
}
|
||||
default:
|
||||
// 알 수 없는 공급자는 건너뛰고 다음 후보를 시도
|
||||
slog.Warn("Skipping unsupported IDP provider entry", "provider", providerName)
|
||||
}
|
||||
}
|
||||
if len(initialized) == 0 {
|
||||
return nil, fmt.Errorf("no valid IDP_PROVIDER entries configured from: %s", rawProviders)
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
// Auto-migrate
|
||||
err = db.AutoMigrate(&domain.Tenant{}, &domain.TenantDomain{}, &domain.User{})
|
||||
err = db.AutoMigrate(&domain.Tenant{}, &domain.TenantDomain{}, &domain.User{}, &domain.ClientConsent{})
|
||||
if err != nil {
|
||||
log.Fatalf("failed to migrate database: %s", err)
|
||||
}
|
||||
|
||||
@@ -174,10 +174,7 @@ function ClientGeneralPage() {
|
||||
{
|
||||
id: "2",
|
||||
name: "tenant",
|
||||
description: t(
|
||||
"msg.dev.clients.scopes.tenant",
|
||||
"소속 테넌트 정보 접근",
|
||||
),
|
||||
description: t("msg.dev.clients.scopes.tenant", "소속 테넌트 정보 접근"),
|
||||
mandatory: false,
|
||||
},
|
||||
{
|
||||
@@ -347,12 +344,15 @@ function ClientGeneralPage() {
|
||||
return {
|
||||
...scope,
|
||||
description: scope.description || tenantScopeDescription,
|
||||
mandatory: restricted ? true : false,
|
||||
mandatory: restricted,
|
||||
locked: restricted,
|
||||
};
|
||||
});
|
||||
|
||||
if (restricted && !normalized.some((scope) => scope.name.trim() === "tenant")) {
|
||||
if (
|
||||
restricted &&
|
||||
!normalized.some((scope) => scope.name.trim() === "tenant")
|
||||
) {
|
||||
normalized.push(buildTenantScope(`tenant-${Date.now()}`));
|
||||
}
|
||||
|
||||
@@ -526,7 +526,8 @@ function ClientGeneralPage() {
|
||||
|
||||
const hasValidationErrors = validationErrors.length > 0;
|
||||
const normalizedTenantSearch = tenantSearch.trim().toLowerCase();
|
||||
const tenantOptions: Array<TenantSummary | MyTenantSummary> = tenantData ?? [];
|
||||
const tenantOptions: Array<TenantSummary | MyTenantSummary> =
|
||||
tenantData ?? [];
|
||||
const filteredTenants = tenantOptions.filter((tenant) => {
|
||||
if (!normalizedTenantSearch) {
|
||||
return true;
|
||||
@@ -1375,10 +1376,7 @@ function ClientGeneralPage() {
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t(
|
||||
"ui.common.delete",
|
||||
"삭제",
|
||||
)}
|
||||
aria-label={t("ui.common.delete", "삭제")}
|
||||
onClick={() => toggleAllowedTenant(tenant.id)}
|
||||
className="text-muted-foreground transition hover:text-destructive"
|
||||
>
|
||||
@@ -1403,10 +1401,7 @@ function ClientGeneralPage() {
|
||||
<span className="max-w-44 truncate">{tenantId}</span>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t(
|
||||
"ui.common.delete",
|
||||
"삭제",
|
||||
)}
|
||||
aria-label={t("ui.common.delete", "삭제")}
|
||||
onClick={() => toggleAllowedTenant(tenantId)}
|
||||
className="text-muted-foreground transition hover:text-destructive"
|
||||
>
|
||||
|
||||
@@ -650,6 +650,22 @@ title_generic = "An error occurred."
|
||||
title_with_code = "Error: {{code}}"
|
||||
type = "Error type: {{type}}"
|
||||
|
||||
[msg.userfront.error.tenant]
|
||||
account = "Account"
|
||||
account_unknown = "Unknown"
|
||||
affiliated_tenants = "All affiliated tenants"
|
||||
allowed_box_title = "Allowed tenants"
|
||||
allowed_tenants = "Allowed tenants"
|
||||
detail = "The currently signed-in account cannot access this application."
|
||||
load_failed = "We could not confirm the account details. Please try again."
|
||||
loading = "Loading the current account details."
|
||||
lookup_fallback = "Some fields could not be verified because the access context was incomplete."
|
||||
page_title = "Access to this application is restricted"
|
||||
primary_tenant = "Primary affiliated tenant"
|
||||
tenant = "Tenant"
|
||||
tenant_unknown = "Unknown"
|
||||
title = "Access restriction details"
|
||||
|
||||
[msg.userfront.error.ory]
|
||||
"$normalizedCode" = "{{error}}"
|
||||
access_denied = "The user denied the consent request."
|
||||
@@ -2239,6 +2255,7 @@ windows = "Desktop(Windows)"
|
||||
[ui.userfront.error]
|
||||
go_home = "Go Home"
|
||||
go_login = "Go Login"
|
||||
switch_account = "Sign in with another account"
|
||||
|
||||
[ui.userfront.forgot]
|
||||
heading = "Forgot your password?"
|
||||
|
||||
@@ -208,6 +208,22 @@ title_generic = "오류가 발생했습니다"
|
||||
title_with_code = "오류: {{code}}"
|
||||
type = "오류 종류: {{type}}"
|
||||
|
||||
[msg.userfront.error.tenant]
|
||||
account = "계정"
|
||||
account_unknown = "알 수 없음"
|
||||
affiliated_tenants = "전체 소속 테넌트"
|
||||
allowed_box_title = "접속 가능 테넌트"
|
||||
allowed_tenants = "접속 가능 테넌트"
|
||||
detail = "현재 로그인된 계정은 이 애플리케이션에 접근할 수 없습니다."
|
||||
load_failed = "계정 정보를 확인하지 못했습니다. 다시 시도해 주세요."
|
||||
loading = "현재 계정 정보를 불러오는 중입니다."
|
||||
lookup_fallback = "표시 정보가 충분하지 않아 일부 항목은 확인되지 않을 수 있습니다."
|
||||
page_title = "애플리케이션 접근이 제한되었습니다"
|
||||
primary_tenant = "대표 소속 테넌트"
|
||||
tenant = "소속 테넌트"
|
||||
tenant_unknown = "알 수 없음"
|
||||
title = "접근 제한 정보"
|
||||
|
||||
[msg.userfront.forgot]
|
||||
description = "계정과 연결된 이메일 주소 또는 휴대폰 번호를 입력하시면, 비밀번호를 재설정할 수 있는 링크를 보내드립니다."
|
||||
dry_send = "drySend 모드: 실제 이메일/SMS는 발송되지 않습니다."
|
||||
@@ -456,6 +472,7 @@ windows = "Desktop(Windows)"
|
||||
[ui.userfront.error]
|
||||
go_home = "홈으로 이동"
|
||||
go_login = "로그인으로 이동"
|
||||
switch_account = "다른 계정으로 로그인"
|
||||
|
||||
[ui.userfront.forgot]
|
||||
heading = "비밀번호를 잊으셨나요?"
|
||||
@@ -1088,6 +1105,22 @@ title_generic = "오류가 발생했습니다"
|
||||
title_with_code = "오류: {{code}}"
|
||||
type = "오류 종류: {{type}}"
|
||||
|
||||
[msg.userfront.error.tenant]
|
||||
account = "계정"
|
||||
account_unknown = "알 수 없음"
|
||||
affiliated_tenants = "전체 소속 테넌트"
|
||||
allowed_box_title = "접속 가능 테넌트"
|
||||
allowed_tenants = "접속 가능 테넌트"
|
||||
detail = "현재 로그인된 계정은 이 애플리케이션에 접근할 수 없습니다."
|
||||
load_failed = "계정 정보를 확인하지 못했습니다. 다시 시도해 주세요."
|
||||
loading = "현재 계정 정보를 불러오는 중입니다."
|
||||
lookup_fallback = "표시 정보가 충분하지 않아 일부 항목은 확인되지 않을 수 있습니다."
|
||||
page_title = "애플리케이션 접근이 제한되었습니다"
|
||||
primary_tenant = "대표 소속 테넌트"
|
||||
tenant = "소속 테넌트"
|
||||
tenant_unknown = "알 수 없음"
|
||||
title = "접근 제한 정보"
|
||||
|
||||
[msg.userfront.error.ory]
|
||||
"$normalizedCode" = "{{error}}"
|
||||
access_denied = "사용자가 동의를 거부했습니다."
|
||||
@@ -2638,6 +2671,7 @@ windows = "Desktop(Windows)"
|
||||
[ui.userfront.error]
|
||||
go_home = "홈으로 이동"
|
||||
go_login = "로그인으로 이동"
|
||||
switch_account = "다른 계정으로 로그인"
|
||||
|
||||
[ui.userfront.forgot]
|
||||
heading = "비밀번호를 잊으셨나요?"
|
||||
|
||||
@@ -83,6 +83,22 @@ title_generic = ""
|
||||
title_with_code = ""
|
||||
type = ""
|
||||
|
||||
[msg.userfront.error.tenant]
|
||||
account = ""
|
||||
account_unknown = ""
|
||||
affiliated_tenants = ""
|
||||
allowed_box_title = ""
|
||||
allowed_tenants = ""
|
||||
detail = ""
|
||||
load_failed = ""
|
||||
loading = ""
|
||||
lookup_fallback = ""
|
||||
page_title = ""
|
||||
primary_tenant = ""
|
||||
tenant = ""
|
||||
tenant_unknown = ""
|
||||
title = ""
|
||||
|
||||
[msg.userfront.forgot]
|
||||
description = ""
|
||||
dry_send = ""
|
||||
@@ -331,6 +347,7 @@ windows = ""
|
||||
[ui.userfront.error]
|
||||
go_home = ""
|
||||
go_login = ""
|
||||
switch_account = ""
|
||||
|
||||
[ui.userfront.forgot]
|
||||
heading = ""
|
||||
@@ -963,6 +980,22 @@ title_generic = ""
|
||||
title_with_code = ""
|
||||
type = ""
|
||||
|
||||
[msg.userfront.error.tenant]
|
||||
account = ""
|
||||
account_unknown = ""
|
||||
affiliated_tenants = ""
|
||||
allowed_box_title = ""
|
||||
allowed_tenants = ""
|
||||
detail = ""
|
||||
load_failed = ""
|
||||
loading = ""
|
||||
lookup_fallback = ""
|
||||
page_title = ""
|
||||
primary_tenant = ""
|
||||
tenant = ""
|
||||
tenant_unknown = ""
|
||||
title = ""
|
||||
|
||||
[msg.userfront.error.ory]
|
||||
"$normalizedCode" = ""
|
||||
access_denied = ""
|
||||
@@ -2513,6 +2546,7 @@ windows = ""
|
||||
[ui.userfront.error]
|
||||
go_home = ""
|
||||
go_login = ""
|
||||
switch_account = ""
|
||||
|
||||
[ui.userfront.forgot]
|
||||
heading = ""
|
||||
|
||||
@@ -5,9 +5,14 @@ job_name="${1:-adminfront-tests}"
|
||||
|
||||
mkdir -p reports
|
||||
|
||||
if [ -n "${CI:-}" ]; then
|
||||
playwright_install_cmd=(npx playwright install --with-deps)
|
||||
playwright_install_desc="npx playwright install --with-deps"
|
||||
PLAYWRIGHT_BROWSERS_PATH="${HOME}/.cache/ms-playwright"
|
||||
PLAYWRIGHT_CHROMIUM_COMPLETE="${PLAYWRIGHT_BROWSERS_PATH}/chromium-1208/INSTALLATION_COMPLETE"
|
||||
PLAYWRIGHT_FIREFOX_COMPLETE="${PLAYWRIGHT_BROWSERS_PATH}/firefox-1509/INSTALLATION_COMPLETE"
|
||||
PLAYWRIGHT_WEBKIT_COMPLETE="${PLAYWRIGHT_BROWSERS_PATH}/webkit-2248/INSTALLATION_COMPLETE"
|
||||
|
||||
if [ -f "$PLAYWRIGHT_CHROMIUM_COMPLETE" ] && [ -f "$PLAYWRIGHT_FIREFOX_COMPLETE" ] && [ -f "$PLAYWRIGHT_WEBKIT_COMPLETE" ]; then
|
||||
playwright_install_cmd=(sh -c 'echo "Playwright browsers already installed"')
|
||||
playwright_install_desc="Playwright browsers already installed"
|
||||
else
|
||||
playwright_install_cmd=(npx playwright install)
|
||||
playwright_install_desc="npx playwright install"
|
||||
@@ -16,7 +21,7 @@ fi
|
||||
set +e
|
||||
(
|
||||
cd adminfront
|
||||
npm ci
|
||||
npm ci --ignore-scripts
|
||||
) 2>&1 | tee reports/adminfront-install.log
|
||||
install_exit_code=${PIPESTATUS[0]}
|
||||
set -e
|
||||
@@ -31,7 +36,7 @@ if [ "$install_exit_code" -ne 0 ]; then
|
||||
echo "- Exit Code: \`$install_exit_code\`"
|
||||
echo
|
||||
echo "## Command"
|
||||
echo "\`cd adminfront && npm ci\`"
|
||||
echo "\`cd adminfront && npm ci --ignore-scripts\`"
|
||||
echo
|
||||
echo "## Install Log Tail (last 200 lines)"
|
||||
echo '```text'
|
||||
@@ -70,7 +75,7 @@ if [ "$provision_exit_code" -ne 0 ]; then
|
||||
fi
|
||||
|
||||
set +e
|
||||
port="$(node -e "const net=require('node:net'); const s=net.createServer(); s.listen(0,'127.0.0.1',()=>{console.log(s.address().port); s.close();});")"
|
||||
port="${PORT:-5173}"
|
||||
echo "==> adminfront using PORT=$port"
|
||||
(
|
||||
cd adminfront
|
||||
@@ -89,7 +94,7 @@ if [ "$test_exit_code" -ne 0 ]; then
|
||||
echo
|
||||
echo "## Commands"
|
||||
echo "1. \`cd adminfront\`"
|
||||
echo "2. \`npm ci\`"
|
||||
echo "2. \`npm ci --ignore-scripts\`"
|
||||
echo "3. \`${playwright_install_desc}\`"
|
||||
echo "4. \`npm test\`"
|
||||
echo
|
||||
|
||||
@@ -140,9 +140,15 @@ type = "Error type: {type}"
|
||||
[msg.userfront.error.tenant]
|
||||
account = "Account"
|
||||
account_unknown = "Unknown"
|
||||
affiliated_tenants = "All affiliated tenants"
|
||||
allowed_box_title = "Allowed tenants"
|
||||
allowed_tenants = "Allowed tenants"
|
||||
detail = "The currently signed-in account cannot access this application."
|
||||
load_failed = "We could not confirm the account details. Please try again."
|
||||
loading = "Loading the current account details."
|
||||
lookup_fallback = "Some fields could not be verified because the access context was incomplete."
|
||||
page_title = "Access to this application is restricted"
|
||||
primary_tenant = "Primary affiliated tenant"
|
||||
tenant = "Tenant"
|
||||
tenant_unknown = "Unknown"
|
||||
title = "Access restriction details"
|
||||
|
||||
@@ -81,9 +81,15 @@ type = "오류 종류: {type}"
|
||||
[msg.userfront.error.tenant]
|
||||
account = "계정"
|
||||
account_unknown = "알 수 없음"
|
||||
affiliated_tenants = "전체 소속 테넌트"
|
||||
allowed_box_title = "접속 가능 테넌트"
|
||||
allowed_tenants = "접속 가능 테넌트"
|
||||
detail = "현재 로그인된 계정은 이 애플리케이션에 접근할 수 없습니다."
|
||||
load_failed = "계정 정보를 확인하지 못했습니다. 다시 시도해 주세요."
|
||||
loading = "현재 계정 정보를 불러오는 중입니다."
|
||||
lookup_fallback = "표시 정보가 충분하지 않아 일부 항목은 확인되지 않을 수 있습니다."
|
||||
page_title = "애플리케이션 접근이 제한되었습니다"
|
||||
primary_tenant = "대표 소속 테넌트"
|
||||
tenant = "소속 테넌트"
|
||||
tenant_unknown = "알 수 없음"
|
||||
title = "접근 제한 정보"
|
||||
@@ -355,6 +361,22 @@ title_generic = "오류가 발생했습니다"
|
||||
title_with_code = "오류: {code}"
|
||||
type = "오류 종류: {type}"
|
||||
|
||||
[msg.userfront.error.tenant]
|
||||
account = "계정"
|
||||
account_unknown = "알 수 없음"
|
||||
affiliated_tenants = "전체 소속 테넌트"
|
||||
allowed_box_title = "접속 가능 테넌트"
|
||||
allowed_tenants = "접속 가능 테넌트"
|
||||
detail = "현재 로그인된 계정은 이 애플리케이션에 접근할 수 없습니다."
|
||||
load_failed = "계정 정보를 확인하지 못했습니다. 다시 시도해 주세요."
|
||||
loading = "현재 계정 정보를 불러오는 중입니다."
|
||||
lookup_fallback = "표시 정보가 충분하지 않아 일부 항목은 확인되지 않을 수 있습니다."
|
||||
page_title = "애플리케이션 접근이 제한되었습니다"
|
||||
primary_tenant = "대표 소속 테넌트"
|
||||
tenant = "소속 테넌트"
|
||||
tenant_unknown = "알 수 없음"
|
||||
title = "접근 제한 정보"
|
||||
|
||||
[msg.userfront.error.ory]
|
||||
"$normalizedCode" = "{error}"
|
||||
access_denied = "사용자가 동의를 거부했습니다."
|
||||
@@ -722,6 +744,7 @@ windows = "Desktop(Windows)"
|
||||
[ui.userfront.error]
|
||||
go_home = "홈으로 이동"
|
||||
go_login = "로그인으로 이동"
|
||||
switch_account = "다른 계정으로 로그인"
|
||||
|
||||
[ui.userfront.forgot]
|
||||
heading = "비밀번호를 잊으셨나요?"
|
||||
|
||||
@@ -53,9 +53,15 @@ type = ""
|
||||
[msg.userfront.error.tenant]
|
||||
account = ""
|
||||
account_unknown = ""
|
||||
affiliated_tenants = ""
|
||||
allowed_box_title = ""
|
||||
allowed_tenants = ""
|
||||
detail = ""
|
||||
load_failed = ""
|
||||
loading = ""
|
||||
lookup_fallback = ""
|
||||
page_title = ""
|
||||
primary_tenant = ""
|
||||
tenant = ""
|
||||
tenant_unknown = ""
|
||||
title = ""
|
||||
@@ -327,6 +333,22 @@ title_generic = ""
|
||||
title_with_code = ""
|
||||
type = ""
|
||||
|
||||
[msg.userfront.error.tenant]
|
||||
account = ""
|
||||
account_unknown = ""
|
||||
affiliated_tenants = ""
|
||||
allowed_box_title = ""
|
||||
allowed_tenants = ""
|
||||
detail = ""
|
||||
load_failed = ""
|
||||
loading = ""
|
||||
lookup_fallback = ""
|
||||
page_title = ""
|
||||
primary_tenant = ""
|
||||
tenant = ""
|
||||
tenant_unknown = ""
|
||||
title = ""
|
||||
|
||||
[msg.userfront.error.ory]
|
||||
"$normalizedCode" = ""
|
||||
access_denied = ""
|
||||
@@ -694,6 +716,7 @@ windows = ""
|
||||
[ui.userfront.error]
|
||||
go_home = ""
|
||||
go_login = ""
|
||||
switch_account = ""
|
||||
|
||||
[ui.userfront.forgot]
|
||||
heading = ""
|
||||
|
||||
Reference in New Issue
Block a user