feat(integrations): add role-based security and 1-click Outlook connect for colleagues
This commit is contained in:
@@ -12,6 +12,90 @@ const MS_OAUTH_TOKEN_URL = 'https://login.microsoftonline.com/%s/oauth2/v2.0
|
||||
const MS_GRAPH_BASE_URL = 'https://graph.microsoft.com/v1.0';
|
||||
const MS_DEFAULT_SCOPES = 'offline_access User.Read Calendars.ReadWrite';
|
||||
|
||||
/**
|
||||
* Haalt de centrale Microsoft App Registratie configuratie op (Client ID, Secret, Tenant ID)
|
||||
* Prioriteit:
|
||||
* 1. Environment variabelen (.env of server env)
|
||||
* 2. Opgeslagen configuratie van een Beheerder (ADMIN) in de integrations tabel
|
||||
* 3. Fallback naar integrations tabel waar settings client_id bevat
|
||||
*/
|
||||
function microsoft_get_app_config(): array {
|
||||
$clientId = fociz_env('MICROSOFT_CLIENT_ID', '');
|
||||
$clientSecret = fociz_env('MICROSOFT_CLIENT_SECRET', '');
|
||||
$tenantId = fociz_env('MICROSOFT_TENANT_ID', '');
|
||||
|
||||
if (empty($clientId) || empty($clientSecret)) {
|
||||
try {
|
||||
$db = get_db_connection();
|
||||
// Zoek bij actieve beheerder(s) in de integrations tabel
|
||||
$stmt = $db->query("
|
||||
SELECT i.settings
|
||||
FROM integrations i
|
||||
JOIN users u ON i.user_id = u.id
|
||||
WHERE u.role = 'ADMIN' AND i.provider = 'OUTLOOK' AND i.settings IS NOT NULL
|
||||
ORDER BY i.updated_at DESC
|
||||
LIMIT 5
|
||||
");
|
||||
$rows = $stmt->fetchAll();
|
||||
foreach ($rows as $row) {
|
||||
if (!empty($row['settings'])) {
|
||||
$saved = json_decode($row['settings'], true) ?: [];
|
||||
if (empty($clientId) && !empty($saved['client_id'])) {
|
||||
$clientId = trim($saved['client_id']);
|
||||
}
|
||||
if (empty($clientSecret) && !empty($saved['client_secret'])) {
|
||||
$clientSecret = trim($saved['client_secret']);
|
||||
}
|
||||
if (empty($tenantId) && !empty($saved['tenant_id'])) {
|
||||
$tenantId = trim($saved['tenant_id']);
|
||||
}
|
||||
if (!empty($clientId) && !empty($clientSecret)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: zoek een willekeurige OUTLOOK rij met ingevulde client_id
|
||||
if (empty($clientId) || empty($clientSecret)) {
|
||||
$stmtFallback = $db->query("
|
||||
SELECT settings
|
||||
FROM integrations
|
||||
WHERE provider = 'OUTLOOK' AND settings LIKE '%client_id%'
|
||||
ORDER BY updated_at DESC
|
||||
LIMIT 3
|
||||
");
|
||||
$fallbackRows = $stmtFallback->fetchAll();
|
||||
foreach ($fallbackRows as $fbRow) {
|
||||
if (!empty($fbRow['settings'])) {
|
||||
$saved = json_decode($fbRow['settings'], true) ?: [];
|
||||
if (empty($clientId) && !empty($saved['client_id'])) {
|
||||
$clientId = trim($saved['client_id']);
|
||||
}
|
||||
if (empty($clientSecret) && !empty($saved['client_secret'])) {
|
||||
$clientSecret = trim($saved['client_secret']);
|
||||
}
|
||||
if (empty($tenantId) && !empty($saved['tenant_id'])) {
|
||||
$tenantId = trim($saved['tenant_id']);
|
||||
}
|
||||
if (!empty($clientId) && !empty($clientSecret)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
error_log("microsoft_get_app_config error: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'client_id' => $clientId ?: '',
|
||||
'client_secret' => $clientSecret ?: '',
|
||||
'tenant_id' => !empty($tenantId) ? $tenantId : 'common',
|
||||
'is_configured' => !empty($clientId) && !empty($clientSecret),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Genereert de Microsoft OAuth2 login URL
|
||||
*/
|
||||
@@ -117,9 +201,10 @@ function microsoft_get_valid_token(int $userId): ?string {
|
||||
|
||||
// Token is verlopen of verloopt bijna; refresh met refresh_token
|
||||
$refreshToken = $integration['refresh_token'] ?? '';
|
||||
$clientId = $settings['client_id'] ?? fociz_env('MICROSOFT_CLIENT_ID', '');
|
||||
$clientSecret = $settings['client_secret'] ?? fociz_env('MICROSOFT_CLIENT_SECRET', '');
|
||||
$tenantId = $settings['tenant_id'] ?? fociz_env('MICROSOFT_TENANT_ID', 'common');
|
||||
$appConfig = microsoft_get_app_config();
|
||||
$clientId = !empty($settings['client_id']) ? $settings['client_id'] : $appConfig['client_id'];
|
||||
$clientSecret = !empty($settings['client_secret']) ? $settings['client_secret'] : $appConfig['client_secret'];
|
||||
$tenantId = !empty($settings['tenant_id']) ? $settings['tenant_id'] : $appConfig['tenant_id'];
|
||||
|
||||
if (empty($refreshToken) || empty($clientId) || empty($clientSecret)) {
|
||||
return null;
|
||||
@@ -251,10 +336,33 @@ function microsoft_fetch_today_events(int $userId, ?string $date = null): array
|
||||
|
||||
/**
|
||||
* Ontkoppelt Microsoft integratie voor een gebruiker
|
||||
* Wist uitsluitend de persoonlijke tokens; behoudt de centrale Azure App gegevens indien aanwezig.
|
||||
*/
|
||||
function microsoft_disconnect(int $userId): bool {
|
||||
$db = get_db_connection();
|
||||
$stmt = $db->prepare("DELETE FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK'");
|
||||
return $stmt->execute([$userId]);
|
||||
$stmt = $db->prepare("SELECT * FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK'");
|
||||
$stmt->execute([$userId]);
|
||||
$integration = $stmt->fetch();
|
||||
|
||||
if (!$integration) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$settings = json_decode($integration['settings'] ?? '{}', true) ?: [];
|
||||
// Verwijder gebruikers-specifieke token gegevens
|
||||
unset($settings['account_email'], $settings['account_name'], $settings['expires_at']);
|
||||
|
||||
// Als er centrale app credentials in staan (bijv. van beheerder), behouden we deze netjes
|
||||
if (!empty($settings['client_id']) || !empty($settings['client_secret'])) {
|
||||
$updateStmt = $db->prepare("
|
||||
UPDATE integrations
|
||||
SET access_token = NULL, refresh_token = NULL, settings = ?, last_sync_at = NULL, is_enabled = 1, is_demo_mode = 0
|
||||
WHERE id = ?
|
||||
");
|
||||
return $updateStmt->execute([json_encode($settings), $integration['id']]);
|
||||
} else {
|
||||
$delStmt = $db->prepare("DELETE FROM integrations WHERE id = ?");
|
||||
return $delStmt->execute([$integration['id']]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user