mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-07-09 18:04:33 +02:00
Merge branch 'master' into permission_rework
This commit is contained in:
commit
33f8d2ba9e
109 changed files with 5095 additions and 2860 deletions
|
@ -34,7 +34,12 @@ use LogicException;
|
|||
* Class Attachment.
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="App\Repository\AttachmentRepository")
|
||||
* @ORM\Table(name="`attachments`")
|
||||
* @ORM\Table(name="`attachments`", indexes={
|
||||
* @ORM\Index(name="attachments_idx_id_element_id_class_name", columns={"id", "element_id", "class_name"}),
|
||||
* @ORM\Index(name="attachments_idx_class_name_id", columns={"class_name", "id"}),
|
||||
* @ORM\Index(name="attachment_name_idx", columns={"name"}),
|
||||
* @ORM\Index(name="attachment_element_idx", columns={"class_name", "element_id"})
|
||||
* })
|
||||
* @ORM\InheritanceType("SINGLE_TABLE")
|
||||
* @ORM\DiscriminatorColumn(name="class_name", type="string")
|
||||
* @ORM\DiscriminatorMap({
|
||||
|
@ -104,7 +109,7 @@ abstract class Attachment extends AbstractNamedDBElement
|
|||
/**
|
||||
* @var AttachmentType
|
||||
* @ORM\ManyToOne(targetEntity="AttachmentType", inversedBy="attachments_with_type")
|
||||
* @ORM\JoinColumn(name="type_id", referencedColumnName="id")
|
||||
* @ORM\JoinColumn(name="type_id", referencedColumnName="id", nullable=false)
|
||||
* @Selectable()
|
||||
* @Assert\NotNull(message="validator.attachment.must_not_be_null")
|
||||
*/
|
||||
|
|
|
@ -34,7 +34,10 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||
* Class AttachmentType.
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="App\Repository\StructuralDBElementRepository")
|
||||
* @ORM\Table(name="`attachment_types`")
|
||||
* @ORM\Table(name="`attachment_types`", indexes={
|
||||
* @ORM\Index(name="attachment_types_idx_name", columns={"name"}),
|
||||
* @ORM\Index(name="attachment_types_idx_parent_name", columns={"parent_id", "name"}),
|
||||
* })
|
||||
*/
|
||||
class AttachmentType extends AbstractStructuralDBElement
|
||||
{
|
||||
|
|
|
@ -93,7 +93,7 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
|
|||
* @NoneOfItsChildren()
|
||||
* @Groups({"include_parents"})
|
||||
*/
|
||||
protected $parent;
|
||||
protected $parent = null;
|
||||
|
||||
/** @var string[] all names of all parent elements as a array of strings,
|
||||
* the last array element is the name of the element itself
|
||||
|
@ -271,16 +271,17 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
|
|||
*/
|
||||
public function getSubelements(): iterable
|
||||
{
|
||||
return $this->children;
|
||||
return $this->children ?? new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see getSubelements()
|
||||
* @return Collection<static>|iterable
|
||||
* @psalm-return Collection<int, static>
|
||||
*/
|
||||
public function getChildren(): iterable
|
||||
{
|
||||
return $this->children;
|
||||
return $this->getSubelements();
|
||||
}
|
||||
|
||||
public function isNotSelectable(): bool
|
||||
|
|
|
@ -78,12 +78,12 @@ class DevicePart extends AbstractDBElement
|
|||
* @ORM\ManyToOne(targetEntity="Device", inversedBy="parts")
|
||||
* @ORM\JoinColumn(name="id_device", referencedColumnName="id")
|
||||
*/
|
||||
protected Device $device;
|
||||
protected ?Device $device = null;
|
||||
|
||||
/**
|
||||
* @var Part
|
||||
* @ORM\ManyToOne(targetEntity="App\Entity\Parts\Part")
|
||||
* @ORM\JoinColumn(name="id_part", referencedColumnName="id")
|
||||
*/
|
||||
protected Part $part;
|
||||
protected ?Part $part = null;
|
||||
}
|
||||
|
|
|
@ -71,7 +71,11 @@ use Psr\Log\LogLevel;
|
|||
* This entity describes a entry in the event log.
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="App\Repository\LogEntryRepository")
|
||||
* @ORM\Table("log")
|
||||
* @ORM\Table("log", indexes={
|
||||
* @ORM\Index(name="log_idx_type", columns={"type"}),
|
||||
* @ORM\Index(name="log_idx_type_target", columns={"type", "target_type", "target_id"}),
|
||||
* @ORM\Index(name="log_idx_datetime", columns={"datetime"}),
|
||||
* })
|
||||
* @ORM\InheritanceType("SINGLE_TABLE")
|
||||
* @ORM\DiscriminatorColumn(name="type", type="smallint")
|
||||
* @ORM\DiscriminatorMap({
|
||||
|
|
|
@ -34,7 +34,11 @@ use function sprintf;
|
|||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass="App\Repository\ParameterRepository")
|
||||
* @ORM\Table("parameters")
|
||||
* @ORM\Table("parameters", indexes={
|
||||
* @ORM\Index(name="parameter_name_idx", columns={"name"}),
|
||||
* @ORM\Index(name="parameter_group_idx", columns={"param_group"}),
|
||||
* @ORM\Index(name="parameter_type_element_idx", columns={"type", "element_id"})
|
||||
* })
|
||||
* @ORM\InheritanceType("SINGLE_TABLE")
|
||||
* @ORM\DiscriminatorColumn(name="type", type="smallint")
|
||||
* @ORM\DiscriminatorMap({
|
||||
|
@ -91,7 +95,6 @@ abstract class AbstractParameter extends AbstractNamedDBElement
|
|||
|
||||
/**
|
||||
* @var string The unit in which the value values are given (e.g. V)
|
||||
* @Assert\Length(max=5)
|
||||
* @ORM\Column(type="string", nullable=false)
|
||||
*/
|
||||
protected string $unit = '';
|
||||
|
|
|
@ -33,7 +33,10 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||
* Class AttachmentType.
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="App\Repository\Parts\CategoryRepository")
|
||||
* @ORM\Table(name="`categories`")
|
||||
* @ORM\Table(name="`categories`", indexes={
|
||||
* @ORM\Index(name="category_idx_name", columns={"name"}),
|
||||
* @ORM\Index(name="category_idx_parent_name", columns={"parent_id", "name"}),
|
||||
* })
|
||||
*/
|
||||
class Category extends AbstractPartsContainingDBElement
|
||||
{
|
||||
|
|
|
@ -61,7 +61,10 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||
* Class Footprint.
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="App\Repository\Parts\FootprintRepository")
|
||||
* @ORM\Table("`footprints`")
|
||||
* @ORM\Table("`footprints`", indexes={
|
||||
* @ORM\Index(name="footprint_idx_name", columns={"name"}),
|
||||
* @ORM\Index(name="footprint_idx_parent_name", columns={"parent_id", "name"}),
|
||||
* })
|
||||
*/
|
||||
class Footprint extends AbstractPartsContainingDBElement
|
||||
{
|
||||
|
|
|
@ -61,7 +61,10 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||
* Class Manufacturer.
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="App\Repository\Parts\ManufacturerRepository")
|
||||
* @ORM\Table("`manufacturers`")
|
||||
* @ORM\Table("`manufacturers`", indexes={
|
||||
* @ORM\Index(name="manufacturer_name", columns={"name"}),
|
||||
* @ORM\Index(name="manufacturer_idx_parent_name", columns={"parent_id", "name"}),
|
||||
* })
|
||||
*/
|
||||
class Manufacturer extends AbstractCompany
|
||||
{
|
||||
|
|
|
@ -55,7 +55,10 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||
* This could be something like N, grams, meters, etc...
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="App\Repository\Parts\MeasurementUnitRepository")
|
||||
* @ORM\Table(name="`measurement_units`")
|
||||
* @ORM\Table(name="`measurement_units`", indexes={
|
||||
* @ORM\Index(name="unit_idx_name", columns={"name"}),
|
||||
* @ORM\Index(name="unit_idx_parent_name", columns={"parent_id", "name"}),
|
||||
* })
|
||||
* @UniqueEntity("unit")
|
||||
*/
|
||||
class MeasurementUnit extends AbstractPartsContainingDBElement
|
||||
|
@ -66,7 +69,7 @@ class MeasurementUnit extends AbstractPartsContainingDBElement
|
|||
* @ORM\Column(type="string", name="unit", nullable=true)
|
||||
* @Assert\Length(max=10)
|
||||
*/
|
||||
protected string $unit;
|
||||
protected ?string $unit = null;
|
||||
|
||||
/**
|
||||
* @var bool Determines if the amount value associated with this unit should be treated as integer.
|
||||
|
|
|
@ -74,7 +74,10 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||
* Otherwise this class would be too big, to be maintained.
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="App\Repository\PartRepository")
|
||||
* @ORM\Table("`parts`")
|
||||
* @ORM\Table("`parts`", indexes={
|
||||
* @ORM\Index(name="parts_idx_datet_name_last_id_needs", columns={"datetime_added", "name", "last_modified", "id", "needs_review"}),
|
||||
* @ORM\Index(name="parts_idx_name", columns={"name"}),
|
||||
* })
|
||||
*/
|
||||
class Part extends AttachmentContainingDBElement
|
||||
{
|
||||
|
|
|
@ -58,7 +58,10 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||
* It is the connection between a part and its store locations.
|
||||
*
|
||||
* @ORM\Entity()
|
||||
* @ORM\Table(name="part_lots")
|
||||
* @ORM\Table(name="part_lots", indexes={
|
||||
* @ORM\Index(name="part_lots_idx_instock_un_expiration_id_part", columns={"instock_unknown", "expiration_date", "id_part"}),
|
||||
* @ORM\Index(name="part_lots_idx_needs_refill", columns={"needs_refill"}),
|
||||
* })
|
||||
* @ORM\HasLifecycleCallbacks()
|
||||
* @ValidPartLot()
|
||||
*/
|
||||
|
|
|
@ -79,7 +79,7 @@ trait ManufacturerTrait
|
|||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
* @Assert\Choice({"announced", "active", "nrfnd", "eol", "discontinued", ""})
|
||||
*/
|
||||
protected string $manufacturing_status = '';
|
||||
protected ?string $manufacturing_status = '';
|
||||
|
||||
/**
|
||||
* Get the link to the website of the article on the manufacturers website
|
||||
|
|
|
@ -79,7 +79,7 @@ trait OrderTrait
|
|||
* @ORM\OneToOne(targetEntity="App\Entity\PriceInformations\Orderdetail")
|
||||
* @ORM\JoinColumn(name="order_orderdetails_id", referencedColumnName="id")
|
||||
*/
|
||||
protected Orderdetail $order_orderdetail;
|
||||
protected ?Orderdetail $order_orderdetail = null;
|
||||
|
||||
/**
|
||||
* Get the selected order orderdetails of this part.
|
||||
|
|
|
@ -61,7 +61,10 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||
* Class Store location.
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="App\Repository\Parts\StorelocationRepository")
|
||||
* @ORM\Table("`storelocations`")
|
||||
* @ORM\Table("`storelocations`", indexes={
|
||||
* @ORM\Index(name="location_idx_name", columns={"name"}),
|
||||
* @ORM\Index(name="location_idx_parent_name", columns={"parent_id", "name"}),
|
||||
* })
|
||||
*/
|
||||
class Storelocation extends AbstractPartsContainingDBElement
|
||||
{
|
||||
|
|
|
@ -65,7 +65,10 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||
* Class Supplier.
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="App\Repository\Parts\SupplierRepository")
|
||||
* @ORM\Table("`suppliers`")
|
||||
* @ORM\Table("`suppliers`", indexes={
|
||||
* @ORM\Index(name="supplier_idx_name", columns={"name"}),
|
||||
* @ORM\Index(name="supplier_idx_parent_name", columns={"parent_id", "name"}),
|
||||
* })
|
||||
*/
|
||||
class Supplier extends AbstractCompany
|
||||
{
|
||||
|
|
|
@ -59,7 +59,10 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||
*
|
||||
* @UniqueEntity("iso_code")
|
||||
* @ORM\Entity()
|
||||
* @ORM\Table(name="currencies")
|
||||
* @ORM\Table(name="currencies", indexes={
|
||||
* @ORM\Index(name="currency_idx_name", columns={"name"}),
|
||||
* @ORM\Index(name="currency_idx_parent_name", columns={"parent_id", "name"}),
|
||||
* })
|
||||
*/
|
||||
class Currency extends AbstractStructuralDBElement
|
||||
{
|
||||
|
@ -78,7 +81,7 @@ class Currency extends AbstractStructuralDBElement
|
|||
* @ORM\Column(type="string")
|
||||
* @Assert\Currency()
|
||||
*/
|
||||
protected string $iso_code;
|
||||
protected string $iso_code = "";
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="Currency", mappedBy="parent", cascade={"persist"})
|
||||
|
|
|
@ -66,7 +66,9 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||
/**
|
||||
* Class Orderdetail.
|
||||
*
|
||||
* @ORM\Table("`orderdetails`")
|
||||
* @ORM\Table("`orderdetails`", indexes={
|
||||
* @ORM\Index(name="orderdetails_supplier_part_nr", columns={"supplierpartnr"}),
|
||||
* })
|
||||
* @ORM\Entity()
|
||||
* @ORM\HasLifecycleCallbacks()
|
||||
* @UniqueEntity({"supplierpartnr", "supplier", "part"})
|
||||
|
|
|
@ -66,7 +66,10 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||
* Class Pricedetail.
|
||||
*
|
||||
* @ORM\Entity()
|
||||
* @ORM\Table("`pricedetails`")
|
||||
* @ORM\Table("`pricedetails`", indexes={
|
||||
* @ORM\Index(name="pricedetails_idx_min_discount", columns={"min_discount_quantity"}),
|
||||
* @ORM\Index(name="pricedetails_idx_min_discount_price_qty", columns={"min_discount_quantity", "price_related_quantity"}),
|
||||
* })
|
||||
* @ORM\HasLifecycleCallbacks()
|
||||
* @UniqueEntity(fields={"min_discount_quantity", "orderdetail"})
|
||||
*/
|
||||
|
|
|
@ -56,7 +56,10 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||
* This entity represents an user group.
|
||||
*
|
||||
* @ORM\Entity()
|
||||
* @ORM\Table("`groups`")
|
||||
* @ORM\Table("`groups`", indexes={
|
||||
* @ORM\Index(name="group_idx_name", columns={"name"}),
|
||||
* @ORM\Index(name="group_idx_parent_name", columns={"parent_id", "name"}),
|
||||
* })
|
||||
*/
|
||||
class Group extends AbstractStructuralDBElement implements HasPermissionsInterface
|
||||
{
|
||||
|
@ -75,8 +78,9 @@ class Group extends AbstractStructuralDBElement implements HasPermissionsInterfa
|
|||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="User", mappedBy="group")
|
||||
* @var Collection<User>
|
||||
*/
|
||||
protected Collection $users;
|
||||
protected $users;
|
||||
|
||||
/**
|
||||
* @var bool If true all users associated with this group must have enabled some kind of 2 factor authentication
|
||||
|
@ -85,7 +89,7 @@ class Group extends AbstractStructuralDBElement implements HasPermissionsInterfa
|
|||
protected $enforce2FA = false;
|
||||
/**
|
||||
* @var Collection<int, GroupAttachment>
|
||||
* @ORM\OneToMany(targetEntity="App\Entity\Attachments\ManufacturerAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true)
|
||||
* @ORM\OneToMany(targetEntity="App\Entity\Attachments\GroupAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true)
|
||||
* @ORM\OrderBy({"name" = "ASC"})
|
||||
* @Assert\Valid()
|
||||
*/
|
||||
|
|
|
@ -44,8 +44,7 @@ namespace App\Entity\UserSystem;
|
|||
|
||||
use App\Entity\Base\TimestampTrait;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use R\U2FTwoFactorBundle\Model\U2F\TwoFactorKeyInterface;
|
||||
use u2flib_server\Registration;
|
||||
use Jbtronics\TFAWebauthn\Model\LegacyU2FKeyInterface;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
|
@ -56,7 +55,7 @@ use u2flib_server\Registration;
|
|||
* })
|
||||
* @ORM\HasLifecycleCallbacks()
|
||||
*/
|
||||
class U2FKey implements TwoFactorKeyInterface
|
||||
class U2FKey implements LegacyU2FKeyInterface
|
||||
{
|
||||
use TimestampTrait;
|
||||
|
||||
|
@ -110,14 +109,6 @@ class U2FKey implements TwoFactorKeyInterface
|
|||
**/
|
||||
protected ?User $user = null;
|
||||
|
||||
public function fromRegistrationData(Registration $data): void
|
||||
{
|
||||
$this->keyHandle = $data->keyHandle;
|
||||
$this->publicKey = $data->publicKey;
|
||||
$this->certificate = $data->certificate;
|
||||
$this->counter = $data->counter;
|
||||
}
|
||||
|
||||
public function getKeyHandle(): string
|
||||
{
|
||||
return $this->keyHandle;
|
||||
|
|
|
@ -57,7 +57,9 @@ use App\Entity\PriceInformations\Currency;
|
|||
use App\Security\Interfaces\HasPermissionsInterface;
|
||||
use App\Validator\Constraints\Selectable;
|
||||
use App\Validator\Constraints\ValidPermission;
|
||||
use Jbtronics\TFAWebauthn\Model\LegacyU2FKeyInterface;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
use Webauthn\PublicKeyCredentialUserEntity;
|
||||
use function count;
|
||||
use DateTime;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
@ -65,8 +67,6 @@ use Doctrine\Common\Collections\Collection;
|
|||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Exception;
|
||||
use function in_array;
|
||||
use R\U2FTwoFactorBundle\Model\U2F\TwoFactorInterface as U2FTwoFactorInterface;
|
||||
use R\U2FTwoFactorBundle\Model\U2F\TwoFactorKeyInterface;
|
||||
use Scheb\TwoFactorBundle\Model\BackupCodeInterface;
|
||||
use Scheb\TwoFactorBundle\Model\Google\TwoFactorInterface;
|
||||
use Scheb\TwoFactorBundle\Model\PreferredProviderInterface;
|
||||
|
@ -74,17 +74,20 @@ use Scheb\TwoFactorBundle\Model\TrustedDeviceInterface;
|
|||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Jbtronics\TFAWebauthn\Model\TwoFactorInterface as WebauthnTwoFactorInterface;
|
||||
|
||||
/**
|
||||
* This entity represents a user, which can log in and have permissions.
|
||||
* Also this entity is able to save some informations about the user, like the names, email-address and other info.
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="App\Repository\UserRepository")
|
||||
* @ORM\Table("`users`")
|
||||
* @ORM\Table("`users`", indexes={
|
||||
* @ORM\Index(name="user_idx_username", columns={"name"})
|
||||
* })
|
||||
* @ORM\EntityListeners({"App\EntityListeners\TreeCacheInvalidationListener"})
|
||||
* @UniqueEntity("name", message="validator.user.username_already_used")
|
||||
*/
|
||||
class User extends AttachmentContainingDBElement implements UserInterface, HasPermissionsInterface, TwoFactorInterface, BackupCodeInterface, TrustedDeviceInterface, U2FTwoFactorInterface, PreferredProviderInterface, PasswordAuthenticatedUserInterface
|
||||
class User extends AttachmentContainingDBElement implements UserInterface, HasPermissionsInterface, TwoFactorInterface, BackupCodeInterface, TrustedDeviceInterface, WebauthnTwoFactorInterface, PreferredProviderInterface, PasswordAuthenticatedUserInterface
|
||||
{
|
||||
//use MasterAttachmentTrait;
|
||||
|
||||
|
@ -146,7 +149,8 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
|
|||
|
||||
/**
|
||||
* @var Group|null the group this user belongs to
|
||||
* @ORM\ManyToOne(targetEntity="Group", inversedBy="users", fetch="EAGER")
|
||||
* DO NOT PUT A fetch eager here! Otherwise you can not unset the group of a user! This seems to be some kind of bug in doctrine. Maybe this is fixed in future versions.
|
||||
* @ORM\ManyToOne(targetEntity="Group", inversedBy="users")
|
||||
* @ORM\JoinColumn(name="group_id", referencedColumnName="id")
|
||||
* @Selectable()
|
||||
*/
|
||||
|
@ -239,11 +243,17 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
|
|||
*/
|
||||
protected ?DateTime $backupCodesGenerationDate = null;
|
||||
|
||||
/** @var Collection<int, TwoFactorKeyInterface>
|
||||
/** @var Collection<int, LegacyU2FKeyInterface>
|
||||
* @ORM\OneToMany(targetEntity="App\Entity\UserSystem\U2FKey", mappedBy="user", cascade={"REMOVE"}, orphanRemoval=true)
|
||||
*/
|
||||
protected $u2fKeys;
|
||||
|
||||
/**
|
||||
* @var Collection<int, WebauthnKey>
|
||||
* @ORM\OneToMany(targetEntity="App\Entity\UserSystem\WebauthnKey", mappedBy="user", cascade={"REMOVE"}, orphanRemoval=true)
|
||||
*/
|
||||
protected $webauthn_keys;
|
||||
|
||||
/**
|
||||
* @var Currency|null The currency the user wants to see prices in.
|
||||
* Dont use fetch=EAGER here, this will cause problems with setting the currency setting.
|
||||
|
@ -272,6 +282,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
|
|||
parent::__construct();
|
||||
$this->permissions = new PermissionsEmbed();
|
||||
$this->u2fKeys = new ArrayCollection();
|
||||
$this->webauthn_keys = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -762,7 +773,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
|
|||
*/
|
||||
public function isBackupCode(string $code): bool
|
||||
{
|
||||
return in_array($code, $this->backupCodes, true);
|
||||
return in_array($code, $this->getBackupCodes(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -772,7 +783,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
|
|||
*/
|
||||
public function invalidateBackupCode(string $code): void
|
||||
{
|
||||
$key = array_search($code, $this->backupCodes, true);
|
||||
$key = array_search($code, $this->getBackupCodes(), true);
|
||||
if (false !== $key) {
|
||||
unset($this->backupCodes[$key]);
|
||||
}
|
||||
|
@ -836,48 +847,48 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
|
|||
++$this->trustedDeviceCookieVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if U2F is enabled.
|
||||
*/
|
||||
public function isU2FAuthEnabled(): bool
|
||||
{
|
||||
return count($this->u2fKeys) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all U2F Keys that are associated with this user.
|
||||
*
|
||||
* @psalm-return Collection<int, TwoFactorKeyInterface>
|
||||
*/
|
||||
public function getU2FKeys(): Collection
|
||||
{
|
||||
return $this->u2fKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a U2F key to this user.
|
||||
*/
|
||||
public function addU2FKey(TwoFactorKeyInterface $key): void
|
||||
{
|
||||
$this->u2fKeys->add($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a U2F key from this user.
|
||||
*/
|
||||
public function removeU2FKey(TwoFactorKeyInterface $key): void
|
||||
{
|
||||
$this->u2fKeys->removeElement($key);
|
||||
}
|
||||
|
||||
public function getPreferredTwoFactorProvider(): ?string
|
||||
{
|
||||
//If U2F is available then prefer it
|
||||
if ($this->isU2FAuthEnabled()) {
|
||||
return 'u2f_two_factor';
|
||||
//if ($this->isU2FAuthEnabled()) {
|
||||
// return 'u2f_two_factor';
|
||||
//}
|
||||
|
||||
if ($this->isWebAuthnAuthenticatorEnabled()) {
|
||||
return 'webauthn_two_factor_provider';
|
||||
}
|
||||
|
||||
//Otherwise use other methods
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isWebAuthnAuthenticatorEnabled(): bool
|
||||
{
|
||||
return count($this->u2fKeys) > 0
|
||||
|| count($this->webauthn_keys) > 0;
|
||||
}
|
||||
|
||||
public function getLegacyU2FKeys(): iterable
|
||||
{
|
||||
return $this->u2fKeys;
|
||||
}
|
||||
|
||||
public function getWebAuthnUser(): PublicKeyCredentialUserEntity
|
||||
{
|
||||
return new PublicKeyCredentialUserEntity(
|
||||
$this->getUsername(),
|
||||
(string) $this->getId(),
|
||||
$this->getFullName(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getWebauthnKeys(): iterable
|
||||
{
|
||||
return $this->webauthn_keys;
|
||||
}
|
||||
|
||||
public function addWebauthnKey(WebauthnKey $webauthnKey): void
|
||||
{
|
||||
$this->webauthn_keys->add($webauthnKey);
|
||||
}
|
||||
}
|
||||
|
|
98
src/Entity/UserSystem/WebauthnKey.php
Normal file
98
src/Entity/UserSystem/WebauthnKey.php
Normal file
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity\UserSystem;
|
||||
|
||||
use App\Entity\Base\TimestampTrait;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Webauthn\PublicKeyCredentialSource as BasePublicKeyCredentialSource;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="webauthn_keys")
|
||||
* @ORM\Entity()
|
||||
* @ORM\HasLifecycleCallbacks()
|
||||
*/
|
||||
class WebauthnKey extends BasePublicKeyCredentialSource
|
||||
{
|
||||
use TimestampTrait;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
protected int $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
protected string $name;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="App\Entity\UserSystem\User", inversedBy="webauthn_keys")
|
||||
**/
|
||||
protected ?User $user = null;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return WebauthnKey
|
||||
*/
|
||||
public function setName(string $name): WebauthnKey
|
||||
{
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User|null
|
||||
*/
|
||||
public function getUser(): ?User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User|null $user
|
||||
* @return WebauthnKey
|
||||
*/
|
||||
public function setUser(?User $user): WebauthnKey
|
||||
{
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function fromRegistration(BasePublicKeyCredentialSource $registration): self
|
||||
{
|
||||
return new self(
|
||||
$registration->getPublicKeyCredentialId(),
|
||||
$registration->getType(),
|
||||
$registration->getTransports(),
|
||||
$registration->getAttestationType(),
|
||||
$registration->getTrustPath(),
|
||||
$registration->getAaguid(),
|
||||
$registration->getCredentialPublicKey(),
|
||||
$registration->getUserHandle(),
|
||||
$registration->getCounter(),
|
||||
$registration->getOtherUI()
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue