diff --git a/config/packages/security.yaml b/config/packages/security.yaml index d91be49c..b44dd984 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -2,8 +2,8 @@ security: password_hashers: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' providers: - admin_user_provider: - id: App\Security\AdminUserProvider + user_provider: + id: App\Security\UserProvider firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ @@ -17,7 +17,7 @@ security: lazy: true custom_authenticators: - App\Security\LoginFormAuthenticator - provider: admin_user_provider + provider: user_provider login_throttling: max_attempts: 5 logout: @@ -38,4 +38,4 @@ security: - { path: ^/logout$, roles: PUBLIC_ACCESS } - { path: ^/api/v1/health$, roles: PUBLIC_ACCESS } - { path: ^/api, roles: IS_AUTHENTICATED } - - { path: ^/, roles: ROLE_ADMIN, allow_if: "'%env(default:default_admin_auth_bypass:ADMIN_AUTH_BYPASS)%' === 'true'" } + - { path: ^/, roles: [ROLE_ADMIN, ROLE_USER], allow_if: "'%env(default:default_admin_auth_bypass:ADMIN_AUTH_BYPASS)%' === 'true'" } diff --git a/config/services.yaml b/config/services.yaml index 25662b12..254e837f 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -70,6 +70,11 @@ services: $adminLogin: "%env(ADMIN_LOGIN)%" $adminPassword: "%env(ADMIN_PASSWORD)%" + App\Security\UserProvider: + arguments: + $adminLogin: "%env(ADMIN_LOGIN)%" + $adminPassword: "%env(ADMIN_PASSWORD)%" + App\Logging\Monolog\PasswordFilterProcessor: tags: - { name: monolog.processor } diff --git a/src/Controller/Admin/AddressBookController.php b/src/Controller/User/AddressBookController.php similarity index 96% rename from src/Controller/Admin/AddressBookController.php rename to src/Controller/User/AddressBookController.php index 0e743d99..ab4ba70b 100644 --- a/src/Controller/Admin/AddressBookController.php +++ b/src/Controller/User/AddressBookController.php @@ -1,6 +1,6 @@ getPrincipalUri(); @@ -40,6 +42,7 @@ public function addressBooks(ManagerRegistry $doctrine, #[MapEntity(id: 'userId' #[Route('/{userId}/new', name: 'create')] #[Route('/{userId}/edit/{id}', name: 'edit', requirements: ['id' => "\d+"])] + #[IsGranted('access', 'userId')] public function addressbookCreate(ManagerRegistry $doctrine, Request $request, #[MapEntity(id: 'userId')] User $user, int $userId, ?int $id, TranslatorInterface $trans, BirthdayService $birthdayService): Response { $username = $user->getUsername(); @@ -102,6 +105,7 @@ public function addressbookCreate(ManagerRegistry $doctrine, Request $request, # } #[Route('/{userId}/delete/{id}', name: 'delete', requirements: ['id' => "\d+"], methods: ['POST'])] + #[IsGranted('access', 'userId')] public function addressbookDelete(ManagerRegistry $doctrine, Request $request, #[MapEntity(id: 'userId')] User $user, int $userId, string $id, TranslatorInterface $trans, BirthdayService $birthdayService): Response { if (!$this->isCsrfTokenValid('admin_action', $request->getPayload()->getString('_token'))) { diff --git a/src/Controller/Admin/CalendarController.php b/src/Controller/User/CalendarController.php similarity index 92% rename from src/Controller/Admin/CalendarController.php rename to src/Controller/User/CalendarController.php index 71716196..73e2b858 100644 --- a/src/Controller/Admin/CalendarController.php +++ b/src/Controller/User/CalendarController.php @@ -1,6 +1,6 @@ getUsername(); @@ -74,6 +76,7 @@ public function calendars(ManagerRegistry $doctrine, UrlGeneratorInterface $rout #[Route('/{userId}/new', name: 'create')] #[Route('/{userId}/edit/{id}', name: 'edit', requirements: ['id' => "\d+"])] + #[IsGranted('access', 'userId')] public function calendarEdit(ManagerRegistry $doctrine, Request $request, #[MapEntity(id: 'userId')] User $user, int $userId, ?int $id, TranslatorInterface $trans): Response { $principalUri = $user->getPrincipalUri(); @@ -163,6 +166,7 @@ public function calendarEdit(ManagerRegistry $doctrine, Request $request, #[MapE } #[Route('/{userId}/shares/{calendarid}', name: 'shares', requirements: ['calendarid' => "\d+"])] + #[IsGranted('access', 'userId')] public function calendarShares(ManagerRegistry $doctrine, #[MapEntity(id: 'userId')] User $user, int $userId, string $calendarid, TranslatorInterface $trans): Response { $principalUri = $user->getPrincipalUri(); @@ -189,6 +193,7 @@ public function calendarShares(ManagerRegistry $doctrine, #[MapEntity(id: 'userI } #[Route('/{userId}/share/{instanceid}', name: 'share_add', requirements: ['instanceid' => "\d+"], methods: ['POST'])] + #[IsGranted('access', 'userId')] public function calendarShareAdd(ManagerRegistry $doctrine, Request $request, #[MapEntity(id: 'userId')] User $user, int $userId, string $instanceid, TranslatorInterface $trans): Response { if (!$this->isCsrfTokenValid('admin_action', $request->getPayload()->getString('_token'))) { @@ -203,11 +208,24 @@ public function calendarShareAdd(ManagerRegistry $doctrine, Request $request, #[ throw $this->createNotFoundException('Calendar not found'); } - if (!is_numeric($request->request->get('principalId'))) { - throw new BadRequestHttpException(); + if ($this->isGranted('ROLE_ADMIN')) { + // in this case, this is the id of the principal to add + if (!is_numeric($request->request->get('principalId'))) { + throw new BadRequestHttpException(); + } + + $newShareeToAdd = $doctrine->getRepository(Principal::class)->findOneById($request->request->get('principalId')); + } else { + // in this case, this is the username of the user to add, we need to convert it to a principal + $userToAdd = $doctrine->getRepository(User::class)->findOneByUsername($request->request->get('principalId')); + if (!$userToAdd) { + $this->addFlash('warning', 'User does not exist'); + + return $this->redirectToRoute('calendar_index', ['userId' => $userId]); + } + $newShareeToAdd = $doctrine->getRepository(Principal::class)->findOneByUri($userToAdd->getPrincipalUri()); } - $newShareeToAdd = $doctrine->getRepository(Principal::class)->findOneById($request->request->get('principalId')); if (!$newShareeToAdd) { throw $this->createNotFoundException('Member not found'); } @@ -246,6 +264,7 @@ public function calendarShareAdd(ManagerRegistry $doctrine, Request $request, #[ } #[Route('/{userId}/delete/{id}', name: 'delete', requirements: ['id' => "\d+"], methods: ['POST'])] + #[IsGranted('access', 'userId')] public function calendarDelete(ManagerRegistry $doctrine, Request $request, #[MapEntity(id: 'userId')] User $user, int $userId, string $id, TranslatorInterface $trans): Response { if (!$this->isCsrfTokenValid('admin_action', $request->getPayload()->getString('_token'))) { @@ -301,6 +320,7 @@ public function calendarDelete(ManagerRegistry $doctrine, Request $request, #[Ma } #[Route('/{userId}/revoke/{id}', name: 'revoke', requirements: ['id' => "\d+"], methods: ['POST'])] + #[IsGranted('access', 'userId')] public function calendarRevoke(ManagerRegistry $doctrine, Request $request, #[MapEntity(id: 'userId')] User $user, int $userId, string $id, TranslatorInterface $trans): Response { if (!$this->isCsrfTokenValid('admin_action', $request->getPayload()->getString('_token'))) { diff --git a/src/Controller/Admin/DashboardController.php b/src/Controller/User/DashboardController.php similarity index 93% rename from src/Controller/Admin/DashboardController.php rename to src/Controller/User/DashboardController.php index 57aa1549..f0fc0fb6 100644 --- a/src/Controller/Admin/DashboardController.php +++ b/src/Controller/User/DashboardController.php @@ -1,6 +1,6 @@ getRepository(User::class)->count([]); diff --git a/src/Controller/Admin/UserController.php b/src/Controller/User/UserController.php similarity index 87% rename from src/Controller/Admin/UserController.php rename to src/Controller/User/UserController.php index 4943bfca..7dae605c 100644 --- a/src/Controller/Admin/UserController.php +++ b/src/Controller/User/UserController.php @@ -1,6 +1,6 @@ getRepository(Principal::class)->findAllMainPrincipalsWithUserIds(); @@ -33,8 +35,24 @@ public function users(ManagerRegistry $doctrine): Response ]); } + #[Route('/{userId}', name: 'user', requirements: ['userId' => "\d+"])] + #[IsGranted('access', 'userId')] + public function user(ManagerRegistry $doctrine, #[MapEntity(id: 'userId')] User $user, int $userId): Response + { + $results = $doctrine->getRepository(Principal::class)->findOneMainPrincipalsWithUserId($userId); + + if (!$results) { + throw BadRequestHttpException('User not found'); + } + + return $this->render('users/index.html.twig', [ + 'results' => $results, + ]); + } + #[Route('/new', name: 'create')] #[Route('/edit/{userId}', name: 'edit')] + #[IsGranted('access', 'userId')] public function userCreate(ManagerRegistry $doctrine, Utils $utils, Request $request, ?int $userId, TranslatorInterface $trans): Response { if ($userId) { @@ -115,7 +133,11 @@ public function userCreate(ManagerRegistry $doctrine, Utils $utils, Request $req $this->addFlash('success', $trans->trans('user.saved')); - return $this->redirectToRoute('user_index'); + if ($this->isGranted('ROLE_ADMIN')) { + return $this->redirectToRoute('user_index'); + } + + return $this->redirectToRoute('user_user', ['userId' => $userId]); } return $this->render('users/edit.html.twig', [ @@ -126,6 +148,7 @@ public function userCreate(ManagerRegistry $doctrine, Utils $utils, Request $req } #[Route('/delete/{userId}', name: 'delete', methods: ['POST'])] + #[IsGranted('access', 'userId')] public function userDelete(ManagerRegistry $doctrine, Request $request, #[MapEntity(id: 'userId')] User $user, int $userId, TranslatorInterface $trans): Response { if (!$this->isCsrfTokenValid('admin_action', $request->getPayload()->getString('_token'))) { @@ -195,10 +218,15 @@ public function userDelete(ManagerRegistry $doctrine, Request $request, #[MapEnt $entityManager->flush(); $this->addFlash('success', $trans->trans('user.deleted')); - return $this->redirectToRoute('user_index'); + if ($this->isGranted('ROLE_ADMIN')) { + return $this->redirectToRoute('user_index'); + } + + return $this->redirectToRoute('app_logout'); } #[Route('/delegates/{userId}', name: 'delegates')] + #[IsGranted('access', 'userId')] public function userDelegates(ManagerRegistry $doctrine, #[MapEntity(id: 'userId')] User $user, int $userId): Response { $principalUri = $user->getPrincipalUri(); @@ -222,6 +250,7 @@ public function userDelegates(ManagerRegistry $doctrine, #[MapEntity(id: 'userId } #[Route('/delegation/{userId}/{toggle}', name: 'delegation_toggle', requirements: ['toggle' => '(on|off)'], methods: ['POST'])] + #[IsGranted('access', 'userId')] public function userToggleDelegation(ManagerRegistry $doctrine, Request $request, #[MapEntity(id: 'userId')] User $user, int $userId, string $toggle): Response { if (!$this->isCsrfTokenValid('admin_action', $request->getPayload()->getString('_token'))) { @@ -265,19 +294,32 @@ public function userToggleDelegation(ManagerRegistry $doctrine, Request $request } #[Route('/delegates/{userId}/add', name: 'delegate_add', methods: ['POST'])] + #[IsGranted('access', 'userId')] public function userDelegateAdd(ManagerRegistry $doctrine, Request $request, #[MapEntity(id: 'userId')] User $user, int $userId): Response { if (!$this->isCsrfTokenValid('admin_action', $request->getPayload()->getString('_token'))) { throw $this->createAccessDeniedException('Invalid CSRF token.'); } - if (!is_numeric($request->request->get('principalId'))) { - throw new BadRequestHttpException(); - } - $principalUri = $user->getPrincipalUri(); - $newMemberToAdd = $doctrine->getRepository(Principal::class)->findOneById($request->request->get('principalId')); + if ($this->isGranted('ROLE_ADMIN')) { + // in this case, this is the id of the principal to add + if (!is_numeric($request->request->get('principalId'))) { + throw new BadRequestHttpException(); + } + + $newMemberToAdd = $doctrine->getRepository(Principal::class)->findOneById($request->request->get('principalId')); + } else { + // in this case, this is the username of the member to add, we need to convert it to a principal + $memberToAdd = $doctrine->getRepository(User::class)->findOneByUsername($request->request->get('principalId')); + if (!$memberToAdd) { + $this->addFlash('warning', 'User does not exist'); + + return $this->redirectToRoute('user_delegates', ['userId' => $userId]); + } + $newMemberToAdd = $doctrine->getRepository(Principal::class)->findOneByUri($memberToAdd->getPrincipalUri()); + } if (!$newMemberToAdd) { throw $this->createNotFoundException('Member not found'); @@ -309,6 +351,7 @@ public function userDelegateAdd(ManagerRegistry $doctrine, Request $request, #[M } #[Route('/delegates/{userId}/remove/{principalProxyId}/{delegateId}', name: 'delegate_remove', requirements: ['principalProxyId' => "\d+", 'delegateId' => "\d+"], methods: ['POST'])] + #[IsGranted('access', 'userId')] public function userDelegateRemove(ManagerRegistry $doctrine, Request $request, #[MapEntity(id: 'userId')] User $user, int $userId, int $principalProxyId, int $delegateId): Response { if (!$this->isCsrfTokenValid('admin_action', $request->getPayload()->getString('_token'))) { diff --git a/src/Repository/PrincipalRepository.php b/src/Repository/PrincipalRepository.php index 4fce0d08..84ecd1d8 100644 --- a/src/Repository/PrincipalRepository.php +++ b/src/Repository/PrincipalRepository.php @@ -52,4 +52,26 @@ public function findAllMainPrincipalsWithUserIds(): array ->getQuery() ->getResult(); } + + /** + * @return array + */ + public function findOneMainPrincipalsWithUserId(int $userId): array + { + return $this->createQueryBuilder('p') + ->addSelect('u.id AS userId') + ->leftJoin( + \App\Entity\User::class, + 'u', + \Doctrine\ORM\Query\Expr\Join::WITH, + 'CONCAT(:prefix, u.username) = p.uri' + ) + ->andWhere('p.isMain = :isMain') + ->andWhere('u.id = :userid') + ->setParameter('isMain', true) + ->setParameter('userid', $userId) + ->setParameter('prefix', Principal::PREFIX) + ->getQuery() + ->getResult(); + } } diff --git a/src/Security/LoginFormAuthenticator.php b/src/Security/LoginFormAuthenticator.php index 3704cb60..711bdcdb 100644 --- a/src/Security/LoginFormAuthenticator.php +++ b/src/Security/LoginFormAuthenticator.php @@ -2,6 +2,8 @@ namespace App\Security; +use App\Entity\User; +use Doctrine\Persistence\ManagerRegistry; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -20,18 +22,20 @@ class LoginFormAuthenticator extends AbstractLoginFormAuthenticator { use TargetPathTrait; + private $doctrine; private $urlGenerator; private $csrfTokenManager; private $adminLogin; private $adminPassword; - public function __construct(UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, string $adminLogin, string $adminPassword) + public function __construct(ManagerRegistry $doctrine, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, string $adminLogin, string $adminPassword) { + $this->doctrine = $doctrine; $this->urlGenerator = $urlGenerator; $this->csrfTokenManager = $csrfTokenManager; $this->adminLogin = $adminLogin; - $this->adminPassword = $adminPassword; + $this->adminPassword = password_hash($adminPassword, PASSWORD_DEFAULT); } protected function getLoginUrl(Request $request): string @@ -55,17 +59,26 @@ public function authenticate(Request $request): Passport $username = $request->request->getString('_username'); $password = $request->request->getString('_password'); + $user = $this->doctrine->getRepository(User::class)->findOneByUsername($username); + if ($user) { + $username_to_test = $user->getUsername(); + $password_to_test = $user->getPassword(); + } else { + $username_to_test = $this->adminLogin; + $password_to_test = $this->adminPassword; + } + $request->getSession()->set(SecurityRequestAttributes::LAST_USERNAME, $username); return new Passport( new UserBadge($username), new CustomCredentials( - function (string $presentedPassword) use ($username): bool { + function (string $presentedPassword) use ($username, $username_to_test, $password_to_test): bool { // Both halves are compared, and both in constant time, so the response says // nothing about which one was wrong — an unknown name and a wrong password // fail identically with "Invalid credentials.". - $loginMatches = hash_equals($this->adminLogin, $username); - $passwordMatches = hash_equals($this->adminPassword, $presentedPassword); + $loginMatches = hash_equals($username, $username_to_test); + $passwordMatches = password_verify($presentedPassword, $password_to_test); return $loginMatches && $passwordMatches; }, @@ -77,10 +90,13 @@ function (string $presentedPassword) use ($username): bool { public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey): ?Response { - if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) { - return new RedirectResponse($targetPath); + if (in_array('ROLE_ADMIN', $token->getRoleNames(), true)) { + return new RedirectResponse($this->urlGenerator->generate('dashboard')); + } elseif (in_array('ROLE_USER', $token->getRoleNames(), true)) { + return new RedirectResponse($this->urlGenerator->generate('user_user', ['userId' => $token->getUser()->getUserId()])); } - return new RedirectResponse($this->urlGenerator->generate('dashboard')); + // XXX: this should not be reachable + return new RedirectResponse($this->urlGenerator->generate('/')); } } diff --git a/src/Security/NormalUser.php b/src/Security/NormalUser.php new file mode 100644 index 00000000..60f172fa --- /dev/null +++ b/src/Security/NormalUser.php @@ -0,0 +1,83 @@ +username = $username; + $this->password = $password; + $this->userId = $userId; + } + + /** + * @return (Role|string)[] The user roles + */ + public function getRoles(): array + { + return ['ROLE_USER']; + } + + /** + * Returns the password used to authenticate the user. + */ + public function getPassword(): string + { + return $this->password; + } + + /** + * Returns the salt that was originally used to encode the password. + * + * This can return null if the password was not encoded using a salt. + * + * @return string|null The salt + */ + public function getSalt() + { + return null; + } + + /** + * Returns the username used to authenticate the user. + * + * @return string The username + */ + public function getUsername() + { + return $this->username; + } + + /** + * Returns the id used of the user. + * + * @return string The id + */ + public function getUserId() + { + return $this->userId; + } + + public function getUserIdentifier(): string + { + return $this->username; + } + + /** + * Removes sensitive data from the user. + * + * This is important if, at any given point, sensitive information like + * the plain-text password is stored on this object. + */ + public function eraseCredentials(): void + { + } +} diff --git a/src/Security/AdminUserProvider.php b/src/Security/UserProvider.php similarity index 59% rename from src/Security/AdminUserProvider.php rename to src/Security/UserProvider.php index deb34df7..580217cd 100644 --- a/src/Security/AdminUserProvider.php +++ b/src/Security/UserProvider.php @@ -2,13 +2,27 @@ namespace App\Security; +use App\Entity\User; +use Doctrine\Persistence\ManagerRegistry; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; -class AdminUserProvider implements UserProviderInterface +class UserProvider implements UserProviderInterface { + private $doctrine; + + private $adminLogin; + private $adminPassword; + + public function __construct(ManagerRegistry $doctrine, string $adminLogin, string $adminPassword) + { + $this->doctrine = $doctrine; + $this->adminLogin = $adminLogin; + $this->adminPassword = $adminPassword; + } + /** * Symfony calls this method if you use features like switch_user * or remember_me. @@ -27,7 +41,18 @@ public function loadUserByUsername($username) public function loadUserByIdentifier(string $identifier): UserInterface { - return new AdminUser($identifier, bin2hex(random_bytes(64))); + if ($identifier == $this->adminLogin) { + return new AdminUser($identifier, bin2hex(random_bytes(64))); + } + + $user = $this->doctrine->getRepository(User::class)->findOneByUsername($identifier); + if (!$user) { + // instead of throwing an exception, return a fake user: this will + // fail during authentication since the user does not exist + return new NormalUser($identifier, '', 0); + } + + return new NormalUser($identifier, $user->getPassword(), $user->getId()); } /** @@ -43,7 +68,7 @@ public function loadUserByIdentifier(string $identifier): UserInterface */ public function refreshUser(UserInterface $user): UserInterface { - if (!$user instanceof AdminUser) { + if ((!$user instanceof AdminUser) && (!$user instanceof NormalUser)) { throw new UnsupportedUserException(sprintf('Invalid user class "%s".', get_class($user))); } @@ -55,6 +80,6 @@ public function refreshUser(UserInterface $user): UserInterface */ public function supportsClass($class): bool { - return AdminUser::class === $class; + return (AdminUser::class === $class) || (NormalUser::class === $class); } } diff --git a/src/Security/UserVoter.php b/src/Security/UserVoter.php new file mode 100644 index 00000000..c13a9028 --- /dev/null +++ b/src/Security/UserVoter.php @@ -0,0 +1,79 @@ +doctrine = $doctrine; + } + + protected function supports(string $attribute, mixed $subject): bool + { + // if the voter doesn't support this attribute, return false + if (!in_array($attribute, [self::ACCESS])) { + return false; + } + + return true; + } + + protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool + { + $user = $token->getUser(); + + if ($user instanceof AdminUser) { + // admins can alway access everything + return true; + } + + if (!$user instanceof NormalUser) { + // the user must be logged in; if not, deny access + $vote?->addReason('The user is not logged in.'); + + return false; + } + + $userId = $subject; + + return match ($attribute) { + self::ACCESS => $this->canAccess($user, $userId, $vote), + default => throw new \LogicException('This code should not be reached!'), + }; + } + + private function canAccess(NormalUser $logged_user, int $userId, ?Vote $vote): bool + { + $user = $this->doctrine->getRepository(User::class)->findOneById($userId); + if (!$user) { + $vote?->addReason(sprintf( + 'Id %d does not exist', + $userId + )); + + return false; + } + + if ($logged_user->getUsername() === $user->getUsername()) { + return true; + } + + $vote?->addReason(sprintf( + 'The logged in user (username: %s) is not (id: %d)', + $logged_user->getUsername(), $userId + )); + + return false; + } +} diff --git a/templates/_partials/add_delegate_modal.html.twig b/templates/_partials/add_delegate_modal.html.twig index 6eaba06e..4e3cdc2c 100644 --- a/templates/_partials/add_delegate_modal.html.twig +++ b/templates/_partials/add_delegate_modal.html.twig @@ -9,11 +9,16 @@
+ {% if admin_logged %} + {% elseif user_logged %} + {# do not show to normal users the whole list of users #} + + {% endif %} {{ "delegates.member.help"|trans }}
@@ -30,4 +35,4 @@
- \ No newline at end of file + diff --git a/templates/_partials/navigation.html.twig b/templates/_partials/navigation.html.twig index 24dee276..ce054009 100644 --- a/templates/_partials/navigation.html.twig +++ b/templates/_partials/navigation.html.twig @@ -1,19 +1,31 @@ \ No newline at end of file + diff --git a/templates/_partials/share_modal.html.twig b/templates/_partials/share_modal.html.twig index a7833888..239e772e 100644 --- a/templates/_partials/share_modal.html.twig +++ b/templates/_partials/share_modal.html.twig @@ -21,11 +21,16 @@
+ {% if admin_logged %} + {% elseif user_logged %} + {# do not show to normal users the whole list of users #} + + {% endif %} {% if principals|length == 0 %} {{ "calendars.delegates.member.none"|trans }} @@ -48,4 +53,4 @@
- \ No newline at end of file + diff --git a/templates/addressbooks/index.html.twig b/templates/addressbooks/index.html.twig index bdd479b9..36d18da1 100644 --- a/templates/addressbooks/index.html.twig +++ b/templates/addressbooks/index.html.twig @@ -3,7 +3,11 @@ {% block body %} +{% if admin_logged %} {% include '_partials/back_button.html.twig' with { url: path('user_index'), text: "users.back"|trans } %} +{% elseif user_logged %} +{% include '_partials/back_button.html.twig' with { url: path('user_user', {userId: user_logged_id}), text: "users.back"|trans } %} +{% endif %}

{{ "addressbooks.for"|trans({'who': principal.displayName}) }} + {{ "addressbooks.new"|trans }}

@@ -39,4 +43,4 @@ {% include '_partials/delete_modal.html.twig' with {flavour: 'addressbooks'} %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/base.html.twig b/templates/base.html.twig index 3d6f5c99..f126e16c 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -1,3 +1,7 @@ +{% set admin_logged = app.user and is_granted("ROLE_ADMIN") %} +{% set user_logged = app.user and is_granted("ROLE_USER") %} +{% set user_logged_id = app.user and is_granted("ROLE_USER") ? app.user.getUserId() : 0 %} + diff --git a/templates/calendars/index.html.twig b/templates/calendars/index.html.twig index 45121fc5..23aada71 100644 --- a/templates/calendars/index.html.twig +++ b/templates/calendars/index.html.twig @@ -3,7 +3,11 @@ {% block body %} +{% if admin_logged %} {% include '_partials/back_button.html.twig' with { url: path('user_index'), text: "users.back"|trans } %} +{% elseif user_logged %} +{% include '_partials/back_button.html.twig' with { url: path('user_user', {userId: user_logged_id}), text: "users.back"|trans } %} +{% endif %}

{{ "calendars.for"|trans({'who': principal.displayName}) }} + {{ "calendars.new"|trans }}

diff --git a/templates/index.html.twig b/templates/index.html.twig index 402b6045..9728ca1a 100644 --- a/templates/index.html.twig +++ b/templates/index.html.twig @@ -42,7 +42,7 @@ {% if webDAVEnabled %}{{ "enabled"|trans }}{% else %}{{ "disabled"|trans }}{% endif %} - {{ "admin.interface"|trans }} + {{ "index.login"|trans }} diff --git a/templates/users/delegates.html.twig b/templates/users/delegates.html.twig index 871bd8a5..b3c0715f 100644 --- a/templates/users/delegates.html.twig +++ b/templates/users/delegates.html.twig @@ -3,7 +3,11 @@ {% block body %} +{% if admin_logged %} {% include '_partials/back_button.html.twig' with { url: path('user_index'), text: "users.back"|trans } %} +{% elseif user_logged %} +{% include '_partials/back_button.html.twig' with { url: path('user_user', {userId: user_logged_id}), text: "users.back"|trans } %} +{% endif %}

{{ "calendars.delegates.for"|trans({'what': principal.displayName}) }} @@ -44,4 +48,4 @@ {% include '_partials/delete_modal.html.twig' with {flavour: 'delegates'} %} {% include '_partials/add_delegate_modal.html.twig' with {principals: allPrincipals} %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/users/edit.html.twig b/templates/users/edit.html.twig index 137705bd..bf6b539e 100644 --- a/templates/users/edit.html.twig +++ b/templates/users/edit.html.twig @@ -3,7 +3,11 @@ {% block body %} +{% if admin_logged %} {% include '_partials/back_button.html.twig' with { url: path('user_index'), text: "users.back"|trans } %} +{% elseif user_logged %} +{% include '_partials/back_button.html.twig' with { url: path('user_user', {userId: user_logged_id}), text: "users.back"|trans } %} +{% endif %} {% if username %}

{{ "users.edit"|trans({'username': username }) }}

@@ -13,4 +17,4 @@ {{ form(form) }} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/users/index.html.twig b/templates/users/index.html.twig index 099fb403..aed3a3d7 100644 --- a/templates/users/index.html.twig +++ b/templates/users/index.html.twig @@ -3,7 +3,9 @@ {% block body %} +{% if admin_logged %}

{{ "title.users_and_resources"|trans }}+ {{ "users.new"|trans }}

+{% endif %}
{% for result in results %} @@ -48,4 +50,4 @@ {% include '_partials/delete_modal.html.twig' with {flavour: 'users'} %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/translations/messages+intl-icu.de.xlf b/translations/messages+intl-icu.de.xlf index 3f9a28c7..df8302fb 100644 --- a/translations/messages+intl-icu.de.xlf +++ b/translations/messages+intl-icu.de.xlf @@ -29,8 +29,8 @@ label.error Fehler - - admin.interface + + index.login Administrationsoberfläche @@ -217,6 +217,10 @@ calendars.shared.with Kalender geteilt mit {who} + + calendars.share.with + Kalender mit [Benutzername] teilen + users.username Benutzername @@ -281,6 +285,10 @@ calendars.delegates.member.add Diesen Kalender mit anderen Benutzern teilen: + + calendars.delegates.member.add.username + Teile alle Kalender mit [Benutzername] + calendars.delegates.member.help Das Hinzufügen eines Benutzers, der bereits einen gemeinsamen Zugriff auf diesen Kalender hat, wirkt sich nur auf dessen Zugriffsrecht aus. @@ -585,7 +593,7 @@ calendar.share_access.3 lesen / schreiben - + calendar.public öffentlich diff --git a/translations/messages+intl-icu.en.xlf b/translations/messages+intl-icu.en.xlf index 4c6e1cd4..46148590 100644 --- a/translations/messages+intl-icu.en.xlf +++ b/translations/messages+intl-icu.en.xlf @@ -29,9 +29,9 @@ label.error Error - - admin.interface - Administration interface + + index.login + Login close @@ -217,6 +217,10 @@ calendars.shared.with Calendars shared with {who} + + calendars.share.with + Share calendar with [username] + users.username Username @@ -281,6 +285,10 @@ calendars.delegates.member.add Share this calendar with another user: + + calendars.delegates.member.add.username + Share all calendars with [username] + calendars.delegates.member.help Adding a user who already has a shared access to this calendar will only affect its access right @@ -585,7 +593,7 @@ calendar.share_access.3 read / write - + calendar.public public diff --git a/translations/messages+intl-icu.fr.xliff b/translations/messages+intl-icu.fr.xlf similarity index 98% rename from translations/messages+intl-icu.fr.xliff rename to translations/messages+intl-icu.fr.xlf index fad1aede..8e8d908a 100644 --- a/translations/messages+intl-icu.fr.xliff +++ b/translations/messages+intl-icu.fr.xlf @@ -29,8 +29,8 @@ label.error Erreur - - admin.interface + + index.login Interface d'administration @@ -217,6 +217,10 @@ calendars.shared.with Calendriers partagés avec {who} + + calendars.share.with + Partagez le calendrier avec [nom d'utilisateur] + users.username Nom d'utilisateur @@ -281,6 +285,10 @@ calendars.delegates.member.add Partager ce calendrier avec un autre utilisateur : + + calendars.delegates.member.add.username + Partagez tous les calendriers avec [nom d'utilisateur] + calendars.delegates.member.help L'ajout d'un utilisateur ayant déjà un accès partagé à ce calendrier n'affectera que ses droits d'accès