diff --git a/api/docs/classes/App/Entity/Organization.md b/api/docs/classes/App/Entity/Organization.md index 266aa9004..d429e8c55 100644 --- a/api/docs/classes/App/Entity/Organization.md +++ b/api/docs/classes/App/Entity/Organization.md @@ -1,6 +1,6 @@ # App\Entity\Organization -This entity holds the information about an Organisation. +This entity holds the information about an Organization. diff --git a/api/docs/classes/App/Entity/User.md b/api/docs/classes/App/Entity/User.md index bc792a043..a51baa852 100644 --- a/api/docs/classes/App/Entity/User.md +++ b/api/docs/classes/App/Entity/User.md @@ -8,33 +8,33 @@ This entity holds the information about an User. ## Methods -| Name | Description | -|------|-------------| -|[__construct](#user__construct)|| -|[addApplication](#useraddapplication)|| -|[addUserGroup](#useraddusergroup)|| -|[getApplications](#usergetapplications)|| -|[getDateCreated](#usergetdatecreated)|| -|[getDateModified](#usergetdatemodified)|| -|[getEmail](#usergetemail)|| -|[getId](#usergetid)|| -|[getLocale](#usergetlocale)|| -|[getName](#usergetname)|| -|[getOrganisation](#usergetorganization)|| -|[getPassword](#usergetpassword)|| -|[getPerson](#usergetperson)|| -|[getRoles](#usergetroles)|| -|[getUserGroups](#usergetusergroups)|| -|[removeApplication](#userremoveapplication)|| -|[removeUserGroup](#userremoveusergroup)|| -|[setDateCreated](#usersetdatecreated)|| -|[setDateModified](#usersetdatemodified)|| -|[setEmail](#usersetemail)|| -|[setLocale](#usersetlocale)|| -|[setName](#usersetname)|| -|[setOrganisation](#usersetorganization)|| -|[setPassword](#usersetpassword)|| -|[setPerson](#usersetperson)|| +| Name | Description | +|---------------------------------------------|-------------| +| [__construct](#user__construct) || +| [addApplication](#useraddapplication) || +| [addUserGroup](#useraddusergroup) || +| [getApplications](#usergetapplications) || +| [getDateCreated](#usergetdatecreated) || +| [getDateModified](#usergetdatemodified) || +| [getEmail](#usergetemail) || +| [getId](#usergetid) || +| [getLocale](#usergetlocale) || +| [getName](#usergetname) || +| [getOrganization](#usergetorganization) || +| [getPassword](#usergetpassword) || +| [getPerson](#usergetperson) || +| [getRoles](#usergetroles) || +| [getUserGroups](#usergetusergroups) || +| [removeApplication](#userremoveapplication) || +| [removeUserGroup](#userremoveusergroup) || +| [setDateCreated](#usersetdatecreated) || +| [setDateModified](#usersetdatemodified) || +| [setEmail](#usersetemail) || +| [setLocale](#usersetlocale) || +| [setName](#usersetname) || +| [setOrganisation](#usersetorganization) || +| [setPassword](#usersetpassword) || +| [setPerson](#usersetperson) || @@ -279,12 +279,12 @@ This entity holds the information about an User.
-### User::getOrganisation +### User::getOrganization **Description** ```php - getOrganisation (void) + getOrganization (void) ``` @@ -567,12 +567,12 @@ This entity holds the information about an User.
-### User::setOrganisation +### User::setOrganization **Description** ```php - setOrganisation (void) + setOrganization (void) ``` diff --git a/api/migrations/Version20230922133622.php b/api/migrations/Version20230922133622.php new file mode 100644 index 000000000..03fe88ae2 --- /dev/null +++ b/api/migrations/Version20230922133622.php @@ -0,0 +1,34 @@ +addSql('ALTER TABLE action ADD user_id VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE cronjob ADD user_id VARCHAR(255) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('ALTER TABLE action DROP user_id'); + $this->addSql('ALTER TABLE cronjob DROP user_id'); + } +} diff --git a/api/migrations/Version20230926112500.php b/api/migrations/Version20230926112500.php new file mode 100644 index 000000000..cbbde0146 --- /dev/null +++ b/api/migrations/Version20230926112500.php @@ -0,0 +1,32 @@ +addSql('ALTER TABLE "user" RENAME COLUMN organisation_id TO organization_id'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('ALTER TABLE "user" DROP organisation_id'); + } +} diff --git a/api/src/Command/CronjobCommand.php b/api/src/Command/CronjobCommand.php index 99e914b57..fdefafa5b 100644 --- a/api/src/Command/CronjobCommand.php +++ b/api/src/Command/CronjobCommand.php @@ -5,6 +5,7 @@ namespace App\Command; use App\Entity\Cronjob; +use App\Entity\User; use App\Event\ActionEvent; use Cron\CronExpression; use Doctrine\ORM\EntityManagerInterface; @@ -66,6 +67,8 @@ protected function configure(): void /** * This function makes action events. * + * After running this function, even if it returns an exception, currentCronJobUserId should always be removed from cache. + * * @param Cronjob $cronjob * @param SymfonyStyle $io * @@ -85,6 +88,16 @@ public function makeActionEvent(Cronjob $cronjob, SymfonyStyle $io): void $io->newLine(); $io->newLine(); + // Keep track of the user used for running this CronJob. + // After makeActionEvent() is done, even if it returns an exception, currentCronJobUserId should be removed from cache (outside this function) + $this->session->remove('currentCronjobUserId'); + if ($cronjob->getUserId() !== null && Uuid::isValid($cronjob->getUserId()) === true) { + $user = $this->entityManager->getRepository('App:User')->find($cronjob->getUserId()); + if ($user instanceof User === true) { + $this->session->set('currentCronjobUserId', $cronjob->getUserId()); + } + } + $throws = $cronjob->getThrows(); foreach ($throws as $key => $throw) { $io->block("Dispatch ActionEvent for Throw: \"$throw\""); @@ -158,6 +171,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->block("Trace: {$exception->getTraceAsString()}"); $errorCount++; } + // Make sure we remove currentCronJobUserid from cache. + $this->session->remove('currentCronJobUserId'); $io->progressAdvance(); } @@ -186,6 +201,7 @@ private function handleCronjobIoStart(SymfonyStyle $io, Cronjob $cronjob) ['Id' => $cronjob->getId()->toString()], ['Name' => $cronjob->getName()], ['Description' => $cronjob->getDescription()], + ['UserId' => $cronjob->getUserId()], ['Crontab' => $cronjob->getCrontab()], ['Throws' => implode(', ', $cronjob->getThrows())], // ['Data' => "[{$this->objectEntityService->implodeMultiArray($cronjob->getData())}]"], diff --git a/api/src/Command/InitializationCommand.php b/api/src/Command/InitializationCommand.php index 047fa2852..6d4ac71ba 100644 --- a/api/src/Command/InitializationCommand.php +++ b/api/src/Command/InitializationCommand.php @@ -258,7 +258,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $user->setPassword($this->hasher->hashPassword($user, '!ChangeMe!')); $user->addSecurityGroup($securityGroupAdmin); $user->addApplication($application); - $user->setOrganisation($organization); + $user->setOrganization($organization); $this->entityManager->persist($user); } else { diff --git a/api/src/Controller/UserController.php b/api/src/Controller/UserController.php index d9fb2c201..ddc35e31d 100644 --- a/api/src/Controller/UserController.php +++ b/api/src/Controller/UserController.php @@ -100,8 +100,8 @@ public function resetTokenAction(SerializerInterface $serializer, \CommonGateway $user = $this->entityManager->getRepository('App:User')->find($user->getUserIdentifier()); - if ($user->getOrganisation() !== null) { - $organizations[] = $user->getOrganisation(); + if ($user->getOrganization() !== null) { + $organizations[] = $user->getOrganization(); } foreach ($user->getApplications() as $application) { if ($application->getOrganization() !== null) { @@ -110,7 +110,7 @@ public function resetTokenAction(SerializerInterface $serializer, \CommonGateway } // If user has no organization, we default activeOrganization to an organization of a userGroup this user has and else the application organization; - $this->session->set('activeOrganization', $user->getOrganisation()->getId()->toString()); + $this->session->set('activeOrganization', $user->getOrganization()->getId()->toString()); $user->setJwtToken($authenticationService->createJwtToken($user->getApplications()[0]->getPrivateKey(), $authenticationService->serializeUser($user, $this->session))); @@ -145,7 +145,7 @@ public function createAuthenticationUser(User $user): AuthenticationUser 'id' => $user->getId()->toString(), 'email' => $user->getEmail(), 'locale' => $user->getLocale(), - 'organization' => $user->getOrganisation()->getId()->toString(), + 'organization' => $user->getOrganization()->getId()->toString(), 'roles' => $roleArray['roles'], ]; @@ -203,8 +203,8 @@ public function apiLoginAction(Request $request, UserPasswordHasherInterface $ha return new Response(json_encode($response), 401, ['Content-type' => 'application/json']); } - if ($user->getOrganisation() !== null) { - $organizations[] = $user->getOrganisation(); + if ($user->getOrganization() !== null) { + $organizations[] = $user->getOrganization(); } foreach ($user->getApplications() as $application) { if ($application->getOrganization() !== null) { @@ -213,7 +213,7 @@ public function apiLoginAction(Request $request, UserPasswordHasherInterface $ha } // If user has no organization, we default activeOrganization to an organization of a userGroup this user has and else the application organization; - $this->session->set('activeOrganization', $user->getOrganisation()->getId()->toString()); + $this->session->set('activeOrganization', $user->getOrganization()->getId()->toString()); $token = $authenticationService->createJwtToken($user->getApplications()[0]->getPrivateKey(), $authenticationService->serializeUser($user, $this->session)); @@ -255,7 +255,7 @@ private function getActiveOrganization(array $user, array $organizations): ?stri } /** - * Get all the child organisations for an organisation. + * Get all the child organizations for an organization. * * @param array $organizations * @param string $organization @@ -284,7 +284,7 @@ private function getSubOrganizations(array $organizations, string $organization, } /** - * Get al the parent organizations for an organisation. + * Get al the parent organizations for an organization. * * @param array $organizations * @param string $organization diff --git a/api/src/Entity/Action.php b/api/src/Entity/Action.php index 093633d79..17c9e6cb4 100644 --- a/api/src/Entity/Action.php +++ b/api/src/Entity/Action.php @@ -171,6 +171,16 @@ class Action */ private ?array $configuration = []; + /** + * @var string|null The userId of a user. This user will be used to run this Action for, if there is no logged-in user. + * This helps when, for example: setting the organization of newly created ObjectEntities while running this Action. + * + * @Groups({"read","write"}) + * + * @ORM\Column(type="string", length=255, nullable=true) + */ + private ?string $userId = null; + /** * @Groups({"read", "write"}) * @@ -481,6 +491,18 @@ public function setConfiguration(?array $configuration): self return $this; } + public function getUserId(): ?string + { + return $this->userId; + } + + public function setUserId(?string $userId): self + { + $this->userId = $userId; + + return $this; + } + public function getIsLockable(): ?bool { return $this->isLockable; diff --git a/api/src/Entity/Cronjob.php b/api/src/Entity/Cronjob.php index 5d279456f..862ba6b03 100644 --- a/api/src/Entity/Cronjob.php +++ b/api/src/Entity/Cronjob.php @@ -123,6 +123,16 @@ class Cronjob */ private string $crontab = '*/5 * * * *'; + /** + * @var string|null The userId of a user. This user will be used to run this Cronjob for, if there is no logged-in user. + * This helps when, for example: setting the organization of newly created ObjectEntities while running this Cronjob. + * + * @Groups({"read","write"}) + * + * @ORM\Column(type="string", length=255, nullable=true) + */ + private ?string $userId = null; + /** * @var array The actions that put on the stack by the crontab. * @@ -320,6 +330,18 @@ public function setCrontab(string $crontab): self return $this; } + public function getUserId(): ?string + { + return $this->userId; + } + + public function setUserId(?string $userId): self + { + $this->userId = $userId; + + return $this; + } + public function getThrows(): ?array { return $this->throws; diff --git a/api/src/Entity/Entity.php b/api/src/Entity/Entity.php index 257d3be21..a3091daed 100644 --- a/api/src/Entity/Entity.php +++ b/api/src/Entity/Entity.php @@ -182,7 +182,7 @@ class Entity private $extend = false; /** - * Whether objects created from this entity should be available to child organisations. + * Whether objects created from this entity should be available to child organizations. * * @Groups({"read","write"}) * diff --git a/api/src/Entity/Organization.php b/api/src/Entity/Organization.php index 00983dd53..cd231688b 100644 --- a/api/src/Entity/Organization.php +++ b/api/src/Entity/Organization.php @@ -21,21 +21,20 @@ use Symfony\Component\Validator\Constraints as Assert; /** - * This entity holds the information about an Organisation. + * This entity holds the information about an Organization. * * @ApiResource( - * normalizationContext={"groups"={"read"}, "enable_max_depth"=true}, - * denormalizationContext={"groups"={"write"}, "enable_max_depth"=true}, + * normalizationContext={"groups"={"read"}, "enable_max_depth"=true}, + * denormalizationContext={"groups"={"write"}, "enable_max_depth"=true}, * itemOperations={ - * "get"={"path"="/admin/organisations/{id}"}, - * "put"={"path"="/admin/organisations/{id}"}, - * "delete"={"path"="/admin/organisations/{id}"} + * "get"={"path"="/admin/organizations/{id}"}, + * "put"={"path"="/admin/organizations/{id}"}, + * "delete"={"path"="/admin/organizations/{id}"} * }, * collectionOperations={ - * "get"={"path"="/admin/organisations"}, - * "post"={"path"="/admin/organisations"} + * "get"={"path"="/admin/organizations"}, + * "post"={"path"="/admin/organizations"} * }) - * ) * * @ORM\HasLifecycleCallbacks * @@ -117,7 +116,7 @@ class Organization * * @MaxDepth(1) * - * @ORM\OneToMany(targetEntity=User::class, mappedBy="organisation", orphanRemoval=true) + * @ORM\OneToMany(targetEntity=User::class, mappedBy="organization", orphanRemoval=true) */ private $users; @@ -321,7 +320,7 @@ public function addUser(User $user): self { if (!$this->users->contains($user)) { $this->users[] = $user; - $user->setOrganisation($this); + $user->setOrganization($this); } return $this; @@ -331,8 +330,8 @@ public function removeUser(User $user): self { if ($this->users->removeElement($user)) { // set the owning side to null (unless already changed) - if ($user->getOrganisation() === $this) { - $user->setOrganisation(null); + if ($user->getOrganization() === $this) { + $user->setOrganization(null); } } diff --git a/api/src/Entity/User.php b/api/src/Entity/User.php index 107755d5f..3cde47dbc 100644 --- a/api/src/Entity/User.php +++ b/api/src/Entity/User.php @@ -137,7 +137,7 @@ class User implements PasswordAuthenticatedUserInterface * * @ORM\JoinColumn(nullable=false) */ - private ?Organization $organisation = null; + private ?Organization $organization = null; /** * @Groups({"read", "write"}) @@ -239,7 +239,7 @@ public function fromSchema(array $schema): self $this->setPassword('!ChangeMe!'); array_key_exists('locale', $schema) ? $this->setLocale($schema['locale']) : ''; array_key_exists('person', $schema) ? $this->setPerson($schema['person']) : ''; - array_key_exists('organization', $schema) ? $this->setOrganisation($schema['organization']) : ''; + array_key_exists('organization', $schema) ? $this->setOrganization($schema['organization']) : ''; array_key_exists('applications', $schema) ? $this->setApplications($schema['applications']) : ''; // Todo: temporary? make sure we never allow admin scopes to be added or removed with fromSchema @@ -297,7 +297,7 @@ public function toSchema(): array 'locale' => $this->getLocale(), 'person' => $this->getPerson(), 'scopes' => $this->getScopes(), - 'organization' => $this->getOrganisation() ? $this->getOrganisation()->toSchema() : null, + 'organization' => $this->getOrganization() ? $this->getOrganization()->toSchema() : null, 'applications' => $applications, 'securityGroups' => $securityGroups, ]; @@ -392,14 +392,14 @@ public function setEmail(string $email): self return $this; } - public function getOrganisation(): ?Organization + public function getOrganization(): ?Organization { - return $this->organisation; + return $this->organization; } - public function setOrganisation(?Organization $organization): self + public function setOrganization(?Organization $organization): self { - $this->organisation = $organization; + $this->organization = $organization; return $this; } diff --git a/api/src/Security/ApiKeyAuthenticator.php b/api/src/Security/ApiKeyAuthenticator.php index 24917c76b..d9c19f97e 100644 --- a/api/src/Security/ApiKeyAuthenticator.php +++ b/api/src/Security/ApiKeyAuthenticator.php @@ -56,7 +56,7 @@ public function supports(Request $request): ?bool } /** - * Get all the child organisations for an organisation. + * Get all the child organizations for an organization. * * @param array $organizations * @param string $organization @@ -85,7 +85,7 @@ private function getSubOrganizations(array $organizations, string $organization, } /** - * Get al the parent organizations for an organisation. + * Get al the parent organizations for an organization. * * @param array $organizations * @param string $organization @@ -176,20 +176,20 @@ public function authenticate(Request $request): PassportInterface } $organizations = []; - if ($user->getOrganisation()) { - $organizations[] = $user->getOrganisation(); + if ($user->getOrganization()) { + $organizations[] = $user->getOrganization(); } $organizations[] = 'localhostOrganization'; $this->session->set('organizations', $organizations); // If user has no organization, we default activeOrganization to an organization of a userGroup this user has and else the application organization; - $this->session->set('activeOrganization', $user->getOrganisation()); + $this->session->set('activeOrganization', $user->getOrganization()); $userArray = [ 'id' => $user->getId()->toString(), 'email' => $user->getEmail(), 'locale' => $user->getLocale(), - 'organization' => $user->getOrganisation()->getId()->toString(), + 'organization' => $user->getOrganization()->getId()->toString(), 'roles' => $roleArray['roles'], ]; diff --git a/api/src/Service/ObjectEntityService.php b/api/src/Service/ObjectEntityService.php index ede6e2b78..4b4be82b8 100644 --- a/api/src/Service/ObjectEntityService.php +++ b/api/src/Service/ObjectEntityService.php @@ -45,7 +45,7 @@ * @license EUPL * * @category Service - * @deprecated TODO: This service still contains some logic used by CoreBundle->ObjectSyncSubscriber + * @deprecated TODO: This service still contains some logic used by CoreBundle->ObjectEntitySubscriber (old CoreBundle->ObjectSyncSubscriber) * todo: this service is also used by the UserService for showing data when calling the /me endpoint. * todo: and this service still contains some old logic for Files and Promises we might still need at some point? */ diff --git a/api/src/Service/SynchronizationService.php b/api/src/Service/SynchronizationService.php index 6200c7305..11dc2fb73 100644 --- a/api/src/Service/SynchronizationService.php +++ b/api/src/Service/SynchronizationService.php @@ -65,16 +65,6 @@ class SynchronizationService private EventDispatcherInterface $eventDispatcher; private Logger $logger; - /** - * Default user for synchronizations. - * Note that owner => reference is replaces with an uuid of that User object. - * - * @var array|string[] - */ - private array $synchronizationDefault = [ - 'owner' => 'https://docs.commongateway.nl/user/default.user.json', - ]; - private bool $asyncError = false; /** @@ -788,21 +778,6 @@ public function findSyncByObject(ObjectEntity $objectEntity, Source $source, Ent return $synchronization; } - /** - * Sets default owner on objectEntity. - * - * @return ObjectEntity $object - */ - public function setDefaultOwner(ObjectEntity $object): ObjectEntity - { - $defaultUser = $this->entityManager->getRepository('App:User')->findOneBy(['reference' => $this->synchronizationDefault['owner']]); - if ($defaultUser instanceof User === false) { - return $object; - } - - return $object->setOwner($defaultUser->getId()->toString()); - }//end setDefaultOwner() - /** * Adds a new ObjectEntity to a synchronization object. * @@ -814,7 +789,6 @@ public function checkObjectEntity(Synchronization $synchronization): string { if (!$synchronization->getObject()) { $object = new ObjectEntity(); - $object = $this->setDefaultOwner($object); $synchronization->getSourceId() && $object->setExternalId($synchronization->getSourceId()); $object->setEntity($synchronization->getEntity()); $object = $this->setApplicationAndOrganization($object); @@ -968,7 +942,6 @@ public function synchronize(Synchronization $synchronization, array $sourceObjec isset($this->io) && $this->io->text('creating new objectEntity'); $this->logger->info('creating new objectEntity'); $object = new ObjectEntity($synchronization->getEntity()); - $object = $this->setDefaultOwner($object); $object->addSynchronization($synchronization); $this->entityManager->persist($object); $this->entityManager->persist($synchronization); @@ -1088,7 +1061,8 @@ public function setApplicationAndOrganization(ObjectEntity $objectEntity): Objec $application = $this->entityManager->getRepository('App:Application')->findOneBy(['name' => 'main application']); if ($application instanceof Application) { $objectEntity->setApplication($application); - $objectEntity->setOrganization($application->getOrganization()); + // todo: setting organization is done by CoreBundle->ObjectEntitySubscriber & ObjectEntityService, maybe move setting application to there as well? +// $objectEntity->setOrganization($application->getOrganization()); } elseif ( ($applications = $this->entityManager->getRepository('App:Application')->findAll() && !empty($applications) @@ -1096,7 +1070,8 @@ public function setApplicationAndOrganization(ObjectEntity $objectEntity): Objec && $application instanceof Application ) { $objectEntity->setApplication($application); - $objectEntity->setOrganization($application->getOrganization()); + // todo: setting organization is done by CoreBundle->ObjectEntitySubscriber & ObjectEntityService, maybe move setting application to there as well? +// $objectEntity->setOrganization($application->getOrganization()); } return $objectEntity;