feat: harden the myep OAuth2 login
This commit is contained in:
1 parent
4517b84023
commit
91419294b8
25 files changed
+1013
-35
No files matched your search
@@ -18,6 +18,7 @@ use Symfony\Component\Validator\Constraints as Assert;
|
||||
#[ORM\Entity(repositoryClass: UserRepository::class)]
|
||||
#[ORM\Table(name: '`user`')]
|
||||
#[ORM\UniqueConstraint(name: 'uniq_user_email', columns: ['email'])]
|
||||
#[ORM\UniqueConstraint(name: 'uniq_user_sub', columns: ['sub'])]
|
||||
class User implements UserInterface
|
||||
{
|
||||
#[ORM\Id]
|
||||
@@ -25,6 +26,20 @@ class User implements UserInterface
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* myep's account identifier: opaque, stable, and the only claim that identifies an account.
|
||||
* Nullable because rows provisioned before it was stored have none -- those are matched on
|
||||
* their email once more and backfilled on that login, so the column empties out by itself.
|
||||
*/
|
||||
#[ORM\Column(length: 64, nullable: true)]
|
||||
private ?string $sub = null;
|
||||
|
||||
/**
|
||||
* The address this session authenticated with, normalised to lower case. Distinct per myep
|
||||
* account and kept up to date from the claim, but a display and contact value rather than
|
||||
* the key -- one person can hold several myep accounts, each with its own address and its
|
||||
* own roles.
|
||||
*/
|
||||
#[ORM\Column(length: 180)]
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Email]
|
||||
@@ -42,12 +57,34 @@ class User implements UserInterface
|
||||
#[ORM\Column]
|
||||
private array $roles = [];
|
||||
|
||||
/**
|
||||
* When the account was locked out here, or null while it may sign in.
|
||||
*
|
||||
* Local and deliberately not derived from anything myep says: it is the only lever this
|
||||
* application has over its own access, and a login must not be able to clear it.
|
||||
*/
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?\DateTimeImmutable $disabledAt = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $disabledReason = null;
|
||||
|
||||
public function __construct(string $email, string $name)
|
||||
{
|
||||
$this->email = $email;
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getSub(): ?string
|
||||
{
|
||||
return $this->sub;
|
||||
}
|
||||
|
||||
public function setSub(?string $sub): void
|
||||
{
|
||||
$this->sub = $sub;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
@@ -58,6 +95,11 @@ class User implements UserInterface
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
public function setEmail(string $email): void
|
||||
{
|
||||
$this->email = $email;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
@@ -89,6 +131,37 @@ class User implements UserInterface
|
||||
$this->roles = $roles;
|
||||
}
|
||||
|
||||
public function isDisabled(): bool
|
||||
{
|
||||
return null !== $this->disabledAt;
|
||||
}
|
||||
|
||||
public function getDisabledAt(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->disabledAt;
|
||||
}
|
||||
|
||||
public function getDisabledReason(): ?string
|
||||
{
|
||||
return $this->disabledReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-locking an already locked account leaves the original timestamp alone: when the lockout
|
||||
* started is the part worth keeping.
|
||||
*/
|
||||
public function disable(?string $reason = null): void
|
||||
{
|
||||
$this->disabledAt ??= new \DateTimeImmutable();
|
||||
$this->disabledReason = $reason;
|
||||
}
|
||||
|
||||
public function enable(): void
|
||||
{
|
||||
$this->disabledAt = null;
|
||||
$this->disabledReason = null;
|
||||
}
|
||||
|
||||
public function eraseCredentials(): void
|
||||
{
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user