diff --git a/.gitignore b/.gitignore index cd50c406f..95bd3013c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,5 @@ Resources/node_modules/ Resources/public/sass/* !Resources/public/sass/_custom.scss !Resources/public/sass/_timeline.scss -!Resources/public/sass/custom/ \ No newline at end of file +!Resources/public/sass/custom/ + diff --git a/ChillMainBundle.php b/ChillMainBundle.php index 59bf33e50..682a85cf1 100644 --- a/ChillMainBundle.php +++ b/ChillMainBundle.php @@ -7,6 +7,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Chill\MainBundle\DependencyInjection\SearchableServicesCompilerPass; use Chill\MainBundle\DependencyInjection\ConfigConsistencyCompilerPass; use Chill\MainBundle\DependencyInjection\TimelineCompilerClass; +use Chill\MainBundle\DependencyInjection\RoleProvidersCompilerPass; class ChillMainBundle extends Bundle { @@ -16,5 +17,6 @@ class ChillMainBundle extends Bundle $container->addCompilerPass(new SearchableServicesCompilerPass()); $container->addCompilerPass(new ConfigConsistencyCompilerPass()); $container->addCompilerPass(new TimelineCompilerClass()); + $container->addCompilerPass(new RoleProvidersCompilerPass); } } diff --git a/Controller/AdminController.php b/Controller/AdminController.php index 55dca2030..2009ecf21 100644 --- a/Controller/AdminController.php +++ b/Controller/AdminController.php @@ -37,4 +37,9 @@ class AdminController extends Controller { return $this->render('ChillMainBundle:Admin:layout.html.twig'); } + public function indexPermissionsAction() + { + return $this->render('ChillMainBundle:Admin:layout_permissions.html.twig'); + } + } diff --git a/Controller/CenterController.php b/Controller/CenterController.php new file mode 100644 index 000000000..05485770e --- /dev/null +++ b/Controller/CenterController.php @@ -0,0 +1,176 @@ +getDoctrine()->getManager(); + + $entities = $em->getRepository('ChillMainBundle:Center')->findAll(); + + return $this->render('ChillMainBundle:Center:index.html.twig', array( + 'entities' => $entities, + )); + } + /** + * Creates a new Center entity. + * + */ + public function createAction(Request $request) + { + $center = new Center(); + $form = $this->createCreateForm($center); + $form->handleRequest($request); + + if ($form->isValid()) { + $em = $this->getDoctrine()->getManager(); + $em->persist($center); + $em->flush(); + + return $this->redirect($this->generateUrl('admin_center_show', array('id' => $center->getId()))); + } + + return $this->render('ChillMainBundle:Center:new.html.twig', array( + 'entity' => $center, + 'form' => $form->createView(), + )); + } + + /** + * Creates a form to create a Center entity. + * + * @param Center $center The entity + * + * @return \Symfony\Component\Form\Form The form + */ + private function createCreateForm(Center $center) + { + $form = $this->createForm(new CenterType(), $center, array( + 'action' => $this->generateUrl('admin_center_create'), + 'method' => 'POST', + )); + + $form->add('submit', 'submit', array('label' => 'Create')); + + return $form; + } + + /** + * Displays a form to create a new Center entity. + * + */ + public function newAction() + { + $center = new Center(); + $form = $this->createCreateForm($center); + + return $this->render('ChillMainBundle:Center:new.html.twig', array( + 'entity' => $center, + 'form' => $form->createView(), + )); + } + + /** + * Finds and displays a Center entity. + * + */ + public function showAction($id) + { + $em = $this->getDoctrine()->getManager(); + + $center = $em->getRepository('ChillMainBundle:Center')->find($id); + + if (!$center) { + throw $this->createNotFoundException('Unable to find Center entity.'); + } + + return $this->render('ChillMainBundle:Center:show.html.twig', array( + 'entity' => $center + )); + } + + /** + * Displays a form to edit an existing Center entity. + * + */ + public function editAction($id) + { + $em = $this->getDoctrine()->getManager(); + + $center = $em->getRepository('ChillMainBundle:Center')->find($id); + + if (!$center) { + throw $this->createNotFoundException('Unable to find Center entity.'); + } + + $editForm = $this->createEditForm($center); + return $this->render('ChillMainBundle:Center:edit.html.twig', array( + 'entity' => $center, + 'edit_form' => $editForm->createView() + )); + } + + /** + * Creates a form to edit a Center entity. + * + * @param Center $center The entity + * + * @return \Symfony\Component\Form\Form The form + */ + private function createEditForm(Center $center) + { + $form = $this->createForm(new CenterType(), $center, array( + 'action' => $this->generateUrl('admin_center_update', array('id' => $center->getId())), + 'method' => 'PUT', + )); + + $form->add('submit', 'submit', array('label' => 'Update')); + + return $form; + } + /** + * Edits an existing Center entity. + * + */ + public function updateAction(Request $request, $id) + { + $em = $this->getDoctrine()->getManager(); + + $center = $em->getRepository('ChillMainBundle:Center')->find($id); + + if (!$center) { + throw $this->createNotFoundException('Unable to find Center entity.'); + } + + $editForm = $this->createEditForm($center); + $editForm->handleRequest($request); + + if ($editForm->isValid()) { + $em->flush(); + + return $this->redirect($this->generateUrl('admin_center_edit', array('id' => $id))); + } + + return $this->render('ChillMainBundle:Center:edit.html.twig', array( + 'entity' => $center, + 'edit_form' => $editForm->createView() + )); + } +} diff --git a/Controller/PermissionsGroupController.php b/Controller/PermissionsGroupController.php new file mode 100644 index 000000000..b0f0fadab --- /dev/null +++ b/Controller/PermissionsGroupController.php @@ -0,0 +1,447 @@ +getDoctrine()->getManager(); + + $entities = $em->getRepository('ChillMainBundle:PermissionsGroup')->findAll(); + + return $this->render('ChillMainBundle:PermissionsGroup:index.html.twig', array( + 'entities' => $entities, + )); + } + + /** + * Creates a new PermissionsGroup entity. + * + */ + public function createAction(Request $request) + { + $permissionsGroup = new PermissionsGroup(); + $form = $this->createCreateForm($permissionsGroup); + $form->handleRequest($request); + + if ($form->isValid()) { + $em = $this->getDoctrine()->getManager(); + $em->persist($permissionsGroup); + $em->flush(); + + return $this->redirect($this->generateUrl('admin_permissionsgroup_edit', + array('id' => $permissionsGroup->getId()))); + } + + return $this->render('ChillMainBundle:PermissionsGroup:new.html.twig', array( + 'entity' => $permissionsGroup, + 'form' => $form->createView(), + )); + } + + /** + * Creates a form to create a PermissionsGroup entity. + * + * @param PermissionsGroup $permissionsGroup The entity + * + * @return \Symfony\Component\Form\Form The form + */ + private function createCreateForm(PermissionsGroup $permissionsGroup) + { + $form = $this->createForm(new PermissionsGroupType(), $permissionsGroup, array( + 'action' => $this->generateUrl('admin_permissionsgroup_create'), + 'method' => 'POST', + )); + + $form->add('submit', 'submit', array('label' => 'Create')); + + return $form; + } + + /** + * Displays a form to create a new PermissionsGroup entity. + * + */ + public function newAction() + { + $permissionsGroup = new PermissionsGroup(); + $form = $this->createCreateForm($permissionsGroup); + + return $this->render('ChillMainBundle:PermissionsGroup:new.html.twig', array( + 'entity' => $permissionsGroup, + 'form' => $form->createView(), + )); + } + + /** + * Finds and displays a PermissionsGroup entity. + * + */ + public function showAction($id) + { + $em = $this->getDoctrine()->getManager(); + + $permissionsGroup = $em->getRepository('ChillMainBundle:PermissionsGroup')->find($id); + + if (!$permissionsGroup) { + throw $this->createNotFoundException('Unable to find PermissionsGroup entity.'); + } + + $translatableStringHelper = $this->get('chill.main.helper.translatable_string'); + $roleScopes = $permissionsGroup->getRoleScopes()->toArray(); + usort($roleScopes, + function(RoleScope $a, RoleScope $b) use ($translatableStringHelper) { + if ($a->getScope() === NULL) { + return 1; + } + if ($b->getScope() === NULL) { + return +1; + } + + return strcmp( + $translatableStringHelper->localize($a->getScope()->getName()), + $translatableStringHelper->localize($b->getScope()->getName()) + ); + }); + + return $this->render('ChillMainBundle:PermissionsGroup:show.html.twig', array( + 'entity' => $permissionsGroup, + 'role_scopes' => $roleScopes, + 'expanded_roles' => $this->getExpandedRoles($roleScopes) + )); + } + + /** + * expand roleScopes to be easily shown in template + * + * @param array $roleScopes + * @return array + */ + private function getExpandedRoles(array $roleScopes) + { + $expandedRoles = array(); + foreach ($roleScopes as $roleScope) { + if (!array_key_exists($roleScope->getRole(), $expandedRoles)) { + $expandedRoles[$roleScope->getRole()] = + array_map( + function(RoleInterface $role) { + + return $role->getRole(); + }, + $this->get('security.role_hierarchy') + ->getReachableRoles( + array(new Role($roleScope->getRole())) + ) + ); + } + } + return $expandedRoles; + } + + /** + * Displays a form to edit an existing PermissionsGroup entity. + * + */ + public function editAction($id) + { + $em = $this->getDoctrine()->getManager(); + + $permissionsGroup = $em->getRepository('ChillMainBundle:PermissionsGroup')->find($id); + + if (!$permissionsGroup) { + throw $this->createNotFoundException('Unable to find PermissionsGroup entity.'); + } + + $editForm = $this->createEditForm($permissionsGroup); + + $deleteRoleScopesForm = array(); + foreach ($permissionsGroup->getRoleScopes() as $roleScope) { + $deleteRoleScopesForm[$roleScope->getId()] = $this->createDeleteRoleScopeForm( + $permissionsGroup, $roleScope); + } + + $addRoleScopesForm = $this->createAddRoleScopeForm($permissionsGroup); + + return $this->render('ChillMainBundle:PermissionsGroup:edit.html.twig', array( + 'entity' => $permissionsGroup, + 'edit_form' => $editForm->createView(), + 'expanded_roles' => $this->getExpandedRoles($permissionsGroup->getRoleScopes()->toArray()), + 'delete_role_scopes_form' => array_map( function($form) { + + return $form->createView(); + }, $deleteRoleScopesForm), + 'add_role_scopes_form' => $addRoleScopesForm->createView() + )); + } + + /** + * Creates a form to edit a PermissionsGroup entity. + * + * @param PermissionsGroup $permissionsGroup The entity + * + * @return \Symfony\Component\Form\Form The form + */ + private function createEditForm(PermissionsGroup $permissionsGroup) + { + $form = $this->createForm(new PermissionsGroupType(), $permissionsGroup, array( + 'action' => $this->generateUrl('admin_permissionsgroup_update', array('id' => $permissionsGroup->getId())), + 'method' => 'PUT', + )); + + $form->add('submit', 'submit', array('label' => 'Update')); + + return $form; + } + + /** + * Edits an existing PermissionsGroup entity. + * + */ + public function updateAction(Request $request, $id) + { + $em = $this->getDoctrine()->getManager(); + + $permissionsGroup = $em->getRepository('ChillMainBundle:PermissionsGroup')->find($id); + + if (!$permissionsGroup) { + throw $this->createNotFoundException('Unable to find PermissionsGroup entity.'); + } + + $editForm = $this->createEditForm($permissionsGroup); + $editForm->handleRequest($request); + + if ($editForm->isValid()) { + + $em->flush(); + + return $this->redirect($this->generateUrl('admin_permissionsgroup_edit', array('id' => $id))); + } + + $deleteRoleScopesForm = array(); + foreach ($permissionsGroup->getRoleScopes() as $roleScope) { + $deleteRoleScopesForm[$roleScope->getId()] = $this->createDeleteRoleScopeForm( + $permissionsGroup, $roleScope); + } + + $addRoleScopesForm = $this->createAddRoleScopeForm($permissionsGroup); + + return $this->render('ChillMainBundle:PermissionsGroup:edit.html.twig', array( + 'entity' => $permissionsGroup, + 'edit_form' => $editForm->createView(), + 'expanded_roles' => $this->getExpandedRoles($permissionsGroup->getRoleScopes()->toArray()), + 'delete_role_scopes_form' => array_map( function($form) { + + return $form->createView(); + }, $deleteRoleScopesForm), + 'add_role_scopes_form' => $addRoleScopesForm->createView() + )); + } + + /** + * get a role scope by his parameters. The role scope is persisted if it + * doesn't exists in database. + * + * @param Scope $scope + * @param string $role + * @return RoleScope + */ + protected function getPersistentRoleScopeBy($role, Scope $scope = null) + { + $em = $this->getDoctrine()->getManager(); + + $roleScope = $em->getRepository('ChillMainBundle:RoleScope') + ->findOneBy(array('role' => $role, 'scope' => $scope)); + + if ($roleScope === NULL) { + $roleScope = (new RoleScope()) + ->setRole($role) + ->setScope($scope) + ; + + $em->persist($roleScope); + } + + return $roleScope; + } + + /** + * remove an association between permissionsGroup and roleScope + * + * @param int $pgid permissionsGroup id + * @param int $rsid roleScope id + * @return redirection to edit form + */ + public function deleteLinkRoleScopeAction($pgid, $rsid) + { + $em = $this->getDoctrine()->getManager(); + + $permissionsGroup = $em->getRepository('ChillMainBundle:PermissionsGroup')->find($pgid); + $roleScope = $em->getRepository('ChillMainBundle:RoleScope')->find($rsid); + + if (!$permissionsGroup) { + throw $this->createNotFoundException('Unable to find PermissionsGroup entity.'); + } + + if (!$roleScope) { + throw $this->createNotFoundException('Unable to find RoleScope entity'); + } + + try { + $permissionsGroup->removeRoleScope($roleScope); + } catch (\RuntimeException $ex) { + $this->addFlash('notice', + $this->get('translator')->trans("The role '%role%' and circle " + . "'%scope%' is not associated with this permission group", array( + '%role%' => $this->get('translator')->trans($roleScope->getRole()), + '%scope%' => $this->get('chill.main.helper.translatable_string') + ->localize($roleScope->getScope()->getName()) + ))); + + return $this->redirect($this->generateUrl('admin_permissionsgroup_edit', + array('id' => $pgid))); + } + + $em->flush(); + + $this->addFlash('notice', + $this->get('translator')->trans("The role '%role%' on circle " + . "'%scope%' has been removed", array( + '%role%' => $this->get('translator')->trans($roleScope->getRole()), + '%scope%' => $this->get('chill.main.helper.translatable_string') + ->localize($roleScope->getScope()->getName()) + ))); + + return $this->redirect($this->generateUrl('admin_permissionsgroup_edit', + array('id' => $pgid))); + } + + /** + * + * @param Request $request + * @param int $id + * @return Respon + * @throws type + */ + public function addLinkRoleScopeAction(Request $request, $id) + { + $em = $this->getDoctrine()->getManager(); + + $permissionsGroup = $em->getRepository('ChillMainBundle:PermissionsGroup')->find($id); + + if (!$permissionsGroup) { + throw $this->createNotFoundException('Unable to find PermissionsGroup entity.'); + } + + $form = $this->createAddRoleScopeForm($permissionsGroup); + $form->handleRequest($request); + + if ($form->isValid()) { + $roleScope = $this->getPersistentRoleScopeBy( + $form['composed_role_scope']->getData()->getRole(), + $form['composed_role_scope']->getData()->getScope() + ); + + $permissionsGroup->addRoleScope($roleScope); + $violations = $this->get('validator')->validate($permissionsGroup); + + if ($violations->count() === 0) { + $em->flush(); + + $this->addFlash('notice', + $this->get('translator')->trans("The permissions have been added")); + + return $this->redirect($this->generateUrl('admin_permissionsgroup_edit', + array('id' => $id))); + } else { + foreach($violations as $error) { + $this->addFlash('error', $error->getMessage()); + } + } + + } else { + foreach ($form->getErrors() as $error) { + $this->addFlash('error', $error->getMessage()); + } + } + + $editForm = $this->createEditForm($permissionsGroup); + + $deleteRoleScopesForm = array(); + foreach ($permissionsGroup->getRoleScopes() as $roleScope) { + $deleteRoleScopesForm[$roleScope->getId()] = $this->createDeleteRoleScopeForm( + $permissionsGroup, $roleScope); + } + + $addRoleScopesForm = $this->createAddRoleScopeForm($permissionsGroup); + + return $this->render('ChillMainBundle:PermissionsGroup:edit.html.twig', array( + 'entity' => $permissionsGroup, + 'edit_form' => $editForm->createView(), + 'expanded_roles' => $this->getExpandedRoles($permissionsGroup->getRoleScopes()->toArray()), + 'delete_role_scopes_form' => array_map( function($form) { + + return $form->createView(); + }, $deleteRoleScopesForm), + 'add_role_scopes_form' => $addRoleScopesForm->createView() + )); + + } + + /** + * Creates a form to delete a link to roleScope. + * + * @param mixed $permissionsGroup The entity id + * + * @return \Symfony\Component\Form\Form The form + */ + private function createDeleteRoleScopeForm(PermissionsGroup $permissionsGroup, + RoleScope $roleScope) + { + return $this->createFormBuilder() + ->setAction($this->generateUrl('admin_permissionsgroup_delete_role_scope', + array('pgid' => $permissionsGroup->getId(), 'rsid' => $roleScope->getId()))) + ->setMethod('DELETE') + ->add('submit', 'submit', array('label' => 'Delete')) + ->getForm() + ; + } + + /** + * creates a form to add a role scope to permissionsgroup + * + * @param PermissionsGroup $permissionsGroup + * @return \Symfony\Component\Form\Form The form + */ + private function createAddRoleScopeForm(PermissionsGroup $permissionsGroup) + { + return $this->createFormBuilder() + ->setAction($this->generateUrl('admin_permissionsgroup_add_role_scope', + array('id' => $permissionsGroup->getId()))) + ->setMethod('PUT') + ->add('composed_role_scope', 'composed_role_scope') + ->add('submit', 'submit', array('label' => 'Add permission')) + ->getForm() + ; + } + + +} diff --git a/Controller/ScopeController.php b/Controller/ScopeController.php new file mode 100644 index 000000000..6e71d6876 --- /dev/null +++ b/Controller/ScopeController.php @@ -0,0 +1,177 @@ +getDoctrine()->getManager(); + + $entities = $em->getRepository('ChillMainBundle:Scope')->findAll(); + + return $this->render('ChillMainBundle:Scope:index.html.twig', array( + 'entities' => $entities, + )); + } + /** + * Creates a new Scope entity. + * + */ + public function createAction(Request $request) + { + $scope = new Scope(); + $form = $this->createCreateForm($scope); + $form->handleRequest($request); + + if ($form->isValid()) { + $em = $this->getDoctrine()->getManager(); + $em->persist($scope); + $em->flush(); + + return $this->redirect($this->generateUrl('admin_scope_show', array('id' => $scope->getId()))); + } + + return $this->render('ChillMainBundle:Scope:new.html.twig', array( + 'entity' => $scope, + 'form' => $form->createView(), + )); + } + + /** + * Creates a form to create a Scope entity. + * + * @param Scope $scope The entity + * + * @return \Symfony\Component\Form\Form The form + */ + private function createCreateForm(Scope $scope) + { + $form = $this->createForm(new ScopeType(), $scope, array( + 'action' => $this->generateUrl('admin_scope_create'), + 'method' => 'POST', + )); + + $form->add('submit', 'submit', array('label' => 'Create')); + + return $form; + } + + /** + * Displays a form to create a new Scope entity. + * + */ + public function newAction() + { + $scope = new Scope(); + $form = $this->createCreateForm($scope); + + return $this->render('ChillMainBundle:Scope:new.html.twig', array( + 'entity' => $scope, + 'form' => $form->createView(), + )); + } + + /** + * Finds and displays a Scope entity. + * + */ + public function showAction($id) + { + $em = $this->getDoctrine()->getManager(); + + $scope = $em->getRepository('ChillMainBundle:Scope')->find($id); + + if (!$scope) { + throw $this->createNotFoundException('Unable to find Scope entity.'); + } + + return $this->render('ChillMainBundle:Scope:show.html.twig', array( + 'entity' => $scope + )); + } + + /** + * Displays a form to edit an existing Scope entity. + * + */ + public function editAction($id) + { + $em = $this->getDoctrine()->getManager(); + + $scope = $em->getRepository('ChillMainBundle:Scope')->find($id); + + if (!$scope) { + throw $this->createNotFoundException('Unable to find Scope entity.'); + } + + $editForm = $this->createEditForm($scope); + + return $this->render('ChillMainBundle:Scope:edit.html.twig', array( + 'entity' => $scope, + 'edit_form' => $editForm->createView(), + )); + } + + /** + * Creates a form to edit a Scope entity. + * + * @param Scope $scope The entity + * + * @return \Symfony\Component\Form\Form The form + */ + private function createEditForm(Scope $scope) + { + $form = $this->createForm(new ScopeType(), $scope, array( + 'action' => $this->generateUrl('admin_scope_update', array('id' => $scope->getId())), + 'method' => 'PUT', + )); + + $form->add('submit', 'submit', array('label' => 'Update')); + + return $form; + } + /** + * Edits an existing Scope entity. + * + */ + public function updateAction(Request $request, $id) + { + $em = $this->getDoctrine()->getManager(); + + $scope = $em->getRepository('ChillMainBundle:Scope')->find($id); + + if (!$scope) { + throw $this->createNotFoundException('Unable to find Scope entity.'); + } + + $editForm = $this->createEditForm($scope); + $editForm->handleRequest($request); + + if ($editForm->isValid()) { + $em->flush(); + + return $this->redirect($this->generateUrl('admin_scope_edit', array('id' => $id))); + } + + return $this->render('ChillMainBundle:Scope:edit.html.twig', array( + 'entity' => $scope, + 'edit_form' => $editForm->createView() + )); + } +} diff --git a/Controller/UserController.php b/Controller/UserController.php new file mode 100644 index 000000000..604d47152 --- /dev/null +++ b/Controller/UserController.php @@ -0,0 +1,423 @@ +getDoctrine()->getManager(); + + $entities = $em->getRepository('ChillMainBundle:User')->findAll(); + + return $this->render('ChillMainBundle:User:index.html.twig', array( + 'entities' => $entities, + )); + } + /** + * Creates a new User entity. + * + */ + public function createAction(Request $request) + { + $user = new User(); + $form = $this->createCreateForm($user); + $form->handleRequest($request); + + if ($form->isValid()) { + $em = $this->getDoctrine()->getManager(); + + $user->setPassword($this->get('security.password_encoder') + ->encodePassword($user, $form['plainPassword']['password']->getData())); + + $em->persist($user); + $em->flush(); + + return $this->redirect($this->generateUrl('admin_user_show', array('id' => $user->getId()))); + } + + return $this->render('ChillMainBundle:User:new.html.twig', array( + 'entity' => $user, + 'form' => $form->createView(), + )); + } + + /** + * Creates a form to create a User entity. + * + * @param User $entity The entity + * + * @return \Symfony\Component\Form\Form The form + */ + private function createCreateForm(User $entity) + { + $form = $this->createForm(new UserType(), $entity, array( + 'action' => $this->generateUrl('admin_user_create'), + 'method' => 'POST', + 'is_creation' => true + )); + + $form->add('submit', 'submit', array('label' => 'Create')); + + return $form; + } + + /** + * Displays a form to create a new User entity. + * + */ + public function newAction() + { + $user = new User(); + $form = $this->createCreateForm($user); + + return $this->render('ChillMainBundle:User:new.html.twig', array( + 'entity' => $user, + 'form' => $form->createView(), + )); + } + + /** + * Finds and displays a User entity. + * + */ + public function showAction($id) + { + $em = $this->getDoctrine()->getManager(); + + $user = $em->getRepository('ChillMainBundle:User')->find($id); + + if (!$user) { + throw $this->createNotFoundException('Unable to find User entity.'); + } + + return $this->render('ChillMainBundle:User:show.html.twig', array( + 'entity' => $user, + )); + } + + /** + * Displays a form to edit an existing User entity. + * + */ + public function editAction($id) + { + $em = $this->getDoctrine()->getManager(); + + $user = $em->getRepository('ChillMainBundle:User')->find($id); + + if (!$user) { + throw $this->createNotFoundException('Unable to find User entity.'); + } + + $editForm = $this->createEditForm($user); + + return $this->render('ChillMainBundle:User:edit.html.twig', array( + 'entity' => $user, + 'edit_form' => $editForm->createView(), + 'add_groupcenter_form' => $this->createAddLinkGroupCenterForm($user)->createView(), + 'delete_groupcenter_form' => array_map( + function(\Symfony\Component\Form\Form $form) { + return $form->createView(); + + }, + iterator_to_array($this->getDeleteLinkGroupCenterByUser($user), true)) + )); + } + + /** + * Displays a form to edit the user password. + * + */ + public function editPasswordAction($id) + { + $em = $this->getDoctrine()->getManager(); + + $user = $em->getRepository('ChillMainBundle:User')->find($id); + + if (!$user) { + throw $this->createNotFoundException('Unable to find User entity.'); + } + + $editForm = $this->createEditPasswordForm($user); + + return $this->render('ChillMainBundle:User:edit_password.html.twig', array( + 'entity' => $user, + 'edit_form' => $editForm->createView() + )); + } + + /** + * + * + * @param User $user + * @return \Symfony\Component\Form\Form + */ + private function createEditPasswordForm(User $user) + { + return $this->createForm(new UserPasswordType(), $user, array( + 'action' => + $this->generateUrl('admin_user_update_password', array('id' => $user->getId())), + 'method' => 'PUT' + )) + ->add('submit', 'submit', array('label' => 'Change password')) + ; + } + + public function deleteLinkGroupCenterAction($uid, $gcid) + { + $em = $this->getDoctrine()->getManager(); + + $user = $em->getRepository('ChillMainBundle:User')->find($uid); + + if (!$user) { + throw $this->createNotFoundException('Unable to find User entity.'); + } + + $groupCenter = $em->getRepository('ChillMainBundle:GroupCenter') + ->find($gcid); + + if (!$groupCenter) { + throw $this->createNotFoundException('Unable to find groupCenter entity'); + } + + try { + $user->removeGroupCenter($groupCenter); + } catch (\RuntimeException $ex) { + $this->addFlash('error', $this->get('translator')->trans($ex-getMessage())); + + return $this->redirect($this->generateUrl('admin_user_edit', array('id' => $uid))); + } + + $em->flush(); + + $this->addFlash('success', $this->get('translator') + ->trans('The permissions where removed.')); + + return $this->redirect($this->generateUrl('admin_user_edit', array('id' => $uid))); + + } + + public function addLinkGroupCenterAction(Request $request, $uid) + { + $em = $this->getDoctrine()->getManager(); + + $user = $em->getRepository('ChillMainBundle:User')->find($uid); + + if (!$user) { + throw $this->createNotFoundException('Unable to find User entity.'); + } + + $form = $this->createAddLinkGroupCenterForm($user); + $form->handleRequest($request); + + if ($form->isValid()) { + $groupCenter = $this->getPersistedGroupCenter( + $form[self::FORM_GROUP_CENTER_COMPOSED]->getData()); + $user->addGroupCenter($groupCenter); + + if ($this->get('validator')->validate($user)->count() === 0) { + $em->flush(); + + $this->addFlash('success', $this->get('translator')->trans('The ' + . 'permissions have been successfully added to the user')); + + return $this->redirect($this->generateUrl('admin_user_edit', + array('id' => $uid))); + } else { + foreach($this->get('validator')->validate($user) as $error) + $this->addFlash('error', $error->getMessage()); + } + } + + return $this->render('ChillMainBundle:User:edit.html.twig', array( + 'entity' => $user, + 'edit_form' => $this->createEditForm($user)->createView(), + 'add_groupcenter_form' => $this->createAddLinkGroupCenterForm($user)->createView(), + 'delete_groupcenter_form' => array_map( + function(\Symfony\Component\Form\Form $form) { + return $form->createView(); + + }, + iterator_to_array($this->getDeleteLinkGroupCenterByUser($user), true)) + )); + } + + private function getPersistedGroupCenter(GroupCenter $groupCenter) + { + $em = $this->getDoctrine()->getManager(); + + $groupCenterManaged = $em->getRepository('ChillMainBundle:GroupCenter') + ->findOneBy(array( + 'center' => $groupCenter->getCenter(), + 'permissionsGroup' => $groupCenter->getPermissionsGroup() + )); + + if (!$groupCenterManaged) { + $em->persist($groupCenter); + return $groupCenter; + } + + return $groupCenterManaged; + } + + /** + * Creates a form to edit a User entity. + * + * @param User $user The entity + * + * @return \Symfony\Component\Form\Form The form + */ + private function createEditForm(User $user) + { + $form = $this->createForm(new UserType(), $user, array( + 'action' => $this->generateUrl('admin_user_update', array('id' => $user->getId())), + 'method' => 'PUT', + )); + + $form->add('submit', 'submit', array('label' => 'Update')); + + return $form; + } + + /** + * Edits an existing User entity. + * + */ + public function updateAction(Request $request, $id) + { + $em = $this->getDoctrine()->getManager(); + + $user = $em->getRepository('ChillMainBundle:User')->find($id); + + if (!$user) { + throw $this->createNotFoundException('Unable to find User entity.'); + } + + $editForm = $this->createEditForm($user); + $editForm->handleRequest($request); + + if ($editForm->isValid()) { + $em->flush(); + + return $this->redirect($this->generateUrl('admin_user_edit', array('id' => $id))); + } + + return $this->render('ChillMainBundle:User:edit.html.twig', array( + 'entity' => $user, + 'edit_form' => $editForm->createView(), + 'add_groupcenter_form' => $this->createAddLinkGroupCenterForm($user)->createView(), + 'delete_groupcenter_form' => array_map( + function(\Symfony\Component\Form\Form $form) { + return $form->createView(); + + }, + iterator_to_array($this->getDeleteLinkGroupCenterByUser($user), true)) + )); + } + + /** + * Edits the user password + * + */ + public function updatePasswordAction(Request $request, $id) + { + $em = $this->getDoctrine()->getManager(); + + $user = $em->getRepository('ChillMainBundle:User')->find($id); + + if (!$user) { + throw $this->createNotFoundException('Unable to find User entity.'); + } + + $editForm = $this->createEditPasswordForm($user); + $editForm->handleRequest($request); + + if ($editForm->isValid()) { + $password = $editForm->getData(); + + $user->setPassword($this->get('security.password_encoder') + ->encodePassword($user, $password)); + + $em->flush(); + + $this->addFlash('success', $this->get('translator')->trans('Password successfully updated!')); + + return $this->redirect($this->generateUrl('admin_user_edit', array('id' => $id))); + } + + return $this->render('ChillMainBundle:User:edit_password.html.twig', array( + 'entity' => $user, + 'edit_form' => $editForm->createView(), + )); + } + + + /** + * Creates a form to delete a link to a GroupCenter + * + * @param mixed $permissionsGroup The entity id + * + * @return \Symfony\Component\Form\Form The form + */ + private function createDeleteLinkGroupCenterForm(User $user, GroupCenter $groupCenter) + { + return $this->createFormBuilder() + ->setAction($this->generateUrl('admin_user_delete_group_center', + array('uid' => $user->getId(), 'gcid' => $groupCenter->getId()))) + ->setMethod('DELETE') + ->add('submit', 'submit', array('label' => 'Delete')) + ->getForm() + ; + } + + /** + * create a form to add a link to a groupcenter + * + * @param User $user + * @return \Symfony\Component\Form\Form + */ + private function createAddLinkGroupCenterForm(User $user) + { + return $this->createFormBuilder() + ->setAction($this->generateUrl('admin_user_add_group_center', + array('uid' => $user->getId()))) + ->setMethod('POST') + ->add(self::FORM_GROUP_CENTER_COMPOSED, new ComposedGroupCenterType()) + ->add('submit', 'submit', array('label' => 'Add a new groupCenter')) + ->getForm() + ; + } + + /** + * + * @param User $user + */ + private function getDeleteLinkGroupCenterByUser(User $user) + { + foreach ($user->getGroupCenters() as $groupCenter) { + yield $groupCenter->getId() => $this + ->createDeleteLinkGroupCenterForm($user, $groupCenter); + } + } +} diff --git a/DataFixtures/ORM/LoadGroupCenters.php b/DataFixtures/ORM/LoadGroupCenters.php index 04a34d080..2629c0613 100644 --- a/DataFixtures/ORM/LoadGroupCenters.php +++ b/DataFixtures/ORM/LoadGroupCenters.php @@ -47,7 +47,7 @@ class LoadGroupCenters extends AbstractFixture implements OrderedFixtureInterfac foreach (LoadPermissionsGroup::$refs as $permissionGroupRef) { $GroupCenter = new GroupCenter(); $GroupCenter->setCenter($this->getReference($centerRef)); - $GroupCenter->addPermissionGroup($this->getReference($permissionGroupRef)); + $GroupCenter->setPermissionsGroup($this->getReference($permissionGroupRef)); $manager->persist($GroupCenter); diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 27d6a842f..cb8ba9982 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -36,6 +36,7 @@ class Configuration implements ConfigurationInterface ->prototype('scalar')->end() ->end() ->end() + ->end() ->end(); return $treeBuilder; diff --git a/DependencyInjection/RoleProvidersCompilerPass.php b/DependencyInjection/RoleProvidersCompilerPass.php new file mode 100644 index 000000000..1f2b92388 --- /dev/null +++ b/DependencyInjection/RoleProvidersCompilerPass.php @@ -0,0 +1,57 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace Chill\MainBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Reference; + +/** + * + * + * @author Julien Fastré + */ +class RoleProvidersCompilerPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('chill.main.role_provider')) { + throw new \LogicException('service chill.main.role_provider ' + . 'is not defined. It is required by RoleProviderCompilerPass'); + } + + $definition = $container->getDefinition( + 'chill.main.role_provider' + ); + + $taggedServices = $container->findTaggedServiceIds( + 'chill.role' + ); + + foreach ($taggedServices as $id => $tagAttributes) { + $definition->addMethodCall( + 'addProvider', + array(new Reference($id)) + ); + } + + } + +} diff --git a/Entity/GroupCenter.php b/Entity/GroupCenter.php index d882ab526..429aac3da 100644 --- a/Entity/GroupCenter.php +++ b/Entity/GroupCenter.php @@ -52,9 +52,9 @@ class GroupCenter /** * - * @var Collection + * @var PermissionsGroup */ - private $permissionGroups; + private $permissionsGroup; public function __construct() { @@ -76,15 +76,6 @@ class GroupCenter return $this->center; } - /** - * - * @return PermissionGroup[] - */ - public function getPermissionGroups() - { - return $this->permissionGroups; - } - /** * * @param Center $center @@ -95,22 +86,32 @@ class GroupCenter $this->center = $center; return $this; } - - /** - * - * @param PermissionGroup $permission - * @return \Chill\MainBundle\Entity\GroupCenter - */ - public function addPermissionGroup(PermissionsGroup $permission) - { - $this->permissionGroups->add($permission); - return $this; - } public function getUsers() { return $this->users; } + + /** + * + * @return PermissionGroup + */ + public function getPermissionsGroup() + { + return $this->permissionsGroup; + } + + /** + * + * @param \Chill\MainBundle\Entity\PermissionsGroup $permissionGroup + * @return \Chill\MainBundle\Entity\GroupCenter + */ + public function setPermissionsGroup(PermissionsGroup $permissionsGroup) + { + $this->permissionsGroup = $permissionsGroup; + return $this; + } + diff --git a/Entity/PermissionsGroup.php b/Entity/PermissionsGroup.php index c3f78fcb0..95630e120 100644 --- a/Entity/PermissionsGroup.php +++ b/Entity/PermissionsGroup.php @@ -21,7 +21,7 @@ namespace Chill\MainBundle\Entity; use Chill\MainBundle\Entity\RoleScope; - +use Symfony\Component\Validator\Context\ExecutionContextInterface; /** @@ -79,10 +79,50 @@ class PermissionsGroup return $this; } + /** + * + * @param RoleScope $roleScope + */ public function addRoleScope(RoleScope $roleScope) { $this->roleScopes->add($roleScope); } + + /** + * + * @param RoleScope $roleScope + * @throws \RuntimeException if the roleScope could not be removed. + */ + public function removeRoleScope(RoleScope $roleScope) + { + $result = $this->roleScopes->removeElement($roleScope); + if ($result === FALSE) { + throw new \RuntimeException(sprintf("The roleScope '%s' could not be removed, " + . "aborting.", spl_object_hash($roleScope))); + } + } + + /** + * Test that a role scope is associated only once with the permission group + * + * @param ExecutionContextInterface $context + */ + public function isRoleScopePresentOnce(ExecutionContextInterface $context) + { + $roleScopesId = array_map(function(RoleScope $roleScope) { + return $roleScope->getId(); + }, + $this->getRoleScopes()->toArray()); + $countedIds = array_count_values($roleScopesId); + + foreach ($countedIds as $id => $nb) { + if ($nb > 1) { + $context->buildViolation("A permission is already present " + . "for the same role and scope") + ->addViolation(); + } + } + } } diff --git a/Entity/RoleScope.php b/Entity/RoleScope.php index 8d85ff4a9..d8a0d2256 100644 --- a/Entity/RoleScope.php +++ b/Entity/RoleScope.php @@ -45,6 +45,10 @@ class RoleScope */ private $scope; + public function __construct() { + $this->new = true; + } + public function getId() { return $this->id; @@ -76,6 +80,7 @@ class RoleScope public function setRole($role) { $this->role = $role; + return $this; } @@ -84,11 +89,10 @@ class RoleScope * @param \Chill\MainBundle\Entity\Scope $scope * @return \Chill\MainBundle\Entity\RoleScope */ - public function setScope(Scope $scope) + public function setScope(Scope $scope = null) { $this->scope = $scope; + return $this; } - - } diff --git a/Entity/User.php b/Entity/User.php index 0a25ee04a..13f49b59e 100644 --- a/Entity/User.php +++ b/Entity/User.php @@ -5,6 +5,7 @@ namespace Chill\MainBundle\Entity; use Symfony\Component\Security\Core\User\AdvancedUserInterface; use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\ArrayCollection; +use Symfony\Component\Validator\Context\ExecutionContextInterface; /** * User @@ -165,6 +166,15 @@ class User implements AdvancedUserInterface { return $this->enabled; } + /** + * + * @param bool $enabled + */ + public function setEnabled($enabled) + { + $this->enabled = $enabled; + } + /** * * @return GroupCenter[] @@ -184,5 +194,37 @@ class User implements AdvancedUserInterface { $this->groupCenters->add($groupCenter); return $this; } + + /** + * + * @param \Chill\MainBundle\Entity\GroupCenter $groupCenter + * @throws \RuntimeException if the groupCenter is not in the collection + */ + public function removeGroupCenter(GroupCenter $groupCenter) + { + if ($this->groupCenters->removeElement($groupCenter) === FALSE) { + throw new \RuntimeException(sprintf("The groupCenter could not be removed, " + . "it seems not to be associated with the user. Aborting.")); + } + } + + /** + * This function check that groupCenter are present only once. The validator + * use this function to avoid a user to be associated to the same groupCenter + * more than once. + */ + public function isGroupCenterPresentOnce(ExecutionContextInterface $context) + { + $groupCentersIds = array(); + foreach ($this->getGroupCenters() as $groupCenter) { + if (in_array($groupCenter->getId(), $groupCentersIds)) { + $context->buildViolation("The user has already those permissions") + ->addViolation(); + + } else { + $groupCentersIds[] = $groupCenter->getId(); + } + } + } } diff --git a/Form/CenterType.php b/Form/CenterType.php new file mode 100644 index 000000000..8a7291cc2 --- /dev/null +++ b/Form/CenterType.php @@ -0,0 +1,39 @@ +add('name') + ; + } + + /** + * @param OptionsResolverInterface $resolver + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults(array( + 'data_class' => 'Chill\MainBundle\Entity\Center' + )); + } + + /** + * @return string + */ + public function getName() + { + return 'chill_mainbundle_center'; + } +} diff --git a/Form/PermissionsGroupType.php b/Form/PermissionsGroupType.php new file mode 100644 index 000000000..6a43865b1 --- /dev/null +++ b/Form/PermissionsGroupType.php @@ -0,0 +1,39 @@ +add('name') + ; + } + + /** + * @param OptionsResolverInterface $resolver + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'data_class' => 'Chill\MainBundle\Entity\PermissionsGroup' + )); + } + + /** + * @return string + */ + public function getName() + { + return 'chill_mainbundle_permissionsgroup'; + } +} diff --git a/Form/ScopeType.php b/Form/ScopeType.php new file mode 100644 index 000000000..eebdd726a --- /dev/null +++ b/Form/ScopeType.php @@ -0,0 +1,39 @@ +add('name', 'translatable_string') + ; + } + + /** + * @param OptionsResolverInterface $resolver + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'data_class' => 'Chill\MainBundle\Entity\Scope' + )); + } + + /** + * @return string + */ + public function getName() + { + return 'chill_mainbundle_scope'; + } +} diff --git a/Form/Type/ComposedGroupCenterType.php b/Form/Type/ComposedGroupCenterType.php new file mode 100644 index 000000000..fca3c8ed0 --- /dev/null +++ b/Form/Type/ComposedGroupCenterType.php @@ -0,0 +1,62 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace Chill\MainBundle\Form\Type; + +use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Chill\MainBundle\Entity\PermissionsGroup; +use Chill\MainBundle\Entity\Center; + +/** + * + * + * @author Julien Fastré + */ +class ComposedGroupCenterType extends AbstractType +{ + + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->add('permissionsgroup', 'entity', array( + 'class' => 'Chill\MainBundle\Entity\PermissionsGroup', + 'choice_label' => function(PermissionsGroup $group) { + return $group->getName(); + } + ))->add('center', 'entity', array( + 'class' => 'Chill\MainBundle\Entity\Center', + 'choice_label' => function(Center $center) { + return $center->getName(); + } + )) + ; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefault('data_class', 'Chill\MainBundle\Entity\GroupCenter'); + } + + public function getName() + { + return 'composed_groupcenter'; + } + +} diff --git a/Form/Type/ComposedRoleScopeType.php b/Form/Type/ComposedRoleScopeType.php new file mode 100644 index 000000000..194fa6f15 --- /dev/null +++ b/Form/Type/ComposedRoleScopeType.php @@ -0,0 +1,112 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace Chill\MainBundle\Form\Type; + +use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Chill\MainBundle\Templating\TranslatableStringHelper; +use Chill\MainBundle\Entity\Scope; +use Chill\MainBundle\Security\RoleProvider; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; + +/** + * Form to Edit/create a role scope. If the role scope does not + * exists in the database, he is generated. + * + * @author Julien Fastré + * @author Champs Libres + */ +class ComposedRoleScopeType extends AbstractType +{ + /** + * + * @var string[] + */ + private $roles = array(); + + /** + * + * @var string[] + */ + private $rolesWithoutScope = array(); + + /** + * + * @var TranslatableStringHelper + */ + private $translatableStringHelper; + + public function __construct(TranslatableStringHelper $translatableStringHelper, + RoleProvider $roleProvider) + { + $this->roles = $roleProvider->getRoles(); + $this->rolesWithoutScope = $roleProvider->getRolesWithoutScopes(); + $this->translatableStringHelper = $translatableStringHelper; + } + + public function buildForm(FormBuilderInterface $builder, array $options) + { + // store values used in internal function + $translatableStringHelper = $this->translatableStringHelper; + $rolesWithoutScopes = $this->rolesWithoutScope; + + //build roles + $values = array(); + foreach ($this->roles as $role) { + $values[$role] = $role; + } + + $builder + ->add('role', 'choice', array( + 'choices' => $values, + 'placeholder' => 'Choose amongst roles', + 'choice_attr' => function($role) use ($rolesWithoutScopes) { + if (in_array($role, $rolesWithoutScopes)) { + return array('data-has-scope' => '0'); + } else { + return array('data-has-scope' => '1'); + } + } + )) + ->add('scope', 'entity', array( + 'class' => 'ChillMainBundle:Scope', + 'choice_label' => function(Scope $scope) use ($translatableStringHelper) { + return $translatableStringHelper->localize($scope->getName()); + }, + 'required' => false, + 'data' => null + )); + + } + + public function getName() + { + return 'composed_role_scope'; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefault('data_class', 'Chill\MainBundle\Entity\RoleScope'); + } + +} diff --git a/Form/UserPasswordType.php b/Form/UserPasswordType.php new file mode 100644 index 000000000..827688b3d --- /dev/null +++ b/Form/UserPasswordType.php @@ -0,0 +1,66 @@ +add('password', 'repeated', array( + 'type' => 'password', + 'required' => false, + 'options' => array(), + 'first_options' => array( + 'label' => 'Password' + ), + 'second_options' => array( + 'label' => 'Repeat the password' + ), + 'invalid_message' => "The password fields must match", + 'constraints' => array( + new Length(array( + 'min' => 9, + 'minMessage' => 'The password must be greater than {{ limit }} characters' + )), + new NotBlank(), + new Regex(array( + 'pattern' => "/((?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%!,;:+\"'-\/{}~=µ\(\)£]).{6,})/", + 'message' => "The password must contains one letter, one " + . "capitalized letter, one number and one special character " + . "as *[@#$%!,;:+\"'-/{}~=µ()£]). Other characters are allowed." + )) + ) + )) + ; + } + + /** + * @param OptionsResolverInterface $resolver + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'data_classds' => 'Chill\MainBundle\Entity\User' + )); + } + + /** + * @return string + */ + public function getName() + { + return 'chill_mainbundle_user_password'; + } +} diff --git a/Form/UserType.php b/Form/UserType.php new file mode 100644 index 000000000..14bc72b43 --- /dev/null +++ b/Form/UserType.php @@ -0,0 +1,62 @@ +add('username') + ; + if ($options['is_creation']) { + $builder->add('plainPassword', new UserPasswordType(), array( + 'mapped' => false + )); + + } else { + $builder->add($builder + ->create('enabled', 'choice', array( + 'choices' => array( + 0 => 'Disabled, the user is not allowed to login', + 1 => 'Enabled, the user is active' + ), + 'expanded' => false, + 'multiple' => false + )) + ); + } + } + + /** + * @param OptionsResolverInterface $resolver + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'data_class' => 'Chill\MainBundle\Entity\User' + )); + + $resolver + ->setDefaults(array('is_creation' => false)) + ->addAllowedValues(array('is_creation' => array(true, false))) + ; + } + + /** + * @return string + */ + public function getName() + { + return 'chill_mainbundle_user'; + } +} diff --git a/Resources/config/doctrine/GroupCenter.orm.yml b/Resources/config/doctrine/GroupCenter.orm.yml index 37a7a81f5..97ed5978b 100644 --- a/Resources/config/doctrine/GroupCenter.orm.yml +++ b/Resources/config/doctrine/GroupCenter.orm.yml @@ -11,6 +11,5 @@ Chill\MainBundle\Entity\GroupCenter: center: targetEntity: Chill\MainBundle\Entity\Center inversedBy: groupCenters - manyToMany: - permissionGroups: + permissionsGroup: targetEntity: Chill\MainBundle\Entity\PermissionsGroup \ No newline at end of file diff --git a/Resources/config/doctrine/RoleScope.orm.yml b/Resources/config/doctrine/RoleScope.orm.yml index 4ab688b7b..aa308753b 100644 --- a/Resources/config/doctrine/RoleScope.orm.yml +++ b/Resources/config/doctrine/RoleScope.orm.yml @@ -14,4 +14,5 @@ Chill\MainBundle\Entity\RoleScope: manyToOne: scope: targetEntity: Chill\MainBundle\Entity\Scope - inversedBy: roleScopes \ No newline at end of file + inversedBy: roleScopes + nullable: true \ No newline at end of file diff --git a/Resources/config/routing.yml b/Resources/config/routing.yml index 438c7cccf..b10e69342 100644 --- a/Resources/config/routing.yml +++ b/Resources/config/routing.yml @@ -1,3 +1,19 @@ +chill_main_admin_permissionsgroup: + resource: "@ChillMainBundle/Resources/config/routing/permissionsgroup.yml" + prefix: "{_locale}/admin/permissionsgroup" + +chill_main_admin_user: + resource: "@ChillMainBundle/Resources/config/routing/user.yml" + prefix: "{_locale}/admin/user" + +chill_main_admin_scope: + resource: "@ChillMainBundle/Resources/config/routing/scope.yml" + prefix: "{_locale}/admin/scope" + +chill_main_admin: + resource: "@ChillMainBundle/Resources/config/routing/center.yml" + prefix: "{_locale}/admin/center" + root: path: / defaults: @@ -38,6 +54,18 @@ chill_main_admin_central: order: 30 label: Admin Menu icons: [gears] + admin_permissions: + order: 0 + label: Main admin menu + +chill_main_admin_permissions: + path: /{_locale}/admin/permissions + defaults: {_controller: ChillMainBundle:Admin:indexPermissions } + options: + menus: + admin: + order: 200 + label: Permissions chill_main_search: path: /{_locale}/search diff --git a/Resources/config/routing/center.yml b/Resources/config/routing/center.yml new file mode 100644 index 000000000..d70019151 --- /dev/null +++ b/Resources/config/routing/center.yml @@ -0,0 +1,35 @@ +admin_center: + path: / + defaults: { _controller: "ChillMainBundle:Center:index" } + options: + menus: + admin_rpermissions: + order: 100 + label: List centers + +admin_center_show: + path: /{id}/show + defaults: { _controller: "ChillMainBundle:Center:show" } + +admin_center_new: + path: /new + defaults: { _controller: "ChillMainBundle:Center:new" } + options: + menus: + admin_permissions: + order: 101 + label: New center + +admin_center_create: + path: /create + defaults: { _controller: "ChillMainBundle:Center:create" } + methods: POST + +admin_center_edit: + path: /{id}/edit + defaults: { _controller: "ChillMainBundle:Center:edit" } + +admin_center_update: + path: /{id}/update + defaults: { _controller: "ChillMainBundle:Center:update" } + methods: [POST, PUT] diff --git a/Resources/config/routing/permissionsgroup.yml b/Resources/config/routing/permissionsgroup.yml new file mode 100644 index 000000000..ac4c18c55 --- /dev/null +++ b/Resources/config/routing/permissionsgroup.yml @@ -0,0 +1,45 @@ +admin_permissionsgroup: + path: / + defaults: { _controller: "ChillMainBundle:PermissionsGroup:index" } + options: + menus: + admin_permissions: + order: 300 + label: Permissions group list + +admin_permissionsgroup_show: + path: /{id}/show + defaults: { _controller: "ChillMainBundle:PermissionsGroup:show" } + +admin_permissionsgroup_new: + path: /new + defaults: { _controller: "ChillMainBundle:PermissionsGroup:new" } + options: + menus: + admin_permissions: + order: 301 + label: New permission group + +admin_permissionsgroup_create: + path: /create + defaults: { _controller: "ChillMainBundle:PermissionsGroup:create" } + methods: POST + +admin_permissionsgroup_edit: + path: /{id}/edit + defaults: { _controller: "ChillMainBundle:PermissionsGroup:edit" } + +admin_permissionsgroup_update: + path: /{id}/update + defaults: { _controller: "ChillMainBundle:PermissionsGroup:update" } + methods: [POST, PUT] + +admin_permissionsgroup_delete_role_scope: + path: /{pgid}/delete_link_role_scope/{rsid} + defaults: { _controller: "ChillMainBundle:PermissionsGroup:deleteLinkRoleScope" } + methods: [DELETE] + +admin_permissionsgroup_add_role_scope: + path: /{id}/add_link_role_scope + defaults: { _controller: "ChillMainBundle:PermissionsGroup:addLinkRoleScope" } + methods: [PUT] diff --git a/Resources/config/routing/scope.yml b/Resources/config/routing/scope.yml new file mode 100644 index 000000000..dd0d0ec32 --- /dev/null +++ b/Resources/config/routing/scope.yml @@ -0,0 +1,35 @@ +admin_scope: + path: / + defaults: { _controller: "ChillMainBundle:Scope:index" } + options: + menus: + admin_permissions: + order: 200 + label: List circles + +admin_scope_show: + path: /{id}/show + defaults: { _controller: "ChillMainBundle:Scope:show" } + +admin_scope_new: + path: /new + defaults: { _controller: "ChillMainBundle:Scope:new" } + options: + menus: + admin_permissions: + order: 201 + label: New circle + +admin_scope_create: + path: /create + defaults: { _controller: "ChillMainBundle:Scope:create" } + methods: POST + +admin_scope_edit: + path: /{id}/edit + defaults: { _controller: "ChillMainBundle:Scope:edit" } + +admin_scope_update: + path: /{id}/update + defaults: { _controller: "ChillMainBundle:Scope:update" } + methods: [POST, PUT] diff --git a/Resources/config/routing/user.yml b/Resources/config/routing/user.yml new file mode 100644 index 000000000..73050fae3 --- /dev/null +++ b/Resources/config/routing/user.yml @@ -0,0 +1,54 @@ +admin_user: + path: / + defaults: { _controller: "ChillMainBundle:User:index" } + options: + menus: + admin_permissions: + order: 400 + label: List users + +admin_user_show: + path: /{id}/show + defaults: { _controller: "ChillMainBundle:User:show" } + +admin_user_new: + path: /new + defaults: { _controller: "ChillMainBundle:User:new" } + options: + menus: + admin_permissions: + order: 401 + label: Add a new user + +admin_user_create: + path: /create + defaults: { _controller: "ChillMainBundle:User:create" } + methods: POST + +admin_user_edit: + path: /{id}/edit + defaults: { _controller: "ChillMainBundle:User:edit" } + +admin_user_edit_password: + path: /{id}/edit_password + defaults: { _controller: "ChillMainBundle:User:editPassword" } + +admin_user_update: + path: /{id}/update + defaults: { _controller: "ChillMainBundle:User:update" } + methods: [POST, PUT] + +admin_user_update_password: + path: /{id}/update_password + defaults: { _controller: "ChillMainBundle:User:updatePassword" } + methods: [POST, PUT] + +admin_user_delete_group_center: + path: /{uid}/delete_link_groupcenter/{gcid} + defaults: { _controller: "ChillMainBundle:User:deleteLinkGroupCenter" } + methods: [DELETE] + +admin_user_add_group_center: + path: /{uid}/add_link_groupcenter + defaults: { _controller: "ChillMainBundle:User:addLinkGroupCenter" } + methods: [POST] diff --git a/Resources/config/services.yml b/Resources/config/services.yml index a92f6c591..ed3742514 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -107,3 +107,23 @@ services: class: Chill\MainBundle\Security\Authorization\AuthorizationHelper arguments: - "@security.role_hierarchy" + + chill.main.role_provider: + class: Chill\MainBundle\Security\RoleProvider + + chill.main.form.type.composed_role_scope: + class: Chill\MainBundle\Form\Type\ComposedRoleScopeType + arguments: + - "@chill.main.helper.translatable_string" + - "@chill.main.role_provider" + tags: + - { name: form.type, alias: composed_role_scope } + + chill.main.validator.role_scope_scope_presence: + class: Chill\MainBundle\Validation\Validator\RoleScopeScopePresence + arguments: + - "@chill.main.role_provider" + - "@logger" + - "@translator" + tags: + - { name: validator.constraint_validator, alias: 'role_scope_scope_presence' } diff --git a/Resources/config/validation.yml b/Resources/config/validation.yml new file mode 100644 index 000000000..6dc383502 --- /dev/null +++ b/Resources/config/validation.yml @@ -0,0 +1,31 @@ +Chill\MainBundle\Entity\PermissionsGroup: + properties: + name: + - NotBlank: ~ + - Length: + max: 50 + roleScopes: + - Valid: ~ + constraints: + - Callback: [isRoleScopePresentOnce] + +Chill\MainBundle\Entity\User: + properties: + username: + - Length: + max: 70 + min: 3 + constraints: + - Callback: [isGroupCenterPresentOnce] + +Chill\MainBundle\Entity\RoleScope: + constraints: + - \Chill\MainBundle\Validation\Constraint\RoleScopeScopePresenceConstraint: ~ + +Chill\MainBundle\Entity\Center: + properties: + name: + - NotBlank: ~ + - Length: + max: 50 + min: 3 diff --git a/Resources/migrations/Version20150821105642.php b/Resources/migrations/Version20150821105642.php new file mode 100644 index 000000000..211532c93 --- /dev/null +++ b/Resources/migrations/Version20150821105642.php @@ -0,0 +1,133 @@ + GroupCenter + * to + * ManyToOne : a GroupCenter can have only one PermissionGroup + * + * @link https://redmine.champs-libres.coop/issues/578 The issue describing the move + */ +class Version20150821105642 extends AbstractMigration implements + \Symfony\Component\DependencyInjection\ContainerAwareInterface +{ + /** + * + * @var ContainerInterface + */ + private $container; + + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + // this up() migration is auto-generated, please modify it to your needs + $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'postgresql', 'Migration can only be executed safely on \'postgresql\'.'); + + $this->addSql('ALTER TABLE group_centers ADD permissionsGroup_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE group_centers ADD CONSTRAINT FK_A14D8F3D447BBB3B FOREIGN KEY (permissionsGroup_id) REFERENCES permission_groups (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_A14D8F3D447BBB3B ON group_centers (permissionsGroup_id)'); + + } + + public function postUp(Schema $schema) + { + //transform data from groupcenter_permissionsgroup table + $em = $this->container->get('doctrine.orm.entity_manager'); + + //get all existing associations + $rsm = new ResultSetMapping(); + $rsm->addScalarResult('groupcenter_id', 'groupcenter_id'); + $rsm->addScalarResult('permissionsgroup_id', 'permissionsgroup_id'); + + $groupPermissionsAssociations = $em->createNativeQuery( + "SELECT groupcenter_id, permissionsgroup_id " + . "FROM groupcenter_permissionsgroup", + $rsm + ) + ->getScalarResult(); + + //update + foreach ($groupPermissionsAssociations as $groupPermissionAssociation) { + //get the corresponding groupCenter + $rsmGroupCenter = new ResultSetMapping(); + $rsmGroupCenter->addScalarResult('id', 'id'); + $rsmGroupCenter->addScalarResult('permissionsGroup_id', 'permissionsGroup_id'); + $rsmGroupCenter->addScalarResult('center_id', 'center_id'); + + $groupCenters = $em->createNativeQuery("SELECT id, permissionsGroup_id, center_id " + . "FROM group_centers " + . "WHERE id = :groupcenter_id AND permissionsGroup_id IS NULL", + $rsmGroupCenter) + ->setParameter('groupcenter_id', $groupPermissionAssociation['groupcenter_id']) + ->getResult(); + + if (count($groupCenters) === 1) { + // we have to update this group with the current association + $em->getConnection()->executeUpdate("UPDATE group_centers " + . "SET permissionsGroup_id = ? " + . "WHERE id = ?", array( + $groupPermissionAssociation['permissionsgroup_id'], + $groupPermissionAssociation['groupcenter_id']) + ); + } elseif (count($groupCenters) === 0) { + // the association was multiple. We have to create a new group_center + $rsmNewId = new ResultSetMapping(); + $rsmNewId->addScalarResult('new_id', 'new_id'); + $newId = $em->createNativeQuery("select nextval('group_centers_id_seq') as new_id", + $rsmNewId) + ->getSingleScalarResult(); + + $em->getConnection()->insert("group_centers", array( + 'id' => $newId, + 'center_id' => $group_center['center_id'], + 'permissionsGroup_id' => $groupPermissionAssociation['permissionsgroup_id'] + )); + + // we have to link existing users to new created groupcenter + $em->getConnection()->executeQuery('INSERT INTO user_groupcenter ' + . '(user_id, groupcenter_id) SELECT user_id, '.$newId.' ' + . 'FROM user_groupcenter WHERE groupcenter_id = ' + .$groupPermissionAssociation['groupcenter_id']); + } else { + throw new \RuntimeException("Error in the data : we should not have two groupCenter " + . "with the same id !"); + } + } + + + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'postgresql', 'Migration can only be executed safely on \'postgresql\'.'); + + $this->addSql('ALTER TABLE group_centers DROP CONSTRAINT FK_A14D8F3D447BBB3B'); + $this->addSql('DROP INDEX IDX_A14D8F3D447BBB3B'); + $this->addSql('ALTER TABLE group_centers DROP permissionGroup_id'); + + } + + public function setContainer(\Symfony\Component\DependencyInjection\ContainerInterface $container = null) + { + if ($container === NULL) { + throw new \RuntimeException('Container is not provided. This migration ' + . 'need container to set a default center'); + } + + $this->container = $container; + } + +} diff --git a/Resources/migrations/Version20150821122935.php b/Resources/migrations/Version20150821122935.php new file mode 100644 index 000000000..38a4f1b5f --- /dev/null +++ b/Resources/migrations/Version20150821122935.php @@ -0,0 +1,39 @@ +addSql('DROP TABLE groupcenter_permissionsgroup'); + $this->addSql('ALTER TABLE group_centers ALTER permissionsGroup_id SET NOT NULL'); + + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + $this->addSql('ALTER TABLE group_centers ALTER permissionsGroup_id SET DEFAULT NULL'); + $this->addSql('CREATE TABLE groupcenter_permissionsgroup (groupcenter_id INT NOT NULL, permissionsgroup_id INT NOT NULL, PRIMARY KEY(groupcenter_id, permissionsgroup_id))'); + $this->addSql('CREATE INDEX idx_55dfec607ec2fa68 ON groupcenter_permissionsgroup (groupcenter_id)'); + $this->addSql('CREATE INDEX idx_55dfec606fa97d46 ON groupcenter_permissionsgroup (permissionsgroup_id)'); + + $this->addSql('ALTER TABLE groupcenter_permissionsgroup ADD CONSTRAINT fk_55dfec607ec2fa68 FOREIGN KEY (groupcenter_id) REFERENCES group_centers (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE groupcenter_permissionsgroup ADD CONSTRAINT fk_55dfec606fa97d46 FOREIGN KEY (permissionsgroup_id) REFERENCES permission_groups (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + + } +} diff --git a/Resources/translations/messages.fr.yml b/Resources/translations/messages.fr.yml index 8a9c93283..29c37a43f 100644 --- a/Resources/translations/messages.fr.yml +++ b/Resources/translations/messages.fr.yml @@ -6,9 +6,17 @@ Logout: Se déconnecter Bad credentials: Le mot de passe et le nom d'utilisateur ne correspondent pas Invalid CSRF token.: Votre session a expirée ou est devenue invalide. Username: Nom d'utilisateur +username: nom d'utilisateur Password: Mot de passe Welcome to %installation_name%: Bienvenue à %installation_name% Login to %installation_name%: Connexion à %installation_name% +Enabled: activé +Id: identifiant +Homepage: Accueil +Welcome: Bienvenue +Export Menu: Export +Admin Menu: Menu d'administration +Details: Détails #serach Your search is empty. Please provide search terms.: La recherche est vide. Merci de fournir des termes de recherche. @@ -16,4 +24,63 @@ The domain %domain% is unknow. Please check your search.: Le domaine de recherch Invalid terms : Recherche invalide You should not have more than one domain. : Vous ne devriez pas avoir plus d'un domaine de recherche. #used for page title -Search %pattern%: Recherche de "%pattern%" \ No newline at end of file +Search %pattern%: Recherche de "%pattern%" + +#admin +Create: Créer +show: voir +edit: modifier +Main admin menu: Menu d'administration principal +Actions: Actions + +#permissions +Permissions Menu: Gestion des droits +Permissions management of your chill installation: Gestion des permissions de votre instance + +#admin section for center's administration +Create a new center: Créer un nouveau centre +Center list: Liste des centres +Center edit: Édition d'un centre +Center creation: Création d'un centre +New center: Nouveau centre +Center: Centre + +#admin section for permissions group +Permissions group list: Liste des groupes de permissions +Create a new permissions group: Créer un nouveau groupe de permissions +Permission group "%name%": Groupe de permissions "%name%" +Grant those permissions: Attribue ces permissions +Which implies: Ce qui implique +Permission group: Groupe de permissions +Permissionsgroup: Group de permissions +New permission group: Nouveau groupe de permissions +PermissionsGroup "%name%" edit: Modification du groupe de permission '%name%' +Grant new permissions: Attribuer de nouvelles permissions +Role: Rôle +Choose amongst roles: Choisir parmi les rôles +Add permission: Ajouter les permissions +This group does not provide any permission: Ce groupe n'attribue aucune permission + +#admin section for users +List users: Liste des utilisateurs +user list: liste des utilisateurs +User edit: Modification d'un utilisateur +User'status: Statut de l'utilisateur +Disabled, the user is not allowed to login: Désactivé, l'utilisateur n'est pas autorisé à se connecter +Enabled, the user is active: Actif, l'utilisateur peut se connecter +Edit password: Modifier le mot de passe +Permissions granted: Permissions accordées +Grant new permissions: Ajout de permissions +Add a new groupCenter: Ajout de permissions +Center & groups: Centre et groupes +User %username%: Utilisateur %username% +Add a new user: Ajouter un nouvel utilisateur +The permissions have been added: Les permissions ont été ajoutées + +#admin section for circles (old: scopes) +List circles: Liste des cercles +New circle: Nouveau cercle +Circle: Cercle +Circle edit: Modification du cercle +Circle creation: Création d'un cercle +Create a new circle: Créer un nouveau cercle diff --git a/Resources/translations/validators.fr.yml b/Resources/translations/validators.fr.yml new file mode 100644 index 000000000..fe2796f60 --- /dev/null +++ b/Resources/translations/validators.fr.yml @@ -0,0 +1,4 @@ +# role_scope constraint +# scope presence +The role "%role%" require to be associated with a scope.: Le rôle "%role%" doit être associé à un cercle. +The role "%role%" should not be associated with a scope.: Le rôle "%role%" ne doit pas être associé à un cercle. \ No newline at end of file diff --git a/Resources/views/Admin/layout_permissions.html.twig b/Resources/views/Admin/layout_permissions.html.twig new file mode 100644 index 000000000..e4bcc6d7f --- /dev/null +++ b/Resources/views/Admin/layout_permissions.html.twig @@ -0,0 +1,31 @@ +{# + * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS, + / + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . +#} + +{% extends "ChillMainBundle::layoutWithVerticalMenu.html.twig" %} + +{% block vertical_menu_content %} + {{ chill_menu('admin_permissions', { + 'layout': 'ChillMainBundle::Menu/admin_permissions.html.twig', + }) }} +{% endblock %} + +{% block layout_wvm_content %} + {% block admin_content %} +

{{ 'Permissions management of your chill installation' |trans }}

+ {% endblock %} +{% endblock %} \ No newline at end of file diff --git a/Resources/views/Center/edit.html.twig b/Resources/views/Center/edit.html.twig new file mode 100644 index 000000000..06c2e6234 --- /dev/null +++ b/Resources/views/Center/edit.html.twig @@ -0,0 +1,20 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'Center edit'|trans }}{% endblock %} + +{% block admin_content -%} +

