diff --git a/i18n/default_de.json b/i18n/default_de.json index 35753cb..fd00a25 100644 --- a/i18n/default_de.json +++ b/i18n/default_de.json @@ -934,6 +934,8 @@ "Sync fields": "Sync-Felder", "Sync first name": "Vorname synchronisieren", "Sync last name": "Nachname synchronisieren", + "Sync display name": "Anzeigename synchronisieren", + "Sync job title": "Berufsbezeichnung synchronisieren", "Sync phone": "Telefon synchronisieren", "Sync mobile": "Mobil synchronisieren", "Sync avatar image": "Avatarbild synchronisieren", diff --git a/i18n/default_en.json b/i18n/default_en.json index ca03ed0..9ff5db6 100644 --- a/i18n/default_en.json +++ b/i18n/default_en.json @@ -934,6 +934,8 @@ "Sync fields": "Sync fields", "Sync first name": "Sync first name", "Sync last name": "Sync last name", + "Sync display name": "Sync display name", + "Sync job title": "Sync job title", "Sync phone": "Sync phone", "Sync mobile": "Sync mobile", "Sync avatar image": "Sync avatar image", diff --git a/lib/Repository/User/UserWriteRepository.php b/lib/Repository/User/UserWriteRepository.php index 6bc4621..9c48486 100644 --- a/lib/Repository/User/UserWriteRepository.php +++ b/lib/Repository/User/UserWriteRepository.php @@ -225,13 +225,13 @@ class UserWriteRepository implements UserWriteRepositoryInterface return false; } - $row = DB::selectOne('select first_name, last_name, email, phone, mobile from users where id = ? limit 1', (string) $id); + $row = DB::selectOne('select first_name, last_name, email, phone, mobile, job_title, display_name from users where id = ? limit 1', (string) $id); $existing = $row['users'] ?? null; if (!is_array($existing)) { return false; } - $allowed = ['first_name', 'last_name', 'phone', 'mobile']; + $allowed = ['first_name', 'last_name', 'display_name', 'job_title', 'phone', 'mobile']; $updates = []; foreach ($allowed as $field) { if (!array_key_exists($field, $fields)) { @@ -251,7 +251,7 @@ class UserWriteRepository implements UserWriteRepositoryInterface return true; } - if (isset($updates['first_name']) || isset($updates['last_name'])) { + if (!isset($updates['display_name']) && (isset($updates['first_name']) || isset($updates['last_name']))) { $displayData = [ 'first_name' => $updates['first_name'] ?? (string) ($existing['first_name'] ?? ''), 'last_name' => $updates['last_name'] ?? (string) ($existing['last_name'] ?? ''), diff --git a/lib/Service/Auth/MicrosoftOidcService.php b/lib/Service/Auth/MicrosoftOidcService.php index 36b6be2..821ad7c 100644 --- a/lib/Service/Auth/MicrosoftOidcService.php +++ b/lib/Service/Auth/MicrosoftOidcService.php @@ -197,6 +197,8 @@ class MicrosoftOidcService 'preferred_language' => trim((string) ($claims['preferred_language'] ?? '')), 'graph_given_name' => trim((string) ($graphProfile['given_name'] ?? '')), 'graph_family_name' => trim((string) ($graphProfile['family_name'] ?? '')), + 'graph_display_name' => trim((string) ($graphProfile['display_name'] ?? '')), + 'graph_job_title' => trim((string) ($graphProfile['job_title'] ?? '')), 'graph_phone' => trim((string) ($graphProfile['phone'] ?? '')), 'graph_mobile' => trim((string) ($graphProfile['mobile'] ?? '')), 'graph_avatar_mime' => trim((string) ($graphAvatar['mime'] ?? '')), @@ -368,7 +370,7 @@ class MicrosoftOidcService private function fetchMicrosoftGraphProfile(string $accessToken): array { - $url = 'https://graph.microsoft.com/v1.0/me?$select=givenName,surname,mobilePhone,businessPhones'; + $url = 'https://graph.microsoft.com/v1.0/me?$select=givenName,surname,displayName,jobTitle,mobilePhone,businessPhones'; $response = $this->oidcHttpGateway->call('GET', $url, '', [ 'Accept' => 'application/json', 'Authorization' => 'Bearer ' . $accessToken, @@ -393,6 +395,8 @@ class MicrosoftOidcService 'profile' => [ 'given_name' => trim((string) ($data['givenName'] ?? '')), 'family_name' => trim((string) ($data['surname'] ?? '')), + 'display_name' => trim((string) ($data['displayName'] ?? '')), + 'job_title' => trim((string) ($data['jobTitle'] ?? '')), 'mobile' => trim((string) ($data['mobilePhone'] ?? '')), 'phone' => $phone, ], diff --git a/lib/Service/Auth/SsoUserLinkService.php b/lib/Service/Auth/SsoUserLinkService.php index ce16ebd..8365562 100644 --- a/lib/Service/Auth/SsoUserLinkService.php +++ b/lib/Service/Auth/SsoUserLinkService.php @@ -153,6 +153,8 @@ class SsoUserLinkService 'last_name' => trim((string) ($claims['family_name'] ?? '')) !== '' ? trim((string) ($claims['family_name'] ?? '')) : trim((string) ($claims['graph_family_name'] ?? '')), + 'display_name' => trim((string) ($claims['graph_display_name'] ?? '')), + 'job_title' => trim((string) ($claims['graph_job_title'] ?? '')), 'phone' => trim((string) ($claims['graph_phone'] ?? '')), 'mobile' => trim((string) ($claims['graph_mobile'] ?? '')), ]; diff --git a/lib/Service/Auth/TenantSsoService.php b/lib/Service/Auth/TenantSsoService.php index 70f9341..3ab4b16 100644 --- a/lib/Service/Auth/TenantSsoService.php +++ b/lib/Service/Auth/TenantSsoService.php @@ -13,6 +13,8 @@ class TenantSsoService private const PROFILE_SYNC_FIELD_OPTIONS = [ 'first_name' => 'Sync first name', 'last_name' => 'Sync last name', + 'display_name' => 'Sync display name', + 'job_title' => 'Sync job title', 'phone' => 'Sync phone', 'mobile' => 'Sync mobile', 'avatar' => 'Sync avatar image', @@ -459,7 +461,9 @@ class TenantSsoService $fields = $this->normalizeProfileSyncFields($fields); return in_array('phone', $fields, true) || in_array('mobile', $fields, true) - || in_array('avatar', $fields, true); + || in_array('avatar', $fields, true) + || in_array('job_title', $fields, true) + || in_array('display_name', $fields, true); } public function shouldAutoRememberOnMicrosoftLogin(int $tenantId): bool @@ -784,7 +788,7 @@ class TenantSsoService if ($enabled && $baseDn === '') { $errors[] = t('LDAP base DN is required'); } - if ($enabled && $port < 1 || $port > 65535) { + if ($enabled && ($port < 1 || $port > 65535)) { $errors[] = t('LDAP port must be between 1 and 65535'); } if ($enabled && !str_contains($userSearchFilter, '%s')) { @@ -832,7 +836,11 @@ class TenantSsoService } $autoRememberMode = $this->normalizeAutoRememberMode($input['ldap_auto_remember_mode'] ?? null); - $attributeMapJson = json_encode($attributeMap, JSON_THROW_ON_ERROR); + try { + $attributeMapJson = json_encode($attributeMap, JSON_THROW_ON_ERROR); + } catch (\JsonException) { + return ['ok' => false, 'errors' => [t('LDAP attribute map could not be encoded')]]; + } $saved = $this->tenantLdapAuthRepository->upsertByTenantId($tenantId, [ 'enabled' => $enabled, diff --git a/pages/admin/tenants/_form.phtml b/pages/admin/tenants/_form.phtml index ec7df2b..b50c20a 100644 --- a/pages/admin/tenants/_form.phtml +++ b/pages/admin/tenants/_form.phtml @@ -49,7 +49,7 @@ if ($rawAllowUserThemeMode === null || $rawAllowUserThemeMode === '') { } else { $tenantAllowUserThemeMode = '0'; } -$microsoftEnabledRaw = $values['microsoft_enabled'] ?? $values['enabled'] ?? false; +$microsoftEnabledRaw = $values['microsoft_enabled'] ?? false; $microsoftEnabled = in_array(strtolower(trim((string) $microsoftEnabledRaw)), ['1', 'true', 'yes', 'on'], true); $microsoftEnforceRaw = $values['enforce_microsoft_login'] ?? false; $microsoftEnforce = in_array(strtolower(trim((string) $microsoftEnforceRaw)), ['1', 'true', 'yes', 'on'], true); @@ -686,11 +686,12 @@ $openOverrideCard = $detailsOpenAll; isMethod('POST')) { 'tenant_sso_create_failed' ); } + $ldapSaveResult = $tenantSsoService->saveTenantLdapAuth($createdTenantId, $post); + if (!($ldapSaveResult['ok'] ?? false)) { + Flash::error( + (string) (($ldapSaveResult['errors'][0] ?? t('Tenant LDAP settings could not be saved'))), + $closeTarget, + 'tenant_ldap_create_failed' + ); + } } } $action = (string) ($post['action'] ?? 'create'); diff --git a/pages/admin/tenants/edit($id).php b/pages/admin/tenants/edit($id).php index d0acad4..0f97d4d 100644 --- a/pages/admin/tenants/edit($id).php +++ b/pages/admin/tenants/edit($id).php @@ -73,7 +73,9 @@ app(\MintyPHP\Service\Audit\AuditMetadataEnricherInterface::class)->enrich($tena $errorBag = formErrors(); $errors = []; -$form = array_merge($tenant, $ssoConfig, $ldapConfig); +$form = array_merge($tenant, $ssoConfig); +$form['microsoft_enabled'] = $ssoConfig['enabled'] ?? false; +$form['ldap_enabled'] = $ldapConfig['enabled'] ?? false; if ($request->isMethod('POST') && !Session::checkCsrfToken()) { Flash::error(t('Form expired, please try again'), $editTarget, 'csrf_expired');