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.      * Globally unique "internalUid" field from SchoolStatus CRS-based data.
  199.      *
  200.      * @var string|null
  201.      *
  202.      * @ORM\Column(
  203.      *     type = "string",
  204.      *     nullable = true,
  205.      * )
  206.      */
  207.     protected ?string $internalUid null;
  208.     /**
  209.      * Constructor
  210.      */
  211.     public function __construct()
  212.     {
  213.         $this->specialPermissions = new SpecialPermissions();
  214.         $this->systemProfile = new SystemProfile();
  215.         $this->accountRoles = new ArrayCollection();
  216.         $this->favorites = new ArrayCollection();
  217.         $this->tokens = new ArrayCollection();
  218.     }
  219.     /**
  220.      * {@inheritdoc}
  221.      */
  222.     public function serialize(): ?string
  223.     {
  224.         return serialize($this->getId());
  225.     }
  226.     /**
  227.      * {@inheritdoc}
  228.      */
  229.     public function unserialize($data): void
  230.     {
  231.         $this->id unserialize($data);
  232.     }
  233.     /**
  234.      * TODO: this really needs implemented as a permission check...
  235.      *
  236.      * Determines whether or not this user is a core company team member.
  237.      * Such members may have extended access.
  238.      *
  239.      * @return bool
  240.      */
  241.     public function isInternal(): bool
  242.     {
  243.         $emails = array(
  244.             // 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
  245.             'csadmin@innersync.com',
  246.         );
  247.         foreach ($emails as $email) {
  248.             if ($this->getEmail() === $email) {
  249.                 return true;
  250.             }
  251.         }
  252.         return false;
  253.     }
  254.     /**
  255.      * TODO: this really needs implemented as a permission check...
  256.      *
  257.      * Determines whether or not this user is a core company contractor.
  258.      * Such members may have extended access.
  259.      *
  260.      * @return bool
  261.      */
  262.     public function isContractor(): bool
  263.     {
  264.         if ($this->isInternal()) {
  265.             return true;
  266.         }
  267.         $emails = array(
  268.             'arif.ews@gmail.com',
  269.         );
  270.         foreach ($emails as $email) {
  271.             if ($this->getEmail() === $email) {
  272.                 return true;
  273.             }
  274.         }
  275.         return false;
  276.     }
  277.     /**
  278.      * Implemented to satisfy Symfony security needs.
  279.      * In our system, the "username" for an account is the ID of the account.
  280.      * "Alternate" usernames could be UIDs or emails.
  281.      *
  282.      * {@inheritdoc}
  283.      */
  284.     public function getUsername(): string
  285.     {
  286.         // TODO: remove the getUsername function once we are on Symfony 6
  287.         return $this->getUserIdentifier();
  288.     }
  289.     /**
  290.      * {@inheritdoc}
  291.      */
  292.     public function getUserIdentifier(): string
  293.     {
  294.         return (string) $this->getId();
  295.     }
  296.     /**
  297.      * Implemented to satisfy Symfony security needs.
  298.      * Salts are not tracked by us (PHP is used for password checking).
  299.      *
  300.      * {@inheritdoc}
  301.      */
  302.     public function getSalt(): ?string
  303.     {
  304.         // TODO: this function is deprecated
  305.         return null;
  306.     }
  307.     /**
  308.      * Implemented to satisfy Symfony security needs.
  309.      * Password checking is done with custom code.
  310.      *
  311.      * {@inheritdoc}
  312.      */
  313.     public function getPassword(): ?string
  314.     {
  315.         // TODO: this function is deprecated
  316.         return $this->password;
  317.     }
  318.     /**
  319.      * @param string|null $value
  320.      * @param bool|DateTime|null $timestamp
  321.      * @return $this
  322.      */
  323.     public function setPassword(?string $value$timestamp true): self
  324.     {
  325.         switch ($timestamp) {
  326.             case $timestamp === true:
  327.                 $timestamp = new DateTime();
  328.                 break;
  329.             case $timestamp instanceof DateTime:
  330.                 break;
  331.             default:
  332.                 $timestamp null;
  333.         }
  334.         if ($timestamp instanceof DateTime) {
  335.             $this->setPasswordChangedAt($timestamp);
  336.         }
  337.         $this->password $value;
  338.         return $this;
  339.     }
  340.     /**
  341.      * @param string $value
  342.      * @param bool|DateTime|null $timestamp
  343.      * @return $this
  344.      */
  345.     public function setPasswordRaw(string $value$timestamp true): self
  346.     {
  347.         return $this->setPassword(Passwords::hash($value), $timestamp);
  348.     }
  349.     /**
  350.      * @return bool
  351.      */
  352.     public function hasPassword(): bool
  353.     {
  354.         return ( ! empty($this->getPassword()));
  355.     }
  356.     /**
  357.      * @return DateTime|null
  358.      */
  359.     public function getPasswordChangedAt(): ?DateTime
  360.     {
  361.         return $this->passwordChangedAt;
  362.     }
  363.     /**
  364.      * @param DateTime $value
  365.      * @return $this
  366.      */
  367.     public function setPasswordChangedAt(DateTime $value): self
  368.     {
  369.         $this->passwordChangedAt $value;
  370.         return $this;
  371.     }
  372.     /**
  373.      * @return DateTime|null
  374.      */
  375.     public function getLastLoggedInAt(): ?DateTime
  376.     {
  377.         return $this->lastLoggedInAt;
  378.     }
  379.     /**
  380.      * @param DateTime $value
  381.      * @return $this
  382.      */
  383.     public function setLastLoggedInAt(DateTime $value): self
  384.     {
  385.         $this->lastLoggedInAt $value;
  386.         return $this;
  387.     }
  388.     /**
  389.      * Implemented to satisfy Symfony security needs.
  390.      * We have more advanced, custom roles that require special handling.
  391.      *
  392.      * @return array
  393.      */
  394.     public function getRoles(): array
  395.     {
  396.         return [];
  397.     }
  398.     /**
  399.      * Implemented to satisfy Symfony security needs.
  400.      * As we do not store passwords or anything sensitive in this class,
  401.      * this does nothing and returns a successful response.
  402.      *
  403.      * {@inheritdoc}
  404.      */
  405.     public function eraseCredentials(): bool
  406.     {
  407.         return true;
  408.     }
  409.     /**
  410.      * @return string|null
  411.      */
  412.     public function getEmail(): ?string
  413.     {
  414.         return $this->email;
  415.     }
  416.     /**
  417.      * @return bool
  418.      */
  419.     public function isActive(): bool
  420.     {
  421.         return $this->active;
  422.     }
  423.     /**
  424.      * @param string $value
  425.      * @return $this
  426.      */
  427.     public function setEmail(string $value): self
  428.     {
  429.         $this->email $value;
  430.         return $this;
  431.     }
  432.     /**
  433.      * @param bool $value
  434.      * @return $this
  435.      */
  436.     public function setActive(bool $value): self
  437.     {
  438.         $this->active = ($value === true);
  439.         return $this;
  440.     }
  441.     /**
  442.      * Implemented to satisfy Symfony security needs.
  443.      * Ensures that our usernames are both not null AND they are equal.
  444.      *
  445.      * {@inheritdoc}
  446.      * @param Account $user
  447.      */
  448.     public function isEqualTo(UserInterface $user): bool
  449.     {
  450.         return (
  451.             ($this->getUserIdentifier() !== null && $user->getUserIdentifier() !== null)
  452.             &&
  453.             ($this->getUserIdentifier() === $user->getUserIdentifier())
  454.         );
  455.     }
  456.     /**
  457.      * @param bool $default
  458.      * @return string|null
  459.      */
  460.     public function getDisplayName(bool $default true): ?string
  461.     {
  462.         $display $this->getSystemProfile()->getDisplayName()
  463.             ?: $this->getSystemProfile()->getFullName()
  464.             ?: null;
  465.         if ( ! $display && $default) {
  466.             $display $this->getEmail();
  467.         }
  468.         return $display ?: null;
  469.     }
  470.     /**
  471.      * @return SystemProfile
  472.      */
  473.     public function getSystemProfile(): SystemProfile
  474.     {
  475.         return $this->systemProfile ?: new SystemProfile();
  476.     }
  477.     /**
  478.      * @return SpecialPermissions
  479.      */
  480.     public function getSpecialPermissions(): SpecialPermissions
  481.     {
  482.         return $this->specialPermissions;
  483.     }
  484.     /**
  485.      * @param bool $value
  486.      * @return $this
  487.      */
  488.     public function setSuperUserSpecialPermission(bool $value): self
  489.     {
  490.         $this->getSpecialPermissions()->setSuperUser($value);
  491.         return $this;
  492.     }
  493.     /**
  494.      * @return ArrayCollection|AccountRoleAssociation[]
  495.      */
  496.     public function getAccountRoles(): Collection
  497.     {
  498.         return $this->accountRoles;
  499.     }
  500.     /**
  501.      * @return DateTimeInterface|null
  502.      */
  503.     public function getPolicyAcceptedOn(): ?DateTimeInterface
  504.     {
  505.         return $this->policyAcceptedOn;
  506.     }
  507.     /**
  508.      * @param DateTimeInterface|null $policyAcceptedOn
  509.      * @return $this
  510.      */
  511.     public function setPolicyAcceptedOn(?DateTimeInterface $policyAcceptedOn): self
  512.     {
  513.         $this->policyAcceptedOn $policyAcceptedOn;
  514.         return $this;
  515.     }
  516.     /**
  517.      * @return DateTimeInterface|null
  518.      */
  519.     public function getTermsAcceptedOn(): ?DateTimeInterface
  520.     {
  521.         return $this->termsAcceptedOn;
  522.     }
  523.     /**
  524.      * @param DateTimeInterface|null $termsAcceptedOn
  525.      * @return $this
  526.      */
  527.     public function setTermsAcceptedOn(DateTimeInterface $termsAcceptedOn): self
  528.     {
  529.         $this->termsAcceptedOn $termsAcceptedOn;
  530.         return $this;
  531.     }
  532.     /**
  533.      * @param Container $container
  534.      * @return $this
  535.      */
  536.     public function addFavorite(Container $container): self
  537.     {
  538.         $this->favorites->add($container);
  539.         return $this;
  540.     }
  541.     /**
  542.      * @return Collection
  543.      */
  544.     public function getFavorites(): Collection
  545.     {
  546.         return $this->favorites;
  547.     }
  548.     /**
  549.      * @param Container $container
  550.      * @return $this
  551.      */
  552.     public function removeFavorite(Container $container): self
  553.     {
  554.         $this->favorites->removeElement($container);
  555.         return $this;
  556.     }
  557.     /**
  558.      * @return bool
  559.      */
  560.     public function canManagePassword(): bool
  561.     {
  562.         //return ( ! empty($this->getPassword()) && ! $this->isOneRoster());
  563.         return ( ! empty($this->getPassword()));
  564.     }
  565.     /**
  566.      * @return array|null
  567.      */
  568.     public function getMetadata(): ?array
  569.     {
  570.         return $this->metadata;
  571.     }
  572.     /**
  573.      * @param array|null $metadata
  574.      * @return Account
  575.      */
  576.     public function setMetadata(?array $metadata): self
  577.     {
  578.         $this->metadata $metadata ?: null;
  579.         return $this;
  580.     }
  581.     /**
  582.      * @param array $metadata
  583.      * @return $this
  584.      */
  585.     public function mergeMetadata(array $metadata): self
  586.     {
  587.         $this->setMetadata(
  588.             array_merge(
  589.                 $this->getMetadata(),
  590.                 $metadata,
  591.             ),
  592.         );
  593.         return $this;
  594.     }
  595.     /**
  596.      * @return array
  597.      */
  598.     public function getMetadataSchools(): array
  599.     {
  600.         $metadata $this->getMetadata();
  601.         return ($metadata && isset($metadata['_schools'])) ? $metadata['_schools'] : [];
  602.     }
  603.     /**
  604.      * {@inheritDoc}
  605.      */
  606.     public function getIdentifier(): string
  607.     {
  608.         return $this->getUidString();
  609.     }
  610.     /**
  611.      * @param $criteria
  612.      * @return iterable
  613.      */
  614.     public function getTokens($criteria null): iterable
  615.     {
  616.         if ( ! $this->tokens instanceof Collection) {
  617.             $this->tokens = new ArrayCollection();
  618.         }
  619.         $criteria = ($criteria === true) ? new Criteria() : ($criteria ?: null);
  620.         if ( ! empty($criteria)) {
  621.             return $this->tokens->matching($criteria);
  622.         }
  623.         return $this->tokens;
  624.     }
  625.     /**
  626.      * @return array
  627.      */
  628.     public function getInternalPermissions(): array
  629.     {
  630.         // TODO: if ever more internal permissions are made, this needs to report only the ones applicable to the account...
  631.         return $this->isInternal() ? Sentry::INTERNAL_PERMISSIONS : [];
  632.     }
  633.     /**
  634.      * @return string|null
  635.      */
  636.     public function getInternalUid(): ?string
  637.     {
  638.         return $this->internalUid;
  639.     }
  640.     /**
  641.      * @param string|null $internalUid
  642.      * @return $this
  643.      */
  644.     public function setInternalUid(?string $internalUid): self
  645.     {
  646.         $this->internalUid $internalUid ?: null;
  647.         return $this;
  648.     }
  649. }