{{ 'Center edit'|trans }}

+ + {{ form_start(edit_form) }} + {{ form_row(edit_form.name) }} + {{ form_row(edit_form.submit, { 'attr' : { 'class' : 'sc-button green' } } ) }} + {{ form_end(edit_form) }} + + +{% endblock %} diff --git a/Resources/views/Center/index.html.twig b/Resources/views/Center/index.html.twig new file mode 100644 index 000000000..d16a92763 --- /dev/null +++ b/Resources/views/Center/index.html.twig @@ -0,0 +1,41 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'Center list'|trans }}{% endblock %} + +{% block admin_content -%} +

{{ 'Center list'|trans }}

+ + + + + + + + + + {% for entity in entities %} + + + + + {% endfor %} + +
{{ 'Name'|trans }}{{ 'Actions'|trans }}
{{ entity.name }} + +
+ + + {% endblock %} diff --git a/Resources/views/Center/new.html.twig b/Resources/views/Center/new.html.twig new file mode 100644 index 000000000..05220bac9 --- /dev/null +++ b/Resources/views/Center/new.html.twig @@ -0,0 +1,20 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'Center creation'|trans }}{% endblock %} + +{% block admin_content -%} +

{{ 'Center creation'|trans }}

+ + {{ form_start(form) }} + {{ form_row(form.name) }} + {{ form_row(form.submit, { 'attr' : { 'class' : 'sc-button green' } } ) }} + {{ form_end(form) }} + + +{% endblock %} diff --git a/Resources/views/Center/show.html.twig b/Resources/views/Center/show.html.twig new file mode 100644 index 000000000..654a09723 --- /dev/null +++ b/Resources/views/Center/show.html.twig @@ -0,0 +1,29 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'Centre %name%'|trans({ '%name%': entity.name }) }}{% endblock %} + +{% block admin_content -%} +

