59 lines
1.7 KiB
PHP
59 lines
1.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Security;
|
|
|
|
/**
|
|
* Decides whether an account belongs to a member of staff, by its email domain.
|
|
*
|
|
* BusPro has no CRM selection expressing "works here", so the email address is the only signal
|
|
* available. The domains are configuration (%employee_email_domains%) rather than a constant
|
|
* because they are deployment-specific, in the same way the brand hosts and the CRM selection ids
|
|
* are.
|
|
*
|
|
* Matching is exact on the domain part and never on a suffix: "mail.ep-reisen.de" and
|
|
* "notep-reisen.de" are not "ep-reisen.de". A suffix match here would hand ROLE_EMPLOYEE to
|
|
* anybody able to register a domain ending in the configured one.
|
|
*/
|
|
final class EmployeeDomainMatcher
|
|
{
|
|
/**
|
|
* @var string[]
|
|
*/
|
|
private readonly array $domains;
|
|
|
|
/**
|
|
* @param string[] $domains
|
|
*/
|
|
public function __construct(array $domains)
|
|
{
|
|
$this->domains = array_values(array_filter(array_map(
|
|
static fn (string $domain): string => strtolower(trim($domain, " \t\n\r\0\x0B.@")),
|
|
$domains,
|
|
)));
|
|
}
|
|
|
|
/**
|
|
* A malformed or missing identifier is simply not an employee. It must not throw: this runs
|
|
* inside the authentication path, where an exception would turn a bad address into a failed
|
|
* login rather than a login without the role.
|
|
*/
|
|
public function isEmployee(?string $email): bool
|
|
{
|
|
if (null === $email || [] === $this->domains) {
|
|
return false;
|
|
}
|
|
|
|
$position = strrpos($email, '@');
|
|
|
|
if (false === $position) {
|
|
return false;
|
|
}
|
|
|
|
$domain = strtolower(substr($email, $position + 1));
|
|
|
|
return \in_array($domain, $this->domains, true);
|
|
}
|
|
}
|