@@ -130,17 +130,21 @@ public function newAction(): void
130130 public function createAction (string $ username , array $ password , User $ user , ?\DateTime $ expirationDate , array $ roleIdentifiers = []): void
131131 {
132132 // make sure self::$roleIdentifier is always added
133- $ roleIdentifiers = array_unique (array_merge ($ roleIdentifiers , [self ::$ roleIdentifier ]));
134- $ user = $ this ->userService ->addUser ($ username , $ password [0 ], $ user , $ roleIdentifiers , self ::$ authenticationProviderName );
133+ $ roleIdentifiersToSet = array_unique (array_merge ($ roleIdentifiers , [self ::$ roleIdentifier ]));
134+ if ($ this ->onlyFrontendRoles ($ roleIdentifiersToSet )) {
135+ $ user = $ this ->userService ->addUser ($ username , $ password [0 ], $ user , $ roleIdentifiersToSet , self ::$ authenticationProviderName );
135136
136- if ($ expirationDate !== null ) {
137- /** @var Account $account */
138- $ account = $ user ->getAccounts ()->first ();
139- $ expirationDate ->setTime (0 , 0 , 0 );
140- $ account ->setExpirationDate ($ expirationDate );
141- }
137+ if ($ expirationDate !== null ) {
138+ /** @var Account $account */
139+ $ account = $ user ->getAccounts ()->first ();
140+ $ expirationDate ->setTime (0 , 0 );
141+ $ account ->setExpirationDate ($ expirationDate );
142+ }
142143
143- $ this ->addFlashMessage ('The user "%s" has been created. ' , 'User created ' , Message::SEVERITY_OK , [htmlspecialchars ($ username )], 1416225561 );
144+ $ this ->addFlashMessage ('The user "%s" has been created. ' , 'User created ' , Message::SEVERITY_OK , [htmlspecialchars ($ username )], 1416225561 );
145+ } else {
146+ $ this ->throwStatus (403 , 'Not allowed to assign the given roles ' );
147+ }
144148 $ this ->redirect ('index ' );
145149 }
146150
@@ -243,17 +247,25 @@ public function updateAccountAction(Account $account, array $roleIdentifiers = [
243247 {
244248 if ($ this ->checkAccount ($ account )) {
245249 // make sure self::$roleIdentifier is always added
246- $ roleIdentifiers = array_unique (array_merge ($ roleIdentifiers , [self ::$ roleIdentifier ]));
247- $ this ->userService ->setRolesForAccount ($ account , $ roleIdentifiers );
248- $ user = $ this ->userService ->getUser ($ account ->getAccountIdentifier (), $ account ->getAuthenticationProviderName ());
249- $ password = array_shift ($ password );
250- if (trim ((string )$ password ) !== '' ) {
251- $ this ->userService ->setUserPassword ($ user , $ password );
252- }
253- $ this ->accountRepository ->update ($ account );
250+ $ roleIdentifiersToSet = array_unique (array_merge ($ roleIdentifiers , [self ::$ roleIdentifier ]));
251+
252+ if ($ this ->onlyFrontendRoles ($ roleIdentifiersToSet )) {
253+ // add any non-FE roles from the current roles to keep them unchanged
254+ $ roleIdentifiersToSet = $ this ->addExistingNonFrontendUserRoles ($ roleIdentifiersToSet , $ account );
254255
255- $ this ->addFlashMessage ('The account has been updated. ' , 'Account updated ' , Message::SEVERITY_OK );
256- $ this ->redirect ('edit ' , null , null , ['user ' => $ user ]);
256+ $ this ->userService ->setRolesForAccount ($ account , $ roleIdentifiersToSet );
257+ $ user = $ this ->userService ->getUser ($ account ->getAccountIdentifier (), $ account ->getAuthenticationProviderName ());
258+ $ password = array_shift ($ password );
259+ if (trim ((string )$ password ) !== '' ) {
260+ $ this ->userService ->setUserPassword ($ user , $ password );
261+ }
262+ $ this ->accountRepository ->update ($ account );
263+
264+ $ this ->addFlashMessage ('The account has been updated. ' , 'Account updated ' );
265+ $ this ->redirect ('edit ' , null , null , ['user ' => $ user ]);
266+ } else {
267+ $ this ->throwStatus (403 , 'Not allowed to assign the given roles ' );
268+ }
257269 } else {
258270 $ this ->throwStatus (403 , 'Not allowed to update that account ' );
259271 }
@@ -321,4 +333,64 @@ protected function getFrontendUserRoles(): array
321333 return $ role ->getIdentifier () === self ::$ roleIdentifier || array_key_exists (self ::$ roleIdentifier , $ role ->getAllParentRoles ());
322334 });
323335 }
336+
337+ /**
338+ * Returns an array with all roles of a user's accounts, including parent roles, the "Everybody" role and the
339+ * "AuthenticatedUser" role, assuming that the user is logged in.
340+ *
341+ * @param Account $account
342+ * @return Role[] indexed by role identifier
343+ */
344+ private function getAllRolesForAccount (Account $ account ): array
345+ {
346+ $ roles = [];
347+ $ accountRoles = $ account ->getRoles ();
348+ foreach ($ accountRoles as $ currentRole ) {
349+ if (!in_array ($ currentRole , $ roles , true )) {
350+ $ roles [$ currentRole ->getIdentifier ()] = $ currentRole ;
351+ }
352+ foreach ($ currentRole ->getAllParentRoles () as $ currentParentRole ) {
353+ if (!in_array ($ currentParentRole , $ roles , true )) {
354+ $ roles [$ currentParentRole ->getIdentifier ()] = $ currentParentRole ;
355+ }
356+ }
357+ }
358+
359+ return $ roles ;
360+ }
361+
362+ /**
363+ * Returns whether it is allowed to add/remove the changed roles.
364+ *
365+ * - Roles not being "FE user roles" can never be set
366+ *
367+ * @param array $roleIdentifiersToSet
368+ * @return bool
369+ */
370+ private function onlyFrontendRoles (array $ roleIdentifiersToSet ): bool
371+ {
372+ $ frontendUserRoleIdentifiers = array_keys ($ this ->getFrontendUserRoles ());
373+
374+ return array_diff ($ roleIdentifiersToSet , $ frontendUserRoleIdentifiers ) === [];
375+ }
376+
377+ /**
378+ * Add any non-FE roles from the current $account roles to $roleIdentifiersToSet
379+ *
380+ * @param array $roleIdentifiersToSet
381+ * @param Account $account
382+ * @return array
383+ */
384+ protected function addExistingNonFrontendUserRoles (array $ roleIdentifiersToSet , Account $ account ): array
385+ {
386+ return array_unique (array_merge (
387+ $ roleIdentifiersToSet ,
388+ array_keys (array_filter (
389+ $ this ->getAllRolesForAccount ($ account ),
390+ static function (Role $ role ) {
391+ return !$ role ->isAbstract () && !($ role ->getIdentifier () === self ::$ roleIdentifier || array_key_exists (self ::$ roleIdentifier , $ role ->getAllParentRoles ()));
392+ }
393+ ))
394+ ));
395+ }
324396}
0 commit comments