{{ 'Centre %name%'|trans({ '%name%': entity.name }) }}

+ + + + + + + + +
{{ 'Name'|trans }}{{ entity.name }}
+ + +{% endblock %} diff --git a/Resources/views/Menu/admin_permissions.html.twig b/Resources/views/Menu/admin_permissions.html.twig new file mode 100644 index 000000000..c30d1d1c3 --- /dev/null +++ b/Resources/views/Menu/admin_permissions.html.twig @@ -0,0 +1,20 @@ +{# + * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS, + / + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . +#} + +{% extends "ChillMainBundle::Menu/verticalMenu.html.twig" %} +{% block v_menu_title %}{{ 'Permissions Menu'|trans }}{% endblock %} \ No newline at end of file diff --git a/Resources/views/PermissionsGroup/edit.html.twig b/Resources/views/PermissionsGroup/edit.html.twig new file mode 100644 index 000000000..3a53dd2da --- /dev/null +++ b/Resources/views/PermissionsGroup/edit.html.twig @@ -0,0 +1,78 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'PermissionsGroup "%name%" edit'|trans( { '%name%': entity.name } ) }}{% endblock %} + +{% block admin_content -%} +

{{ 'PermissionsGroup "%name%" edit'|trans( { '%name%': entity.name } ) }}

+ +

{{ 'Details'|trans }}

+ + {{ form_start(edit_form) }} + {{ form_row(edit_form.name) }} + {{ form_row(edit_form.submit, { 'attr': { 'class': 'sc-button green' } } ) }} + {{ form_end(edit_form) }} + +

{{ 'Grant those permissions'|trans }} :

+ + {%- if entity.getRoleScopes|length > 0 -%} + + + + + + + + + + + {% for role_scope in entity.getRoleScopes %} + + + + + + + {% endfor %} + +
{{ 'Role'|trans }}{{ 'Circle'|trans }}{{ 'Actions'|trans }}
+ {{ role_scope.role|trans }} + {% if expanded_roles[role_scope.role]|length > 1 %} +
+ {{ 'Which implies'|trans }} : {% for role in expanded_roles[role_scope.role] %}{{ role }}{% if not loop.last %}, {% endif %}{% endfor %} + {% endif %} +
+ {%- if role_scope.scope is not null -%} + + {{ role_scope.scope.name|localize_translatable_string }} + + {%- else -%} + N/A + {%- endif -%} + + {{ form_start(delete_role_scopes_form[role_scope.id]) }} + {{ form_widget(delete_role_scopes_form[role_scope.id].submit, { 'attr': { 'class': 'sc-button red' } } ) }} + {{ form_end(delete_role_scopes_form[role_scope.id]) }} +
+ + {%- else -%} +

{{ 'This group does not provide any permission'|trans }}

+ {%- endif -%} + +

{{ 'Grant new permissions'|trans }}

+ + {{ form_start(add_role_scopes_form) }} + {{ form_errors(add_role_scopes_form) }} + {{ form_row(add_role_scopes_form.composed_role_scope.role) }} + {{ form_row(add_role_scopes_form.composed_role_scope.scope) }} + {{ form_row(add_role_scopes_form.submit, { 'attr' : { 'class': 'sc-button green' } } ) }} + {{ form_end(add_role_scopes_form) }} + + + +{% endblock %} diff --git a/Resources/views/PermissionsGroup/index.html.twig b/Resources/views/PermissionsGroup/index.html.twig new file mode 100644 index 000000000..d28790177 --- /dev/null +++ b/Resources/views/PermissionsGroup/index.html.twig @@ -0,0 +1,41 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'Permissions group list'|trans }}{% endblock %} + +{% block admin_content -%} +

