src/Platform/SecurityBundle/Entity/Identity/Account.php line 49

Open in your IDE?
  1. <?php
  2. namespace Platform\SecurityBundle\Entity\Identity;
  3. use Cms\ContainerBundle\Entity\Container;
  4. use Cms\CoreBundle\Model\Interfaces\OneRosterable\OneRosterableInterface;
  5. use Cms\CoreBundle\Model\Interfaces\OneRosterable\OneRosterableTrait;
  6. use Cms\ImportBundle\Model\Interfaces\Importable\ImportableInterface;
  7. use Cms\ImportBundle\Model\Interfaces\Importable\ImportableTrait;
  8. use Common\Util\Passwords;
  9. use DateTime;
  10. use DateTimeInterface;
  11. use Doctrine\Common\Collections\ArrayCollection;
  12. use Doctrine\Common\Collections\Criteria;
  13. use League\OAuth2\Server\Entities\UserEntityInterface;
  14. use Doctrine\Common\Collections\Collection;
  15. use Platform\SecurityBundle\Entity\Access\RoleAssociation\AccountRoleAssociation;
  16. use Platform\SecurityBundle\Entity\Access\SpecialPermissions;
  17. use Cms\TenantBundle\Entity\TenantedEntity;
  18. use Doctrine\ORM\Mapping as ORM;
  19. use Platform\SecurityBundle\Entity\Profiles\SystemProfile;
  20. use Platform\SecurityBundle\Service\Sentry;
  21. use Serializable;
  22. use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
  23. use Symfony\Component\Security\Core\User\UserInterface;
  24. use Symfony\Component\Security\Core\User\EquatableInterface;
  25. use App\Entity\OAuth2\Admin\AbstractAdminToken;
  26. /**
  27.  * Defines a user account in the system that a person can use to gain access to the system.
  28.  *
  29.  * Class Account
  30.  * @package Platform\SecurityBundle\Entity\Identity
  31.  *
  32.  * @ORM\Entity(repositoryClass = "Platform\SecurityBundle\Doctrine\Identity\AccountRepository")
  33.  * @ORM\Table(
  34.  *  name = "cms__security__identity__account",
  35.  *  uniqueConstraints = {
  36.  *      @ORM\UniqueConstraint(
  37.  *          name = "uidx__only_one_email_per_tenant",
  38.  *          columns = {
  39.  *              "tenant",
  40.  *              "email"
  41.  *          }
  42.  *      )
  43.  *  }
  44.  * )
  45.  */
  46. class Account extends TenantedEntity implements
  47.     Serializable,
  48.     UserInterface,
  49.     EquatableInterface,
  50.     OneRosterableInterface,
  51.     ImportableInterface,
  52.     UserEntityInterface,
  53.     PasswordAuthenticatedUserInterface
  54. {
  55.     use OneRosterableTrait;
  56.     use ImportableTrait;
  57.     /**
  58.      * The email address of the user.
  59.      * Any user in the system requires an email address, and they must be unique among the tenant.
  60.      * This also acts as a username in the system.
  61.      *
  62.      * @var string|null
  63.      *
  64.      * @ORM\Column(type = "string", nullable = false)
  65.      */
  66.     protected ?string $email;
  67.     /**
  68.      * Determines whether the user is able to log in.
  69.      * This is distinct from the verification stuff.
  70.      *
  71.      * @var bool
  72.      *
  73.      * @ORM\Column(type = "boolean", nullable = false)
  74.      */
  75.     protected bool $active true;
  76.     /**
  77.      * Any kind of special permissions as designated by our system.
  78.      *
  79.      * @var SpecialPermissions
  80.      *
  81.      * @ORM\Embedded(
  82.      *     class = "Platform\SecurityBundle\Entity\Access\SpecialPermissions",
  83.      *     columnPrefix = "specialPermissions_"
  84.      * )
  85.      */
  86.     protected SpecialPermissions $specialPermissions;
  87.     /**
  88.      * @var SystemProfile
  89.      *
  90.      * @ORM\Embedded(
  91.      *     class = "Platform\SecurityBundle\Entity\Profiles\SystemProfile",
  92.      *     columnPrefix = "systemProfile_"
  93.      * )
  94.      */
  95.     protected SystemProfile $systemProfile;
  96.     /**
  97.      * @var Collection|AccountRoleAssociation[]
  98.      *
  99.      * @ORM\OneToMany(
  100.      *     targetEntity = "Platform\SecurityBundle\Entity\Access\RoleAssociation\AccountRoleAssociation",
  101.      *     mappedBy = "account"
  102.      * )
  103.      */
  104.     protected Collection $accountRoles;
  105.     /**
  106.      * @var DateTimeInterface|null
  107.      *
  108.      * @ORM\Column(
  109.      *     name = "policyAcceptedOn",
  110.      *     type = "datetime",
  111.      *     nullable = true,
  112.      *  )
  113.      */
  114.     protected ?DateTimeInterface $policyAcceptedOn null;
  115.     /**
  116.      * @var DateTimeInterface|null
  117.      *
  118.      * @ORM\Column(
  119.      *     type = "datetime",
  120.      *     nullable = true,
  121.      *  )
  122.      */
  123.     protected ?DateTimeInterface $termsAcceptedOn null;
  124.     /**
  125.      * @var Collection
  126.      *
  127.      * @ORM\ManyToMany(
  128.      *     targetEntity = "Cms\ContainerBundle\Entity\Container"
  129.      * )
  130.      * @ORM\JoinTable(
  131.      *     name = "cms__container__favorite_container",
  132.      *     joinColumns = {
  133.      *         @ORM\JoinColumn(
  134.      *             name = "account",
  135.      *             referencedColumnName = "id",
  136.      *             onDelete = "CASCADE"
  137.      *         )
  138.      *     },
  139.      *     inverseJoinColumns = {
  140.      *         @ORM\JoinColumn(
  141.      *             name = "container",
  142.      *             referencedColumnName = "id",
  143.      *             onDelete = "CASCADE"
  144.      *         )
  145.      *     }
  146.      * )
  147.      */
  148.     protected Collection $favorites;
  149.     /**
  150.      * @var string|null
  151.      *
  152.      * @ORM\Column(
  153.      *     type = "string",
  154.      *     nullable = true
  155.      * )
  156.      */
  157.     protected ?string $password;
  158.     /**
  159.      * @var DateTime|null
  160.      *
  161.      * @ORM\Column(
  162.      *     type = "datetime",
  163.      *     nullable = true
  164.      * )
  165.      */
  166.     protected ?DateTime $passwordChangedAt;
  167.     /**
  168.      * @var DateTime|null
  169.      *
  170.      * @ORM\Column(
  171.      *     type = "datetime",
  172.      *     nullable = true
  173.      * )
  174.      */
  175.     protected ?DateTime $lastLoggedInAt;
  176.     /**
  177.      * @var array|null
  178.      *
  179.      * @ORM\Column(
  180.      *     type = "json",
  181.      *     nullable = true,
  182.      * )
  183.      */
  184.     protected ?array $metadata = [];
  185.     /**
  186.      * @var Collection|AbstractAdminToken[]
  187.      *
  188.      * @ORM\OneToMany(
  189.      *     targetEntity = AbstractAdminToken::class,
  190.      *     mappedBy = "account",
  191.      * )
  192.      * @ORM\OrderBy({
  193.      *     "createdAt" = "DESC",
  194.      * })
  195.      */
  196.     protected Collection $tokens;
  197.     /**
  198.      * Constructor
  199.      */
  200.     public function __construct()
  201.     {
  202.         $this->specialPermissions = new SpecialPermissions();
  203.         $this->systemProfile = new SystemProfile();
  204.         $this->accountRoles = new ArrayCollection();
  205.         $this->favorites = new ArrayCollection();
  206.         $this->tokens = new ArrayCollection();
  207.     }
  208.     /**
  209.      * {@inheritdoc}
  210.      */
  211.     public function serialize(): ?string
  212.     {
  213.         return serialize($this->getId());
  214.     }
  215.     /**
  216.      * {@inheritdoc}
  217.      */
  218.     public function unserialize($data): void
  219.     {
  220.         $this->id unserialize($data);
  221.     }
  222.     /**
  223.      * TODO: this really needs implemented as a permission check...
  224.      *
  225.      * Determines whether or not this user is a core company team member.
  226.      * Such members may have extended access.
  227.      *
  228.      * @return bool
  229.      */
  230.     public function isInternal(): bool
  231.     {
  232.         $emails = array(
  233.             // TODO: this method needs to be like a service call so we can do more with it, like be able to use the param that sets the csadmin account email
  234.             'csadmin@innersync.com',
  235.         );
  236.         foreach ($emails as $email) {
  237.             if ($this->getEmail() === $email) {
  238.                 return true;
  239.             }
  240.         }
  241.         return false;
  242.     }
  243.     /**
  244.      * TODO: this really needs implemented as a permission check...
  245.      *
  246.      * Determines whether or not this user is a core company contractor.
  247.      * Such members may have extended access.
  248.      *
  249.      * @return bool
  250.      */
  251.     public function isContractor(): bool
  252.     {
  253.         if ($this->isInternal()) {
  254.             return true;
  255.         }
  256.         $emails = array(
  257.             'arif.ews@gmail.com',
  258.         );
  259.         foreach ($emails as $email) {
  260.             if ($this->getEmail() === $email) {
  261.                 return true;
  262.             }
  263.         }
  264.         return false;
  265.     }
  266.     /**
  267.      * Implemented to satisfy Symfony security needs.
  268.      * In our system, the "username" for an account is the ID of the account.
  269.      * "Alternate" usernames could be UIDs or emails.
  270.      *
  271.      * {@inheritdoc}
  272.      */
  273.     public function getUsername(): string
  274.     {
  275.         // TODO: remove the getUsername function once we are on Symfony 6
  276.         return $this->getUserIdentifier();
  277.     }
  278.     /**
  279.      * {@inheritdoc}
  280.      */
  281.     public function getUserIdentifier(): string
  282.     {
  283.         return (string) $this->getId();
  284.     }
  285.     /**
  286.      * Implemented to satisfy Symfony security needs.
  287.      * Salts are not tracked by us (PHP is used for password checking).
  288.      *
  289.      * {@inheritdoc}
  290.      */
  291.     public function getSalt(): ?string
  292.     {
  293.         // TODO: this function is deprecated
  294.         return null;
  295.     }
  296.     /**
  297.      * Implemented to satisfy Symfony security needs.
  298.      * Password checking is done with custom code.
  299.      *
  300.      * {@inheritdoc}
  301.      */
  302.     public function getPassword(): ?string
  303.     {
  304.         // TODO: this function is deprecated
  305.         return $this->password;
  306.     }
  307.     /**
  308.      * @param string|null $value
  309.      * @param bool|DateTime|null $timestamp
  310.      * @return $this
  311.      */
  312.     public function setPassword(?string $value$timestamp true): self
  313.     {
  314.         switch ($timestamp) {
  315.             case $timestamp === true:
  316.                 $timestamp = new DateTime();
  317.                 break;
  318.             case $timestamp instanceof DateTime:
  319.                 break;
  320.             default:
  321.                 $timestamp null;
  322.         }
  323.         if ($timestamp instanceof DateTime) {
  324.             $this->setPasswordChangedAt($timestamp);
  325.         }
  326.         $this->password $value;
  327.         return $this;
  328.     }
  329.     /**
  330.      * @param string $value
  331.      * @param bool|DateTime|null $timestamp
  332.      * @return $this
  333.      */
  334.     public function setPasswordRaw(string $value$timestamp true): self
  335.     {
  336.         return $this->setPassword(Passwords::hash($value), $timestamp);
  337.     }
  338.     /**
  339.      * @return bool
  340.      */
  341.     public function hasPassword(): bool
  342.     {
  343.         return ( ! empty($this->getPassword()));
  344.     }
  345.     /**
  346.      * @return DateTime|null
  347.      */
  348.     public function getPasswordChangedAt(): ?DateTime
  349.     {
  350.         return $this->passwordChangedAt;
  351.     }
  352.     /**
  353.      * @param DateTime $value
  354.      * @return $this
  355.      */
  356.     public function setPasswordChangedAt(DateTime $value): self
  357.     {
  358.         $this->passwordChangedAt $value;
  359.         return $this;
  360.     }
  361.     /**
  362.      * @return DateTime|null
  363.      */
  364.     public function getLastLoggedInAt(): ?DateTime
  365.     {
  366.         return $this->lastLoggedInAt;
  367.     }
  368.     /**
  369.      * @param DateTime $value
  370.      * @return $this
  371.      */
  372.     public function setLastLoggedInAt(DateTime $value): self
  373.     {
  374.         $this->lastLoggedInAt $value;
  375.         return $this;
  376.     }
  377.     /**
  378.      * Implemented to satisfy Symfony security needs.
  379.      * We have more advanced, custom roles that require special handling.
  380.      *
  381.      * @return array
  382.      */
  383.     public function getRoles(): array
  384.     {
  385.         return [];
  386.     }
  387.     /**
  388.      * Implemented to satisfy Symfony security needs.
  389.      * As we do not store passwords or anything sensitive in this class,
  390.      * this does nothing and returns a successful response.
  391.      *
  392.      * {@inheritdoc}
  393.      */
  394.     public function eraseCredentials(): bool
  395.     {
  396.         return true;
  397.     }
  398.     /**
  399.      * @return string|null
  400.      */
  401.     public function getEmail(): ?string
  402.     {
  403.         return $this->email;
  404.     }
  405.     /**
  406.      * @return bool
  407.      */
  408.     public function isActive(): bool
  409.     {
  410.         return $this->active;
  411.     }
  412.     /**
  413.      * @param string $value
  414.      * @return $this
  415.      */
  416.     public function setEmail(string $value): self
  417.     {
  418.         $this->email $value;
  419.         return $this;
  420.     }
  421.     /**
  422.      * @param bool $value
  423.      * @return $this
  424.      */
  425.     public function setActive(bool $value): self
  426.     {
  427.         $this->active = ($value === true);
  428.         return $this;
  429.     }
  430.     /**
  431.      * Implemented to satisfy Symfony security needs.
  432.      * Ensures that our usernames are both not null AND they are equal.
  433.      *
  434.      * {@inheritdoc}
  435.      * @param Account $user
  436.      */
  437.     public function isEqualTo(UserInterface $user): bool
  438.     {
  439.         return (
  440.             ($this->getUserIdentifier() !== null && $user->getUserIdentifier() !== null)
  441.             &&
  442.             ($this->getUserIdentifier() === $user->getUserIdentifier())
  443.         );
  444.     }
  445.     /**
  446.      * @param bool $default
  447.      * @return string|null
  448.      */
  449.     public function getDisplayName(bool $default true): ?string
  450.     {
  451.         $display $this->getSystemProfile()->getDisplayName()
  452.             ?: $this->getSystemProfile()->getFullName()
  453.             ?: null;
  454.         if ( ! $display && $default) {
  455.             $display $this->getEmail();
  456.         }
  457.         return $display ?: null;
  458.     }
  459.     /**
  460.      * @return SystemProfile
  461.      */
  462.     public function getSystemProfile(): SystemProfile
  463.     {
  464.         return $this->systemProfile ?: new SystemProfile();
  465.     }
  466.     /**
  467.      * @return SpecialPermissions
  468.      */
  469.     public function getSpecialPermissions(): SpecialPermissions
  470.     {
  471.         return $this->specialPermissions;
  472.     }
  473.     /**
  474.      * @param bool $value
  475.      * @return $this
  476.      */
  477.     public function setSuperUserSpecialPermission(bool $value): self
  478.     {
  479.         $this->getSpecialPermissions()->setSuperUser($value);
  480.         return $this;
  481.     }
  482.     /**
  483.      * @return ArrayCollection|AccountRoleAssociation[]
  484.      */
  485.     public function getAccountRoles(): Collection
  486.     {
  487.         return $this->accountRoles;
  488.     }
  489.     /**
  490.      * @return DateTimeInterface|null
  491.      */
  492.     public function getPolicyAcceptedOn(): ?DateTimeInterface
  493.     {
  494.         return $this->policyAcceptedOn;
  495.     }
  496.     /**
  497.      * @param DateTimeInterface|null $policyAcceptedOn
  498.      * @return $this
  499.      */
  500.     public function setPolicyAcceptedOn(?DateTimeInterface $policyAcceptedOn): self
  501.     {
  502.         $this->policyAcceptedOn $policyAcceptedOn;
  503.         return $this;
  504.     }
  505.     /**
  506.      * @return DateTimeInterface|null
  507.      */
  508.     public function getTermsAcceptedOn(): ?DateTimeInterface
  509.     {
  510.         return $this->termsAcceptedOn;
  511.     }
  512.     /**
  513.      * @param DateTimeInterface|null $termsAcceptedOn
  514.      * @return $this
  515.      */
  516.     public function setTermsAcceptedOn(DateTimeInterface $termsAcceptedOn): self
  517.     {
  518.         $this->termsAcceptedOn $termsAcceptedOn;
  519.         return $this;
  520.     }
  521.     /**
  522.      * @param Container $container
  523.      * @return $this
  524.      */
  525.     public function addFavorite(Container $container): self
  526.     {
  527.         $this->favorites->add($container);
  528.         return $this;
  529.     }
  530.     /**
  531.      * @return Collection
  532.      */
  533.     public function getFavorites(): Collection
  534.     {
  535.         return $this->favorites;
  536.     }
  537.     /**
  538.      * @param Container $container
  539.      * @return $this
  540.      */
  541.     public function removeFavorite(Container $container): self
  542.     {
  543.         $this->favorites->removeElement($container);
  544.         return $this;
  545.     }
  546.     /**
  547.      * @return bool
  548.      */
  549.     public function canManagePassword(): bool
  550.     {
  551.         //return ( ! empty($this->getPassword()) && ! $this->isOneRoster());
  552.         return ( ! empty($this->getPassword()));
  553.     }
  554.     /**
  555.      * @return array|null
  556.      */
  557.     public function getMetadata(): ?array
  558.     {
  559.         return $this->metadata;
  560.     }
  561.     /**
  562.      * @param array|null $metadata
  563.      * @return Account
  564.      */
  565.     public function setMetadata(?array $metadata): self
  566.     {
  567.         $this->metadata $metadata ?: null;
  568.         return $this;
  569.     }
  570.     /**
  571.      * @param array $metadata
  572.      * @return $this
  573.      */
  574.     public function mergeMetadata(array $metadata): self
  575.     {
  576.         $this->setMetadata(
  577.             array_merge(
  578.                 $this->getMetadata(),
  579.                 $metadata,
  580.             ),
  581.         );
  582.         return $this;
  583.     }
  584.     /**
  585.      * @return array
  586.      */
  587.     public function getMetadataSchools(): array
  588.     {
  589.         $metadata $this->getMetadata();
  590.         return ($metadata && isset($metadata['_schools'])) ? $metadata['_schools'] : [];
  591.     }
  592.     /**
  593.      * {@inheritDoc}
  594.      */
  595.     public function getIdentifier(): string
  596.     {
  597.         return $this->getUidString();
  598.     }
  599.     /**
  600.      * @param $criteria
  601.      * @return iterable
  602.      */
  603.     public function getTokens($criteria null): iterable
  604.     {
  605.         if ( ! $this->tokens instanceof Collection) {
  606.             $this->tokens = new ArrayCollection();
  607.         }
  608.         $criteria = ($criteria === true) ? new Criteria() : ($criteria ?: null);
  609.         if ( ! empty($criteria)) {
  610.             return $this->tokens->matching($criteria);
  611.         }
  612.         return $this->tokens;
  613.     }
  614.     /**
  615.      * @return array
  616.      */
  617.     public function getInternalPermissions(): array
  618.     {
  619.         // TODO: if ever more internal permissions are made, this needs to report only the ones applicable to the account...
  620.         return $this->isInternal() ? Sentry::INTERNAL_PERMISSIONS : [];
  621.     }
  622. }