Fetches displayName/jobTitle from Graph API and syncs them via SSO profile sync. Also fixes LDAP enabled state in tenant form, adds LDAP save on tenant create, and hardens LDAP port validation precedence. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
321 lines
12 KiB
PHP
321 lines
12 KiB
PHP
<?php
|
|
|
|
namespace MintyPHP\Repository\User;
|
|
|
|
use MintyPHP\DB;
|
|
use MintyPHP\Repository\Support\RepoQuery;
|
|
|
|
/** Creates and mutates user records including activation, tenant links, and preferences. */
|
|
class UserWriteRepository implements UserWriteRepositoryInterface
|
|
{
|
|
public function updateLastLogin(int $userId, string $provider = 'local'): void
|
|
{
|
|
if ($userId <= 0) {
|
|
return;
|
|
}
|
|
$provider = trim(strtolower($provider));
|
|
if (!in_array($provider, ['local', 'microsoft'], true)) {
|
|
$provider = 'local';
|
|
}
|
|
DB::query('update users set last_login_at = UTC_TIMESTAMP(), last_login_provider = ? where id = ?', $provider, (string) $userId);
|
|
}
|
|
|
|
public function bumpAuthzVersion(int $userId): bool
|
|
{
|
|
if ($userId <= 0) {
|
|
return false;
|
|
}
|
|
|
|
$result = DB::update(
|
|
'update users set authz_version = authz_version + 1 where id = ?',
|
|
(string) $userId
|
|
);
|
|
|
|
return $result !== false;
|
|
}
|
|
|
|
public function bumpAuthzVersionByUserIds(array $userIds): int
|
|
{
|
|
$userIds = array_values(array_unique(array_map('intval', $userIds)));
|
|
$userIds = array_values(array_filter($userIds, static fn ($id) => $id > 0));
|
|
if (!$userIds) {
|
|
return 0;
|
|
}
|
|
|
|
$placeholders = implode(',', array_fill(0, count($userIds), '?'));
|
|
$result = DB::update(
|
|
"update users set authz_version = authz_version + 1 where id in ($placeholders)",
|
|
...array_map('strval', $userIds)
|
|
);
|
|
|
|
return $result !== false ? (int) $result : 0;
|
|
}
|
|
|
|
public function create(array $data): int|false
|
|
{
|
|
$hash = password_hash($data['password'], PASSWORD_DEFAULT);
|
|
return DB::insert(
|
|
'insert into users (uuid, first_name, last_name, display_name, email, profile_description, job_title, phone, mobile, short_dial, address, postal_code, city, country, region, hire_date, password, locale, totp_secret, theme, primary_tenant_id, current_tenant_id, created_by, created, active, active_changed_at, active_changed_by) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,NOW(),?,?,?)',
|
|
$data['uuid'] ?? RepoQuery::uuidV4(),
|
|
$data['first_name'],
|
|
$data['last_name'],
|
|
$this->buildDisplayName($data),
|
|
$data['email'],
|
|
$data['profile_description'] ?? null,
|
|
$data['job_title'] ?? null,
|
|
$data['phone'] ?? null,
|
|
$data['mobile'] ?? null,
|
|
$data['short_dial'] ?? null,
|
|
$data['address'] ?? null,
|
|
$data['postal_code'] ?? null,
|
|
$data['city'] ?? null,
|
|
$data['country'] ?? null,
|
|
$data['region'] ?? null,
|
|
$data['hire_date'] ?? null,
|
|
$hash,
|
|
$data['locale'] ?? null,
|
|
$data['totp_secret'],
|
|
$data['theme'] ?? 'light',
|
|
$data['primary_tenant_id'] ?? null,
|
|
$data['current_tenant_id'] ?? $data['primary_tenant_id'] ?? null,
|
|
$data['created_by'] ?? null,
|
|
$data['active'],
|
|
$data['active_changed_at'] ?? null,
|
|
$data['active_changed_by'] ?? null
|
|
);
|
|
}
|
|
|
|
public function update(int $id, array $data): bool
|
|
{
|
|
$fields = [
|
|
'first_name' => $data['first_name'],
|
|
'last_name' => $data['last_name'],
|
|
'display_name' => $this->buildDisplayName($data),
|
|
'email' => $data['email'],
|
|
'profile_description' => $data['profile_description'] ?? null,
|
|
'job_title' => $data['job_title'] ?? null,
|
|
'phone' => $data['phone'] ?? null,
|
|
'mobile' => $data['mobile'] ?? null,
|
|
'short_dial' => $data['short_dial'] ?? null,
|
|
'address' => $data['address'] ?? null,
|
|
'postal_code' => $data['postal_code'] ?? null,
|
|
'city' => $data['city'] ?? null,
|
|
'country' => $data['country'] ?? null,
|
|
'region' => $data['region'] ?? null,
|
|
'hire_date' => $data['hire_date'] ?? null,
|
|
'totp_secret' => $data['totp_secret'],
|
|
'active' => $data['active'],
|
|
];
|
|
if (array_key_exists('locale', $data)) {
|
|
$fields['locale'] = $data['locale'];
|
|
}
|
|
if (array_key_exists('theme', $data)) {
|
|
$fields['theme'] = $data['theme'];
|
|
}
|
|
if (array_key_exists('primary_tenant_id', $data)) {
|
|
$fields['primary_tenant_id'] = $data['primary_tenant_id'];
|
|
}
|
|
if (array_key_exists('current_tenant_id', $data)) {
|
|
$fields['current_tenant_id'] = $data['current_tenant_id'];
|
|
}
|
|
if (array_key_exists('modified_by', $data)) {
|
|
$fields['modified_by'] = $data['modified_by'];
|
|
}
|
|
if (array_key_exists('active_changed_at', $data)) {
|
|
$fields['active_changed_at'] = $data['active_changed_at'];
|
|
}
|
|
if (array_key_exists('active_changed_by', $data)) {
|
|
$fields['active_changed_by'] = $data['active_changed_by'];
|
|
}
|
|
|
|
if (!empty($data['password'])) {
|
|
$fields['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
|
|
}
|
|
|
|
$setParts = [];
|
|
$params = [];
|
|
foreach ($fields as $field => $value) {
|
|
$setParts[] = sprintf('`%s` = ?', $field);
|
|
$params[] = $value;
|
|
}
|
|
$params[] = (string) $id;
|
|
|
|
$query = 'update users set ' . implode(', ', $setParts) . ' where id = ?';
|
|
$result = DB::update($query, ...$params);
|
|
return $result !== false;
|
|
}
|
|
|
|
public function setActive(int $id, bool $active, ?int $changedBy = null): bool
|
|
{
|
|
$result = DB::update(
|
|
'update users set active = ?, active_changed_at = NOW(), active_changed_by = ? where id = ?',
|
|
$active ? 1 : 0,
|
|
$changedBy,
|
|
(string) $id
|
|
);
|
|
return $result !== false;
|
|
}
|
|
|
|
public function setCurrentTenant(int $id, int $tenantId): bool
|
|
{
|
|
$result = DB::update(
|
|
'update users set current_tenant_id = ? where id = ?',
|
|
(string) $tenantId,
|
|
(string) $id
|
|
);
|
|
return $result !== false;
|
|
}
|
|
|
|
public function setActiveByUuids(array $uuids, bool $active, ?int $modifiedBy = null): bool
|
|
{
|
|
$uuids = array_values(array_filter(array_map('trim', $uuids)));
|
|
if (!$uuids) {
|
|
return false;
|
|
}
|
|
$placeholders = implode(',', array_fill(0, count($uuids), '?'));
|
|
$query = "update users set active = ?, modified_by = ?, active_changed_at = NOW(), active_changed_by = ? where uuid in ($placeholders)";
|
|
$params = array_merge([$active ? 1 : 0, $modifiedBy, $modifiedBy], $uuids);
|
|
$result = DB::update($query, ...$params);
|
|
return $result !== false;
|
|
}
|
|
|
|
public function setInactiveByIds(array $userIds, ?int $changedBy = null): int
|
|
{
|
|
$ids = array_values(array_unique(array_filter(array_map('intval', $userIds), static fn ($id) => $id > 0)));
|
|
if (!$ids) {
|
|
return 0;
|
|
}
|
|
|
|
$placeholders = implode(',', array_fill(0, count($ids), '?'));
|
|
$query = "update users set active = 0, modified_by = ?, active_changed_at = UTC_TIMESTAMP(), active_changed_by = ? where active = 1 and id in ($placeholders)";
|
|
$params = array_merge([$changedBy, $changedBy], array_map('strval', $ids));
|
|
$result = DB::update($query, ...$params);
|
|
return $result !== false ? (int) $result : 0;
|
|
}
|
|
|
|
public function deleteByIds(array $userIds): int
|
|
{
|
|
$ids = array_values(array_unique(array_filter(array_map('intval', $userIds), static fn ($id) => $id > 0)));
|
|
if (!$ids) {
|
|
return 0;
|
|
}
|
|
$placeholders = implode(',', array_fill(0, count($ids), '?'));
|
|
$result = DB::delete(
|
|
"delete from users where id in ($placeholders)",
|
|
...array_map('strval', $ids)
|
|
);
|
|
return $result !== false ? (int) $result : 0;
|
|
}
|
|
|
|
public function setLocale(int $id, string $locale): bool
|
|
{
|
|
$result = DB::update('update users set locale = ? where id = ?', $locale, (string) $id);
|
|
return $result !== false;
|
|
}
|
|
|
|
public function setTheme(int $id, string $theme): bool
|
|
{
|
|
$result = DB::update('update users set theme = ? where id = ?', $theme, (string) $id);
|
|
return $result !== false;
|
|
}
|
|
|
|
public function updateProfileFieldsFromSso(int $id, array $fields): bool
|
|
{
|
|
if ($id <= 0) {
|
|
return false;
|
|
}
|
|
|
|
$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', 'display_name', 'job_title', 'phone', 'mobile'];
|
|
$updates = [];
|
|
foreach ($allowed as $field) {
|
|
if (!array_key_exists($field, $fields)) {
|
|
continue;
|
|
}
|
|
$value = trim((string) $fields[$field]);
|
|
if ($value === '') {
|
|
continue;
|
|
}
|
|
if ((string) ($existing[$field] ?? '') === $value) {
|
|
continue;
|
|
}
|
|
$updates[$field] = $value;
|
|
}
|
|
|
|
if (!$updates) {
|
|
return true;
|
|
}
|
|
|
|
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'] ?? ''),
|
|
'email' => (string) ($existing['email'] ?? ''),
|
|
];
|
|
$updates['display_name'] = $this->buildDisplayName($displayData);
|
|
}
|
|
|
|
$setParts = [];
|
|
$params = [];
|
|
foreach ($updates as $field => $value) {
|
|
$setParts[] = sprintf('`%s` = ?', $field);
|
|
$params[] = $value;
|
|
}
|
|
$params[] = (string) $id;
|
|
|
|
$result = DB::update(
|
|
'update users set ' . implode(', ', $setParts) . ' where id = ?',
|
|
...$params
|
|
);
|
|
|
|
return $result !== false;
|
|
}
|
|
|
|
public function setPassword(int $id, string $password): bool
|
|
{
|
|
$hash = password_hash($password, PASSWORD_DEFAULT);
|
|
$result = DB::update('update users set password = ?, modified_by = NULL where id = ?', $hash, (string) $id);
|
|
return $result !== false;
|
|
}
|
|
|
|
public function setEmailVerified(int $id): bool
|
|
{
|
|
$result = DB::update('update users set email_verified_at = NOW() where id = ?', (string) $id);
|
|
return $result !== false;
|
|
}
|
|
|
|
public function delete(int $id): bool
|
|
{
|
|
$result = DB::delete('delete from users where id = ?', (string) $id);
|
|
return $result !== false;
|
|
}
|
|
|
|
public function deleteByUuids(array $uuids): bool
|
|
{
|
|
$uuids = array_values(array_filter(array_map('trim', $uuids)));
|
|
if (!$uuids) {
|
|
return false;
|
|
}
|
|
$placeholders = implode(',', array_fill(0, count($uuids), '?'));
|
|
$query = "delete from users where uuid in ($placeholders)";
|
|
$result = DB::delete($query, ...$uuids);
|
|
return $result !== false;
|
|
}
|
|
|
|
private function buildDisplayName(array $data): string
|
|
{
|
|
$first = trim((string) ($data['first_name'] ?? ''));
|
|
$last = trim((string) ($data['last_name'] ?? ''));
|
|
$name = trim($first . ' ' . $last);
|
|
if ($name === '') {
|
|
$name = trim((string) ($data['email'] ?? ''));
|
|
}
|
|
return $name;
|
|
}
|
|
}
|