{{ 'Permissions group list'|trans }}

+ + + + + + + + + + {% for entity in entities %} + + + + + {% endfor %} + +
{{ 'Name'|trans }}{{ 'Actions'|trans }}
{{ entity.name }} + +
+ + + {% endblock %} diff --git a/Resources/views/PermissionsGroup/new.html.twig b/Resources/views/PermissionsGroup/new.html.twig new file mode 100644 index 000000000..e28e60992 --- /dev/null +++ b/Resources/views/PermissionsGroup/new.html.twig @@ -0,0 +1,20 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'New permission group'|trans }}{% endblock %} + +{% block admin_content -%} +

{{ 'New permission group'|trans }}

+ + {{ form_start(form) }} + {{ form_row(form.name) }} + {{ form_row(form.submit, { 'attr': { 'class': 'sc-button green' } } ) }} + {{ form_end(form) }} + + +{% endblock %} diff --git a/Resources/views/PermissionsGroup/show.html.twig b/Resources/views/PermissionsGroup/show.html.twig new file mode 100644 index 000000000..025592f9a --- /dev/null +++ b/Resources/views/PermissionsGroup/show.html.twig @@ -0,0 +1,66 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'Permission group "%name%"'|trans({ '%name%': entity.name }) }}{% endblock %} + +{% block admin_content -%} +

