usable(); } /** * Verify a password against the directory on behalf of an account that * already exists locally. * * $user is null when nobody local matches — provisioning handles that * case, and this method still answers so the caller can decide. */ public function attempt(string $email, string $password, ?User $user = null): ?LdapIdentity { if (! $this->applies($password, $user)) { return null; } $identity = $this->directory->authenticate($email, $password); if ($identity === null) { return null; } Cache::forget(self::BREAKER_KEY); return $identity; } /** * Record what the directory told us about an account that already * exists, so an administrator can see which entry it corresponds to. * * `auth_source` is set here only when the account has no source of its * own yet — a login is not an authenticated request and has no business * flipping a privilege-adjacent flag on an account it did not create. */ public function stamp(User $user, LdapIdentity $identity): void { $user->forceFill([ 'ldap_dn' => $identity->dn, 'ldap_synced_at' => now(), ])->save(); } public function markUnreachable(): void { Cache::put(self::BREAKER_KEY, true, self::BREAKER_SECONDS); } private function applies(string $password, ?User $user): bool { if (trim($password) === '') { return false; } // Staff never authenticate against a directory. if ($user !== null && ! $user->isClient()) { return false; } if (Cache::get(self::BREAKER_KEY) === true) { return false; } return $this->enabled(); } /** * Whether this account's password lives in the directory rather than * here — in which case the local hash is not consulted at all. */ public function isDirectoryAccount(?User $user): bool { return $user !== null && $user->isClient() && $user->auth_source === AuthSource::Ldap; } }