Fix tenant SSO status mode and refactor status UI tiles
This commit is contained in:
@@ -31,6 +31,7 @@ return [
|
||||
'css/components/app-empty-state.css',
|
||||
'css/components/app-details.css',
|
||||
'css/components/app-details-card.css',
|
||||
'css/components/app-form-info-tile.css',
|
||||
'css/components/app-list-table.css',
|
||||
'css/components/app-list-tabs.css',
|
||||
'css/components/app-tabs.css',
|
||||
|
||||
@@ -856,6 +856,7 @@
|
||||
"Password login is disabled for this tenant. Please use Microsoft login.": "Passwort-Login ist für diesen Mandanten deaktiviert. Bitte nutze Microsoft-Login.",
|
||||
"Password login is disabled for all assigned tenants": "Passwort-Login ist für alle zugewiesenen Mandanten deaktiviert.",
|
||||
"Password login mode": "Passwort-Login-Modus",
|
||||
"Local password only": "Nur lokales Passwort",
|
||||
"Local password + Microsoft": "Lokales Passwort + Microsoft",
|
||||
"Credentials source": "Quelle der Zugangsdaten",
|
||||
"Shared app": "Geteilte App",
|
||||
|
||||
@@ -856,6 +856,7 @@
|
||||
"Password login is disabled for this tenant. Please use Microsoft login.": "Password login is disabled for this tenant. Please use Microsoft login.",
|
||||
"Password login is disabled for all assigned tenants": "Password login is disabled for all assigned tenants",
|
||||
"Password login mode": "Password login mode",
|
||||
"Local password only": "Local password only",
|
||||
"Local password + Microsoft": "Local password + Microsoft",
|
||||
"Credentials source": "Credentials source",
|
||||
"Shared app": "Shared app",
|
||||
|
||||
@@ -110,9 +110,9 @@ class TenantSsoService
|
||||
'config_complete' => !$state['enabled'] || $configState['complete'],
|
||||
'config_error_code' => (string) ($configState['error_code'] ?? ''),
|
||||
'config_error_label' => $this->microsoftConfigErrorLabel((string) ($configState['error_code'] ?? '')),
|
||||
'password_mode' => $state['enabled'] && $state['enforce_microsoft_login']
|
||||
? 'microsoft_only'
|
||||
: 'local_and_microsoft',
|
||||
'password_mode' => !$state['enabled']
|
||||
? 'local_only'
|
||||
: ($state['enforce_microsoft_login'] ? 'microsoft_only' : 'local_and_microsoft'),
|
||||
'credential_source' => $state['use_shared_app'] ? 'shared' : 'override',
|
||||
'sync_needs_graph' => $state['sync_profile_on_login']
|
||||
&& $this->profileSyncFieldsRequireGraph($state['sync_profile_fields']),
|
||||
|
||||
@@ -69,9 +69,41 @@ $ssoUiState = is_array($ssoUiState ?? null)
|
||||
: $tenantSsoService->buildMicrosoftUiState($tenantId, $values);
|
||||
$ssoConfigComplete = !empty($ssoUiState['config_complete']);
|
||||
$ssoConfigErrorLabel = trim((string) ($ssoUiState['config_error_label'] ?? ''));
|
||||
$ssoPasswordMode = (string) ($ssoUiState['password_mode'] ?? ($microsoftEnforce ? 'microsoft_only' : 'local_and_microsoft'));
|
||||
$ssoPasswordMode = (string) ($ssoUiState['password_mode'] ?? ($microsoftEnforce ? 'microsoft_only' : ($microsoftEnabled ? 'local_and_microsoft' : 'local_only')));
|
||||
$ssoCredentialSource = (string) ($ssoUiState['credential_source'] ?? ($useSharedApp ? 'shared' : 'override'));
|
||||
$ssoSyncNeedsGraph = !empty($ssoUiState['sync_needs_graph']);
|
||||
$ssoPasswordModeLabel = match ($ssoPasswordMode) {
|
||||
'microsoft_only' => t('Microsoft only'),
|
||||
'local_and_microsoft' => t('Local password + Microsoft'),
|
||||
default => t('Local password only'),
|
||||
};
|
||||
$ssoCredentialSourceLabel = $ssoCredentialSource === 'shared' ? t('Shared app') : t('Tenant override');
|
||||
$ssoStatusTiles = [
|
||||
[
|
||||
'label' => t('Microsoft login'),
|
||||
'value' => $microsoftEnabled ? t('Active') : t('Inactive'),
|
||||
'tone' => $microsoftEnabled ? 'success' : 'neutral',
|
||||
],
|
||||
[
|
||||
'label' => t('Configuration complete'),
|
||||
'value' => $ssoConfigComplete ? t('Yes') : t('No'),
|
||||
'tone' => $ssoConfigComplete ? 'success' : 'warning',
|
||||
],
|
||||
[
|
||||
'label' => t('Password login mode'),
|
||||
'value' => $ssoPasswordModeLabel,
|
||||
'tone' => match ($ssoPasswordMode) {
|
||||
'microsoft_only' => 'warning',
|
||||
'local_and_microsoft' => 'info',
|
||||
default => 'neutral',
|
||||
},
|
||||
],
|
||||
[
|
||||
'label' => t('Credentials source'),
|
||||
'value' => $ssoCredentialSourceLabel,
|
||||
'tone' => $ssoCredentialSource === 'shared' ? 'info' : 'neutral',
|
||||
],
|
||||
];
|
||||
|
||||
?>
|
||||
<form id="<?php e($formId); ?>" method="post" data-standard-detail-form="1">
|
||||
@@ -407,39 +439,20 @@ $ssoSyncNeedsGraph = !empty($ssoUiState['sync_needs_graph']);
|
||||
|
||||
<?php if ($showSsoTab): ?>
|
||||
<div data-tab-panel="sso" data-tenant-sso-root>
|
||||
<fieldset>
|
||||
<legend><small><?php e(t('SSO configuration status')); ?></small></legend>
|
||||
<div class="grid grid-1-1">
|
||||
<div>
|
||||
<small><?php e(t('Microsoft login')); ?></small>
|
||||
<div><strong><?php e($microsoftEnabled ? t('Active') : t('Inactive')); ?></strong></div>
|
||||
</div>
|
||||
<div>
|
||||
<small><?php e(t('Configuration complete')); ?></small>
|
||||
<div><strong><?php e($ssoConfigComplete ? t('Yes') : t('No')); ?></strong></div>
|
||||
</div>
|
||||
<div>
|
||||
<small><?php e(t('Password login mode')); ?></small>
|
||||
<div>
|
||||
<strong>
|
||||
<?php e($ssoPasswordMode === 'microsoft_only' ? t('Microsoft only') : t('Local password + Microsoft')); ?>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<small><?php e(t('Credentials source')); ?></small>
|
||||
<div>
|
||||
<strong>
|
||||
<?php e($ssoCredentialSource === 'shared' ? t('Shared app') : t('Tenant override')); ?>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
<small class="app-form-info-tiles-title"><?php e(t('SSO configuration status')); ?></small>
|
||||
<div class="app-form-info-tiles">
|
||||
<?php foreach ($ssoStatusTiles as $ssoStatusTile): ?>
|
||||
<?php
|
||||
$infoTileLabel = (string) ($ssoStatusTile['label'] ?? '');
|
||||
$infoTileValue = (string) ($ssoStatusTile['value'] ?? '');
|
||||
$infoTileTone = (string) ($ssoStatusTile['tone'] ?? 'neutral');
|
||||
require templatePath('partials/app-form-info-tile.phtml');
|
||||
?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php if ($microsoftEnabled && !$ssoConfigComplete && $ssoConfigErrorLabel !== ''): ?>
|
||||
<hr>
|
||||
<small class="muted"><?php e($ssoConfigErrorLabel); ?></small>
|
||||
<?php endif; ?>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend><small><?php e(t('Required Microsoft configuration')); ?></small></legend>
|
||||
|
||||
24
templates/partials/app-form-info-tile.phtml
Normal file
24
templates/partials/app-form-info-tile.phtml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @var string $infoTileLabel
|
||||
* @var string $infoTileValue
|
||||
* @var string $infoTileTone
|
||||
*/
|
||||
|
||||
$infoTileLabel = trim((string) ($infoTileLabel ?? ''));
|
||||
$infoTileValue = trim((string) ($infoTileValue ?? ''));
|
||||
$infoTileTone = strtolower(trim((string) ($infoTileTone ?? 'neutral')));
|
||||
if (!in_array($infoTileTone, ['neutral', 'info', 'success', 'warning'], true)) {
|
||||
$infoTileTone = 'neutral';
|
||||
}
|
||||
|
||||
?>
|
||||
<article class="app-form-info-tile app-form-info-tile--<?php e($infoTileTone); ?>">
|
||||
<?php if ($infoTileLabel !== ''): ?>
|
||||
<small class="app-form-info-tile-label"><?php e($infoTileLabel); ?></small>
|
||||
<?php endif; ?>
|
||||
<?php if ($infoTileValue !== ''): ?>
|
||||
<strong class="app-form-info-tile-value"><?php e($infoTileValue); ?></strong>
|
||||
<?php endif; ?>
|
||||
</article>
|
||||
@@ -159,6 +159,48 @@ class TenantSsoServiceTest extends TestCase
|
||||
$this->assertSame('sso_disabled', $result['microsoft_reason']);
|
||||
}
|
||||
|
||||
public function testBuildMicrosoftUiStateReturnsLocalOnlyPasswordModeWhenSsoDisabled(): void
|
||||
{
|
||||
$repository = $this->createMock(TenantMicrosoftAuthRepositoryInterface::class);
|
||||
$repository->method('findByTenantId')->willReturn([
|
||||
'enabled' => 0,
|
||||
'enforce_microsoft_login' => 1,
|
||||
]);
|
||||
|
||||
$service = $this->newService($repository);
|
||||
$result = $service->buildMicrosoftUiState(5);
|
||||
|
||||
$this->assertSame('local_only', $result['password_mode']);
|
||||
}
|
||||
|
||||
public function testBuildMicrosoftUiStateReturnsLocalAndMicrosoftPasswordModeWhenSsoEnabledWithoutEnforcement(): void
|
||||
{
|
||||
$repository = $this->createMock(TenantMicrosoftAuthRepositoryInterface::class);
|
||||
$repository->method('findByTenantId')->willReturn([
|
||||
'enabled' => 1,
|
||||
'enforce_microsoft_login' => 0,
|
||||
]);
|
||||
|
||||
$service = $this->newService($repository);
|
||||
$result = $service->buildMicrosoftUiState(5);
|
||||
|
||||
$this->assertSame('local_and_microsoft', $result['password_mode']);
|
||||
}
|
||||
|
||||
public function testBuildMicrosoftUiStateReturnsMicrosoftOnlyPasswordModeWhenEnforced(): void
|
||||
{
|
||||
$repository = $this->createMock(TenantMicrosoftAuthRepositoryInterface::class);
|
||||
$repository->method('findByTenantId')->willReturn([
|
||||
'enabled' => 1,
|
||||
'enforce_microsoft_login' => 1,
|
||||
]);
|
||||
|
||||
$service = $this->newService($repository);
|
||||
$result = $service->buildMicrosoftUiState(5);
|
||||
|
||||
$this->assertSame('microsoft_only', $result['password_mode']);
|
||||
}
|
||||
|
||||
public function testResolveTenantLoginMethodsReturnsMicrosoftOnlyWhenEnforcedAndConfigured(): void
|
||||
{
|
||||
$tenantGateway = $this->createMock(AuthTenantGateway::class);
|
||||
|
||||
71
web/css/components/app-form-info-tile.css
Normal file
71
web/css/components/app-form-info-tile.css
Normal file
@@ -0,0 +1,71 @@
|
||||
@layer components {
|
||||
.app-form-info-tiles-title {
|
||||
display: block;
|
||||
margin-bottom: calc(var(--app-spacing) * 0.55);
|
||||
color: var(--app-muted-color);
|
||||
}
|
||||
|
||||
.app-form-info-tiles {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
gap: calc(var(--app-spacing) * 0.75);
|
||||
margin-bottom: calc(var(--app-spacing) * 0.75);
|
||||
}
|
||||
|
||||
.app-form-info-tile {
|
||||
border: 1px solid var(--badge-neutral-border);
|
||||
border-radius: var(--app-border-radius);
|
||||
background: var(--badge-neutral-bg);
|
||||
padding: calc(var(--app-spacing) * 0.75);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--app-spacing) * 0.35);
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.app-form-info-tile-label {
|
||||
display: block;
|
||||
color: var(--app-muted-color);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.app-form-info-tile-value {
|
||||
display: block;
|
||||
color: var(--app-contrast);
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.app-form-info-tile--success {
|
||||
border-color: var(--badge-success-border);
|
||||
background: var(--badge-success-bg);
|
||||
}
|
||||
|
||||
.app-form-info-tile--success .app-form-info-tile-value {
|
||||
color: var(--badge-success-color);
|
||||
}
|
||||
|
||||
.app-form-info-tile--info {
|
||||
border-color: var(--badge-info-border);
|
||||
background: var(--badge-info-bg);
|
||||
}
|
||||
|
||||
.app-form-info-tile--info .app-form-info-tile-value {
|
||||
color: var(--badge-info-color);
|
||||
}
|
||||
|
||||
.app-form-info-tile--warning {
|
||||
border-color: var(--badge-warn-border);
|
||||
background: var(--badge-warn-bg);
|
||||
}
|
||||
|
||||
.app-form-info-tile--warning .app-form-info-tile-value {
|
||||
color: var(--badge-warn-color);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.app-form-info-tiles {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user