{{ 'Permission group "%name%"'|trans({ '%name%': entity.name }) }}

+ + + + + + + + +
{{ 'Name'|trans }}{{ entity.name }}
+ {% if role_scopes|length > 0 %} +

{{ 'Grant those permissions'|trans }} :

+ + + + + + + + + + {% for role_scope in role_scopes %} + + + + + {% endfor %} + +
{{ 'Role'|trans }}{{ 'Circle'|trans }}
+ {{ role_scope.role|trans }} + {% if expanded_roles[role_scope.role]|length > 1 %} +
+ {{ 'Which implies'|trans }} : {% for role in expanded_roles[role_scope.role] %}{{ role }}{% if not loop.last %}, {% endif %}{% endfor %} + {% endif %} +
{%- if role_scope.scope is not null -%} + {{ role_scope.scope.name|localize_translatable_string }} + {%- else -%} + N/A + {%- endif -%} +
+ + {% else %} + +

{{ 'This group does not provide any permission'|trans }}. + + {{ 'add permissions'|trans|capitalize }}

+ {% endif %} + + +{% endblock %} diff --git a/Resources/views/Scope/edit.html.twig b/Resources/views/Scope/edit.html.twig new file mode 100644 index 000000000..cd9ccb01c --- /dev/null +++ b/Resources/views/Scope/edit.html.twig @@ -0,0 +1,20 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'Circle edit'|trans }}{% endblock %} + +{% block admin_content -%} +

{{ 'Circle edit'|trans }}

+ + {{ form_start(edit_form) }} + {{ form_row(edit_form.name) }} + {{ form_row(edit_form.submit, { 'attr' : { 'class' : 'sc-button green' } } ) }} + {{ form_end(edit_form) }} + + +{% endblock %} diff --git a/Resources/views/Scope/index.html.twig b/Resources/views/Scope/index.html.twig new file mode 100644 index 000000000..b3087da49 --- /dev/null +++ b/Resources/views/Scope/index.html.twig @@ -0,0 +1,41 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'List circles'|trans }}{% endblock %} + +{% block admin_content -%} +

{{ 'List circles'|trans }}

+ + + + + + + + + + {% for entity in entities %} + + + + + {% endfor %} + +
{{ 'Name'|trans }}{{ 'Actions'|trans }}
{{ entity.name|localize_translatable_string }} + +
+ + + {% endblock %} diff --git a/Resources/views/Scope/new.html.twig b/Resources/views/Scope/new.html.twig new file mode 100644 index 000000000..ef3fbe58b --- /dev/null +++ b/Resources/views/Scope/new.html.twig @@ -0,0 +1,20 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'Circle creation'|trans }}{% endblock %} + +{% block admin_content -%} +

{{ 'Circle creation'|trans }}

+ + {{ form_start(form) }} + {{ form_row(form.name) }} + {{ form_row(form.submit, { 'attr' : { 'class' : 'sc-button green' } } ) }} + {{ form_end(form) }} + + +{% endblock %} diff --git a/Resources/views/Scope/show.html.twig b/Resources/views/Scope/show.html.twig new file mode 100644 index 000000000..694ab35a1 --- /dev/null +++ b/Resources/views/Scope/show.html.twig @@ -0,0 +1,29 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'Circle'|trans }}{% endblock %} + +{% block admin_content -%} +

{{ 'Circle'|trans }}

+ + + + + + + + +
{{ 'Name'|trans }}{{ entity.name|localize_translatable_string }}
+ + +{% endblock %} diff --git a/Resources/views/User/edit.html.twig b/Resources/views/User/edit.html.twig new file mode 100644 index 000000000..21ec92f96 --- /dev/null +++ b/Resources/views/User/edit.html.twig @@ -0,0 +1,72 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'User edit'|trans }}{% endblock %} + +{% block admin_content -%} +

{{ 'User edit'|trans }}

+ + {{ form_start(edit_form) }} + + {{ form_row(edit_form.username) }} + {{ form_row(edit_form.enabled, { 'label': "User'status"}) }} + + {{ form_widget(edit_form.submit, { 'attr': { 'class' : 'sc-button green center' } } ) }} + {{ 'Edit password'|trans }} + + {{ form_end(edit_form) }} + +

{{ 'Permissions granted'|trans }}

+ + {% if entity.groupcenters|length > 0 %} + + + + + + + + + + {% for groupcenter in entity.groupcenters %} + + + + + + {% endfor %} + +
{{ 'Permission group'|trans }}{{ 'Center'|trans }} 
+ + {{ groupcenter.permissionsgroup.name }} + + + + {{ groupcenter.center.name }} + + + {{ form_start(delete_groupcenter_form[groupcenter.id]) }} + {{ form_row(delete_groupcenter_form[groupcenter.id].submit, { 'attr': { 'class': 'sc-button red' } } ) }} + {{ form_rest(delete_groupcenter_form[groupcenter.id]) }} + {{ form_end(delete_groupcenter_form[groupcenter.id]) }} +
+ {% else %} +

{{ 'no permissions granted to this user'|trans }}

+ {% endif %} + +

{{ 'Grant new permissions'|trans }}

+ + {{ form_start(add_groupcenter_form) }} + {{ form_row(add_groupcenter_form.composed_groupcenter.center) }} + {{ form_row(add_groupcenter_form.composed_groupcenter.permissionsgroup) }} + {{ form_row(add_groupcenter_form.submit, { 'attr' : { 'class': 'sc-button green' } } ) }} + + {{ form_end(add_groupcenter_form) }} + + +{% endblock %} diff --git a/Resources/views/User/edit_password.html.twig b/Resources/views/User/edit_password.html.twig new file mode 100644 index 000000000..3a54d5126 --- /dev/null +++ b/Resources/views/User/edit_password.html.twig @@ -0,0 +1,22 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'Edit password for %username%'|trans( { '%username%': entity.username } ) }}{% endblock %} + +{% block admin_content -%} +

{{ 'Edit password for %username%'|trans( { '%username%': entity.username } ) }}

+ + {{ form(edit_form) }} + + +{% endblock %} diff --git a/Resources/views/User/index.html.twig b/Resources/views/User/index.html.twig new file mode 100644 index 000000000..948725f70 --- /dev/null +++ b/Resources/views/User/index.html.twig @@ -0,0 +1,41 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'user list'|trans|capitalize }}{% endblock %} + +{% block admin_content -%} +

{{ 'user list'|trans|capitalize }}

+ + + + + + + + + + {% for entity in entities %} + + + + + {% endfor %} + +
{{ 'Username'|trans|capitalize }}{{ 'Actions'|trans|capitalize }}
{{ entity.username }} + +
+ + +{% endblock admin_content %} diff --git a/Resources/views/User/new.html.twig b/Resources/views/User/new.html.twig new file mode 100644 index 000000000..9396d072b --- /dev/null +++ b/Resources/views/User/new.html.twig @@ -0,0 +1,17 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'User creation'|trans }}{% endblock %} + +{% block admin_content -%} +

{{ 'User creation'|trans }}

+ + {{ form(form) }} + + +{% endblock %} diff --git a/Resources/views/User/show.html.twig b/Resources/views/User/show.html.twig new file mode 100644 index 000000000..8fa1401b0 --- /dev/null +++ b/Resources/views/User/show.html.twig @@ -0,0 +1,76 @@ +{% extends 'ChillMainBundle::Admin/layout_permissions.html.twig' %} + +{% block title %}{{ 'User %username%'|trans({ '%username%': entity.username }) }}{% endblock %} + +{% block admin_content -%} +

{{ 'User %username%'|trans({ '%username%': entity.username }) }}

+ + + + + + + + + + + + +
{{ 'Username' }}{{ entity.username }}
{{ "User'status"|trans }} + {%- if entity.enabled -%} + {{ 'Enabled, the user is active'|trans }} + {%- else -%} + {{ 'Disabled, the user is not allowed to login'|trans }} + {%- endif -%} +
+ +

{{ 'Permissions granted'|trans }}

+ + {% if entity.groupcenters|length > 0 %} + + + + + + + + + + {% for groupcenter in entity.groupcenters %} + + + + + {% endfor %} + +
{{ 'Permission group'|trans }}{{ 'Center'|trans }} 
+ + {{ groupcenter.permissionsgroup.name }} + + + + {{ groupcenter.center.name }} + +
+ + {% else %} +

{{ 'The user does not belong to any groupcenter'|trans }}. + + {{ 'Add new group centers'|trans }} + +

+ {% endif %} + + +{% endblock admin_content %} diff --git a/Resources/views/layoutWithVerticalMenu.html.twig b/Resources/views/layoutWithVerticalMenu.html.twig index 0a77ed563..b6863bcb7 100644 --- a/Resources/views/layoutWithVerticalMenu.html.twig +++ b/Resources/views/layoutWithVerticalMenu.html.twig @@ -40,7 +40,7 @@ {% endfor %} - {% for flashMessage in app.session.flashbag.get('danger') %} + {% for flashMessage in app.session.flashbag.get('error') %}
{{ flashMessage|raw }} @@ -48,7 +48,7 @@
{% endfor %} - {% for flashMessage in app.session.flashbag.get('info') %} + {% for flashMessage in app.session.flashbag.get('notice') %}
{{ flashMessage|raw }} diff --git a/Security/Authorization/AuthorizationHelper.php b/Security/Authorization/AuthorizationHelper.php index 881a04af4..92316102d 100644 --- a/Security/Authorization/AuthorizationHelper.php +++ b/Security/Authorization/AuthorizationHelper.php @@ -95,30 +95,29 @@ class AuthorizationHelper foreach ($user->getGroupCenters() as $groupCenter){ //filter on center if ($groupCenter->getCenter()->getId() === $entity->getCenter()->getId()) { - //iterate on permissionGroup - foreach($groupCenter->getPermissionGroups() as $permissionGroup) { - //iterate on roleScopes - foreach($permissionGroup->getRoleScopes() as $roleScope) { - //check that the role allow to reach the required role - if ($this->isRoleReached($role, - new Role($roleScope->getRole()))){ - //if yes, we have a right on something... - // perform check on scope if necessary - if ($entity instanceof HasScopeInterface) { - $scope = $entity->getScope(); - if ($scope === NULL) { - return true; - } - if ($scope->getId() === $roleScope - ->getScope()->getId()) { - return true; - } - } else { + $permissionGroup = $groupCenter->getPermissionsGroup(); + //iterate on roleScopes + foreach($permissionGroup->getRoleScopes() as $roleScope) { + //check that the role allow to reach the required role + if ($this->isRoleReached($role, + new Role($roleScope->getRole()))){ + //if yes, we have a right on something... + // perform check on scope if necessary + if ($entity instanceof HasScopeInterface) { + $scope = $entity->getScope(); + if ($scope === NULL) { return true; } + if ($scope->getId() === $roleScope + ->getScope()->getId()) { + return true; + } + } else { + return true; } } } + } } @@ -139,25 +138,24 @@ class AuthorizationHelper $centers = array(); foreach ($user->getGroupCenters() as $groupCenter){ - //iterate on permissionGroup - foreach($groupCenter->getPermissionGroups() as $permissionGroup) { - //iterate on roleScopes - foreach($permissionGroup->getRoleScopes() as $roleScope) { - //check that the role is in the reachable roles - if ($this->isRoleReached($role, - new Role($roleScope->getRole()))) { - if ($scope === null) { + $permissionGroup = $groupCenter->getPermissionsGroup(); + //iterate on roleScopes + foreach($permissionGroup->getRoleScopes() as $roleScope) { + //check that the role is in the reachable roles + if ($this->isRoleReached($role, + new Role($roleScope->getRole()))) { + if ($scope === null) { + $centers[] = $groupCenter->getCenter(); + break 1; + } else { + if ($scope->getId() == $roleScope->getScope()->getId()){ $centers[] = $groupCenter->getCenter(); - break 2; - } else { - if ($scope->getId() == $roleScope->getScope()->getId()){ - $centers[] = $groupCenter->getCenter(); - break 2; - } - } + break 1; + } } } } + } return $centers; @@ -178,15 +176,14 @@ class AuthorizationHelper foreach ($user->getGroupCenters() as $groupCenter){ if ($center->getId() === $groupCenter->getCenter()->getId()) { //iterate on permissionGroup - foreach($groupCenter->getPermissionGroups() as $permissionGroup) { - //iterate on roleScopes - foreach($permissionGroup->getRoleScopes() as $roleScope) { - //check that the role is in the reachable roles - if ($this->isRoleReached($role, - new Role($roleScope->getRole()))) { + $permissionGroup = $groupCenter->getPermissionsGroup(); + //iterate on roleScopes + foreach($permissionGroup->getRoleScopes() as $roleScope) { + //check that the role is in the reachable roles + if ($this->isRoleReached($role, + new Role($roleScope->getRole()))) { - $scopes[] = $roleScope->getScope(); - } + $scopes[] = $roleScope->getScope(); } } } diff --git a/Security/ProvideRoleInterface.php b/Security/ProvideRoleInterface.php new file mode 100644 index 000000000..08a48181a --- /dev/null +++ b/Security/ProvideRoleInterface.php @@ -0,0 +1,53 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace Chill\MainBundle\Security; + +/** + * Declare role + * + * The role are added to the configuration at compile time. + * + * The implemented object must be declared as a service and tagged as + * + *
+ * my_role_declaration:
+ *    # ...
+ *    tags:
+ *       - { name: chill.role }
+ * 
+ * + * @author Julien Fastré + */ +interface ProvideRoleInterface +{ + /** + * return an array of role provided by the object + * + * @return string[] array of roles (as string) + */ + public function getRoles(); + + /** + * return roles which doesn't need + * + * @return string[] array of roles without scopes + */ + public function getRolesWithoutScope(); +} diff --git a/Security/RoleProvider.php b/Security/RoleProvider.php new file mode 100644 index 000000000..e206e64b4 --- /dev/null +++ b/Security/RoleProvider.php @@ -0,0 +1,78 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace Chill\MainBundle\Security; + +/** + * + * + * @author Julien Fastré + */ +class RoleProvider +{ + /** + * + * @var ProvideRoleInterface[] + */ + private $providers = array(); + + /** + * Add a role provider + * + * @internal This function is called by the dependency injector: it inject provider + * @param \Chill\MainBundle\Security\ProvideRoleInterface $provider + */ + public function addProvider(ProvideRoleInterface $provider) + { + $this->providers[] = $provider; + } + + /** + * + * @return string[] the roles as string + */ + public function getRoles() + { + $roles = array(); + foreach ($this->providers as $provider) { + if ($provider->getRoles() !== NULL) { + $roles = array_merge($roles, $provider->getRoles()); + } + } + + return $roles; + } + + /** + * + * @return string[] the roles as string + */ + public function getRolesWithoutScopes() + { + $roles = array(); + foreach ($this->providers as $provider) { + if ($provider->getRolesWithoutScope() !== NULL) { + $roles = array_merge($roles, $provider->getRolesWithoutScope()); + } + } + + return $roles; + } + +} diff --git a/Test/PrepareUserTrait.php b/Test/PrepareUserTrait.php index 83a18d2b2..697e28f5b 100644 --- a/Test/PrepareUserTrait.php +++ b/Test/PrepareUserTrait.php @@ -68,6 +68,7 @@ trait PrepareUserTrait $groupCenter = (new GroupCenter()) ->setCenter($permission['center']); $permissionGroup = new PermissionsGroup(); + foreach ($permission['permissionsGroup'] as $pg) { $roleScope = (new RoleScope()) @@ -75,11 +76,14 @@ trait PrepareUserTrait ->setScope($pg['scope']); ; $permissionGroup->addRoleScope($roleScope); - $groupCenter->addPermissionGroup($permissionGroup); + } + + $groupCenter->setPermissionsGroup($permissionGroup); $user->addGroupCenter($groupCenter); - } - + + } + return $user; } } diff --git a/Tests/Controller/CenterControllerTest.php b/Tests/Controller/CenterControllerTest.php new file mode 100644 index 000000000..4bc8521a0 --- /dev/null +++ b/Tests/Controller/CenterControllerTest.php @@ -0,0 +1,55 @@ + 'admin', + 'PHP_AUTH_PW' => 'password', + )); + + // Create a new entry in the database + $crawler = $client->request('GET', '/fr/admin/center/'); + $this->assertEquals(200, $client->getResponse()->getStatusCode(), + "Unexpected HTTP status code for GET /fr/admin/center/"); + $crawler = $client->click($crawler->selectLink('Créer un nouveau centre')->link()); + + // Fill in the form and submit it + $form = $crawler->selectButton('Créer')->form(array( + 'chill_mainbundle_center[name]' => 'Test center', + )); + + $client->submit($form); + $crawler = $client->followRedirect(); + + // Check data in the show view + $this->assertGreaterThan(0, + $crawler->filter('td:contains("Test center")')->count(), + 'Missing element td:contains("Test center")'); + + // Edit the entity + $crawler = $client->click($crawler->selectLink('Edit')->link()); + + $form = $crawler->selectButton('Update')->form(array( + 'chill_mainbundle_center[name]' => 'Foo', + )); + + $client->submit($form); + $crawler = $client->followRedirect(); + + // Check the element contains an attribute with value equals "Foo" + $this->assertGreaterThan(0, $crawler->filter('[value="Foo"]')->count(), + 'Missing element [value="Foo"]'); + + $crawler = $client->request('GET', '/fr/admin/center/'); + + // Check the entity has been delete on the list + $this->assertRegExp('/Foo/', $client->getResponse()->getContent()); + } +} diff --git a/Tests/Controller/PermissionsGroupControllerTest.php b/Tests/Controller/PermissionsGroupControllerTest.php new file mode 100644 index 000000000..9657c4800 --- /dev/null +++ b/Tests/Controller/PermissionsGroupControllerTest.php @@ -0,0 +1,59 @@ +markTestSkipped(); + } + /* + public function testCompleteScenario() + { + // Create a new client to browse the application + $client = static::createClient(); + + // Create a new entry in the database + $crawler = $client->request('GET', '/admin/permissionsgroup/'); + $this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code for GET /admin/permissionsgroup/"); + $crawler = $client->click($crawler->selectLink('Create a new entry')->link()); + + // Fill in the form and submit it + $form = $crawler->selectButton('Create')->form(array( + 'chill_mainbundle_permissionsgroup[field_name]' => 'Test', + // ... other fields to fill + )); + + $client->submit($form); + $crawler = $client->followRedirect(); + + // Check data in the show view + $this->assertGreaterThan(0, $crawler->filter('td:contains("Test")')->count(), 'Missing element td:contains("Test")'); + + // Edit the entity + $crawler = $client->click($crawler->selectLink('Edit')->link()); + + $form = $crawler->selectButton('Update')->form(array( + 'chill_mainbundle_permissionsgroup[field_name]' => 'Foo', + // ... other fields to fill + )); + + $client->submit($form); + $crawler = $client->followRedirect(); + + // Check the element contains an attribute with value equals "Foo" + $this->assertGreaterThan(0, $crawler->filter('[value="Foo"]')->count(), 'Missing element [value="Foo"]'); + + // Delete the entity + $client->submit($crawler->selectButton('Delete')->form()); + $crawler = $client->followRedirect(); + + // Check the entity has been delete on the list + $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); + } + + */ +} diff --git a/Tests/Controller/ScopeControllerTest.php b/Tests/Controller/ScopeControllerTest.php new file mode 100644 index 000000000..73244c70c --- /dev/null +++ b/Tests/Controller/ScopeControllerTest.php @@ -0,0 +1,59 @@ + 'admin', + 'PHP_AUTH_PW' => 'password', + )); + + // Create a new entry in the database + $crawler = $client->request('GET', '/fr/admin/scope/'); + $this->assertEquals(200, $client->getResponse()->getStatusCode(), + "Unexpected HTTP status code for GET /fr/admin/scope/"); + $crawler = $client->click($crawler->selectLink('Créer un nouveau cercle')->link()); + // Fill in the form and submit it + $form = $crawler->selectButton('Créer')->form(array( + 'chill_mainbundle_scope[name][fr]' => 'Test en fr', + 'chill_mainbundle_scope[name][en]' => 'Test en en' + )); + + $client->submit($form/*, array( + 'chill_mainbundle_scope' => array( + 'name' => array( + 'fr' => 'test en fr', + 'en' => 'test in english', + 'nl' => 'test in nl' + ) + ) + )*/); + $crawler = $client->followRedirect(); + + // Check data in the show view + $this->assertGreaterThan(0, $crawler->filter('td:contains("Test en fr")')->count(), + 'Missing element td:contains("Test en fr")'); + + // Edit the entity + $crawler = $client->click($crawler->selectLink('Edit')->link()); + + $form = $crawler->selectButton('Update')->form(array( + 'chill_mainbundle_scope[name][fr]' => 'Foo', + 'chill_mainbundle_scope[name][en]' => 'Foo en', + )); + + $client->submit($form); + $crawler = $client->followRedirect(); + + // Check the element contains an attribute with value equals "Foo" + $this->assertGreaterThan(0, $crawler->filter('[value="Foo"]')->count(), 'Missing element [value="Foo"]'); + + } + +} diff --git a/Tests/Controller/UserControllerTest.php b/Tests/Controller/UserControllerTest.php new file mode 100644 index 000000000..d856cb2da --- /dev/null +++ b/Tests/Controller/UserControllerTest.php @@ -0,0 +1,59 @@ +markTestSkipped(); + } + /* + public function testCompleteScenario() + { + // Create a new client to browse the application + $client = static::createClient(); + + // Create a new entry in the database + $crawler = $client->request('GET', '/admin/user/'); + $this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code for GET /admin/user/"); + $crawler = $client->click($crawler->selectLink('Create a new entry')->link()); + + // Fill in the form and submit it + $form = $crawler->selectButton('Create')->form(array( + 'chill_mainbundle_user[field_name]' => 'Test', + // ... other fields to fill + )); + + $client->submit($form); + $crawler = $client->followRedirect(); + + // Check data in the show view + $this->assertGreaterThan(0, $crawler->filter('td:contains("Test")')->count(), 'Missing element td:contains("Test")'); + + // Edit the entity + $crawler = $client->click($crawler->selectLink('Edit')->link()); + + $form = $crawler->selectButton('Update')->form(array( + 'chill_mainbundle_user[field_name]' => 'Foo', + // ... other fields to fill + )); + + $client->submit($form); + $crawler = $client->followRedirect(); + + // Check the element contains an attribute with value equals "Foo" + $this->assertGreaterThan(0, $crawler->filter('[value="Foo"]')->count(), 'Missing element [value="Foo"]'); + + // Delete the entity + $client->submit($crawler->selectButton('Delete')->form()); + $crawler = $client->followRedirect(); + + // Check the entity has been delete on the list + $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); + } + + */ +} diff --git a/Tests/Fixtures/App/AppKernel.php b/Tests/Fixtures/App/AppKernel.php index 16f21e1f5..4b9643491 100644 --- a/Tests/Fixtures/App/AppKernel.php +++ b/Tests/Fixtures/App/AppKernel.php @@ -15,7 +15,8 @@ class AppKernel extends Kernel new \Symfony\Bundle\AsseticBundle\AsseticBundle(), new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(), new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(), - new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle() + new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle(), + new Symfony\Bundle\MonologBundle\MonologBundle(), ); } diff --git a/Tests/Fixtures/App/config/config_test.yml b/Tests/Fixtures/App/config/config_test.yml index 18856b3ac..a8a895244 100644 --- a/Tests/Fixtures/App/config/config_test.yml +++ b/Tests/Fixtures/App/config/config_test.yml @@ -10,6 +10,13 @@ security: role_hierarchy: CHILL_MASTER_ROLE: [CHILL_INHERITED_ROLE_1] providers: + chain_provider: + chain : + providers: [in_memory, users] + in_memory: + memory: + users: + admin: { password: "password", roles: 'ROLE_ADMIN' } users: entity: class: Chill\MainBundle\Entity\User @@ -18,6 +25,8 @@ security: encoders: Chill\MainBundle\Entity\User: algorithm: bcrypt + Symfony\Component\Security\Core\User\User: + algorithm: plaintext firewalls: dev: @@ -39,5 +48,5 @@ security: access_control: - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } - - { path: ^/admin, roles: ROLE_ADMIN } + - { path: ^/[a-z]*/admin, roles: ROLE_ADMIN } - { path: ^/, roles: ROLE_USER } \ No newline at end of file diff --git a/Validation/Constraint/RoleScopeScopePresenceConstraint.php b/Validation/Constraint/RoleScopeScopePresenceConstraint.php new file mode 100644 index 000000000..9f9b6381b --- /dev/null +++ b/Validation/Constraint/RoleScopeScopePresenceConstraint.php @@ -0,0 +1,46 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace Chill\MainBundle\Validation\Constraint; + +use Symfony\Component\Validator\Constraint; + +/** + * Check that a role scope has a scope if required + * + * @author Julien Fastré + */ +class RoleScopeScopePresenceConstraint extends Constraint +{ + + public $messagePresenceRequired = "The role \"%role%\" require to be associated with " + . "a scope."; + public $messageNullRequired = "The role \"%role%\" should not be associated with a scope."; + + public function validatedBy() + { + return 'role_scope_scope_presence'; + } + + public function getTargets() + { + return self::CLASS_CONSTRAINT; + } + +} diff --git a/Validation/Validator/RoleScopeScopePresence.php b/Validation/Validator/RoleScopeScopePresence.php new file mode 100644 index 000000000..e6681738a --- /dev/null +++ b/Validation/Validator/RoleScopeScopePresence.php @@ -0,0 +1,102 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace Chill\MainBundle\Validation\Validator; + +use Chill\MainBundle\Security\RoleProvider; +use Chill\MainBundle\Entity\RoleScope; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Chill\MainBundle\Validation\Constraint\RoleScopeScopePresenceConstraint; +use Psr\Log\LoggerInterface; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * + * + * @author Julien Fastré + */ +class RoleScopeScopePresence extends ConstraintValidator +{ + /** + * + * @var RoleProvider + */ + private $roleProvider; + + /** + * + * @var LoggerInterface + */ + private $logger; + + /** + * + * @var TranslatorInterface + */ + private $translator; + + public function __construct(RoleProvider $roleProvider, LoggerInterface $logger, + TranslatorInterface $translator) + { + $this->roleProvider = $roleProvider; + $this->logger = $logger; + $this->translator = $translator; + } + + public function validate($value, Constraint $constraint) + { + if (! $value instanceof RoleScope) { + throw new \RuntimeException('The validated object is not an instance of roleScope'); + } + + if (! $constraint instanceof RoleScopeScopePresenceConstraint) { + throw new \RuntimeException('This validator should be used with RoleScopScopePresenceConstraint'); + } + + $this->logger->debug('begin validation of a role scope instance'); + + //if the role scope should have a scope + if ( + !in_array($value->getRole(), $this->roleProvider->getRolesWithoutScopes()) + && + $value->getScope() === NULL + ) { + $this->context->buildViolation($constraint->messagePresenceRequired) + ->setParameter('%role%', $this->translator->trans($value->getRole())) + ->addViolation(); + $this->logger->debug('the role scope should have a scope, but scope is null. Violation build.'); + } elseif // if the scope should be null + ( + in_array($value->getRole(), $this->roleProvider->getRolesWithoutScopes()) + && + ! is_null($value->getScope()) + ) + { + $this->context->buildViolation($constraint->messageNullRequired) + ->setParameter('%role%', $this->translator->trans($value->getRole())) + ->addViolation(); + $this->logger->debug('the role scole should not have a scope, but scope is not null. Violation build.'); + } // everything is fine ! + else { + $this->logger->debug('role scope is valid. Validation finished.'); + } + } + +}