Document: Fix folder visibility (WIP)

pull/3432/head
Julio Montoya 5 years ago
parent dba5cafde8
commit 57eded82c0
  1. 2
      src/CoreBundle/Controller/Api/CreateResourceNodeFileAction.php
  2. 9
      src/CoreBundle/Controller/Api/UpdateResourceNodeFileAction.php
  3. 2
      src/CoreBundle/Controller/ResourceController.php
  4. 8
      src/CoreBundle/DataProvider/Extension/CDocumentExtension.php
  5. 3
      src/CoreBundle/Entity/Listener/ResourceListener.php
  6. 6
      src/CoreBundle/Entity/Listener/ResourceNodeListener.php
  7. 1
      src/CoreBundle/EventListener/CourseListener.php
  8. 37
      src/CoreBundle/Repository/ResourceRepository.php
  9. 76
      src/CoreBundle/Security/Authorization/Voter/ResourceNodeVoter.php
  10. 83
      src/CoreBundle/Security/Authorization/Voter/ResourceVoter.php
  11. 4
      tests/behat/features/toolDocument.feature

@ -18,7 +18,7 @@ class CreateResourceNodeFileAction
$contentData = $request->getContent();
if (!empty($contentData)) {
$contentData = json_decode($contentData, true);
error_log(print_r($contentData, 1));
//error_log(print_r($contentData, 1));
$title = $contentData['title'];
$comment = $contentData['comment'];
} else {

@ -43,8 +43,10 @@ class UpdateResourceNodeFileAction
$document->setResourceNode($resourceNode);
}
$link = null;
if (!empty($resourceLinkList)) {
foreach ($resourceLinkList as $linkArray) {
// Find the exact link.
$linkId = $linkArray['id'];
/** @var ResourceLink $link */
$link = $document->getResourceNode()->getResourceLinks()
@ -56,10 +58,17 @@ class UpdateResourceNodeFileAction
if (null !== $link) {
$link->setVisibility((int) $linkArray['visibility']);
break;
}
}
}
$isRecursive = 'folder' === $fileType;
// If it's a folder then change the visibility to the children (That have the same link).
if ($isRecursive && null !== $link) {
$repo->copyVisibilityToChildren($document->getResourceNode(), $link);
}
$document->setComment($comment);
error_log('Finish update resource node file action');

@ -46,7 +46,7 @@ class ResourceController extends AbstractResourceController implements CourseCon
use ResourceControllerTrait;
use ControllerTrait;
private $fileContentName = 'file_content';
private string $fileContentName = 'file_content';
/**
* @deprecated in favor of vue CRUD methods

@ -7,14 +7,14 @@ declare(strict_types=1);
namespace Chamilo\CoreBundle\DataProvider\Extension;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
//use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use Chamilo\CoreBundle\Entity\ResourceLink;
use Chamilo\CourseBundle\Entity\CDocument;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Security\Core\Security;
final class CDocumentExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
final class CDocumentExtension implements QueryCollectionExtensionInterface //, QueryItemExtensionInterface
{
private $security;
@ -29,11 +29,11 @@ final class CDocumentExtension implements QueryCollectionExtensionInterface, Que
$this->addWhere($queryBuilder, $resourceClass);
}
public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, string $operationName = null, array $context = []): void
/*public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, string $operationName = null, array $context = []): void
{
error_log('applyToItem');
$this->addWhere($queryBuilder, $resourceClass);
}
}*/
private function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
{

@ -25,9 +25,6 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Security;
/**
* Class ResourceListener.
*/
class ResourceListener
{
protected $slugify;

@ -13,9 +13,6 @@ use Doctrine\ORM\Event\PreUpdateEventArgs;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Security;
/**
* Class ResourceNodeListener.
*/
class ResourceNodeListener
{
protected $slugify;
@ -23,9 +20,6 @@ class ResourceNodeListener
protected $accessUrl;
protected $resourceNodeRepository;
/**
* ResourceListener constructor.
*/
public function __construct(
SlugifyInterface $slugify,
ToolChain $toolChain,

@ -76,7 +76,6 @@ class CourseListener
// Check if URL has cid value. Using Symfony request.
$courseId = (int) $request->get('cid');
error_log($courseId);
$checker = $container->get('security.authorization_checker');
//dump("cid value in request: $courseId");

@ -941,4 +941,41 @@ abstract class ResourceRepository extends ServiceEntityRepository
return true;
}
/**
* Changes the visibility of the children that matches the exact same link.
*/
public function copyVisibilityToChildren(ResourceNode $resourceNode, ResourceLink $link): bool
{
$children = $resourceNode->getChildren();
if (0 === $children->count()) {
return false;
}
$em = $this->getEntityManager();
/** @var ResourceNode $child */
foreach ($children as $child) {
if ($child->getChildren()->count() > 0) {
$this->copyVisibilityToChildren($child, $link);
}
$links = $child->getResourceLinks();
foreach ($links as $linkItem) {
if ($linkItem->getUser() === $link->getUser() &&
$linkItem->getSession() === $link->getSession() &&
$linkItem->getCourse() === $link->getCourse() &&
$linkItem->getUserGroup() === $link->getUserGroup()
) {
$linkItem->setVisibility($link->getVisibility());
$em->persist($linkItem);
}
}
}
$em->flush();
return true;
}
}

@ -11,7 +11,7 @@ use Chamilo\CoreBundle\Entity\ResourceRight;
use Chamilo\CoreBundle\Entity\Session;
use Chamilo\CourseBundle\Entity\CGroup;
use Laminas\Permissions\Acl\Acl;
use Laminas\Permissions\Acl\Resource\GenericResource as SecurityResource;
use Laminas\Permissions\Acl\Resource\GenericResource;
use Laminas\Permissions\Acl\Role\GenericRole as Role;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Acl\Permission\MaskBuilder;
@ -76,6 +76,7 @@ class ResourceNodeVoter extends Voter
self::EXPORT,
];
error_log('resourceNode supports');
// if the attribute isn't one we support, return false
if (!in_array($attribute, $options)) {
return false;
@ -91,6 +92,7 @@ class ResourceNodeVoter extends Voter
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
error_log('resourceNode voteOnAttribute');
$user = $token->getUser();
// Make sure there is a user object (i.e. that the user is logged in)
@ -121,7 +123,6 @@ class ResourceNodeVoter extends Voter
// @todo
switch ($attribute) {
case self::VIEW:
break;
case self::EDIT:
break;
}
@ -240,7 +241,8 @@ class ResourceNodeVoter extends Voter
break;
}*/
}
//var_dump($linkFound, $link->getId());
//var_dump($linkFound, $link->getId()); exit;
// No link was found or not available.
if (0 === $linkFound) {
return false;
@ -266,18 +268,27 @@ class ResourceNodeVoter extends Voter
$editorMask = self::getEditorMask();
if ($courseId) {
$resourceRight = new ResourceRight();
$resourceRight
->setMask($editorMask)
->setRole(self::ROLE_CURRENT_COURSE_TEACHER);
$rights[] = $resourceRight;
// If is teacher.
if ($this->security->isGranted(self::ROLE_CURRENT_COURSE_TEACHER)) {
$resourceRight = new ResourceRight();
$resourceRight
->setMask($editorMask)
->setRole(self::ROLE_CURRENT_COURSE_TEACHER);
$rights[] = $resourceRight;
}
$resourceRight = new ResourceRight();
$resourceRight
->setMask($readerMask)
->setRole(self::ROLE_CURRENT_COURSE_STUDENT);
$rights[] = $resourceRight;
// If is student.
if ($this->security->isGranted(self::ROLE_CURRENT_COURSE_STUDENT) &&
ResourceLink::VISIBILITY_PUBLISHED === $link->getVisibility()
) {
$resourceRight = new ResourceRight();
$resourceRight
->setMask($readerMask)
->setRole(self::ROLE_CURRENT_COURSE_STUDENT);
$rights[] = $resourceRight;
}
// For everyone.
if (ResourceLink::VISIBILITY_PUBLISHED === $link->getVisibility() && $link->getCourse()->isPublic()) {
$allowAnonsToSee = true;
$resourceRight = new ResourceRight();
@ -331,14 +342,18 @@ class ResourceNodeVoter extends Voter
}
}
//var_dump($allowAnonsToSee);
/*foreach ($rights as $right) {
var_dump($right->getRole());
}*/
//exit;
// Asked mask
$mask = new MaskBuilder();
$mask->add($attribute);
$askedMask = $mask->get();
// Setting Simple ACL.
$acl = new Acl();
// Creating roles
// @todo move this in a service
$anon = new Role('IS_AUTHENTICATED_ANONYMOUSLY');
@ -358,7 +373,8 @@ class ResourceNodeVoter extends Voter
$superAdmin = new Role('ROLE_SUPER_ADMIN');
$admin = new Role('ROLE_ADMIN');
// Adding roles to the ACL.
// Setting Simple ACL.
$acl = new Acl();
$acl
->addRole($anon)
->addRole($userRole)
@ -379,8 +395,8 @@ class ResourceNodeVoter extends Voter
;
// Add a security resource.
$securityResource = new SecurityResource($link);
$acl->addResource($securityResource);
$linkId = $link->getId();
$acl->addResource(new GenericResource($linkId));
// Check all the right this link has.
// Set rights from the ResourceRight.
@ -388,32 +404,31 @@ class ResourceNodeVoter extends Voter
$acl->allow($right->getRole(), null, $right->getMask());
}
// var_dump($askedMask, $roles);
// Role and permissions settings
// Student can just view (read)
$acl->allow($student, null, self::getReaderMask());
// Anons can see.
if ($allowAnonsToSee) {
$acl->allow($anon, null, self::getReaderMask());
}
//$acl->allow($student, null, self::getReaderMask());
// Teacher can view/edit
$acl->allow(
/*$acl->allow(
$teacher,
null,
[
self::getReaderMask(),
self::getEditorMask(),
]
);
);*/
// Anons can see.
if ($allowAnonsToSee) {
$acl->allow($anon, null, self::getReaderMask());
}
// Admin can do everything
$acl->allow($admin);
$acl->allow($superAdmin);
if ($token instanceof AnonymousToken) {
if ($acl->isAllowed('IS_AUTHENTICATED_ANONYMOUSLY', $securityResource, $askedMask)) {
if ($acl->isAllowed('IS_AUTHENTICATED_ANONYMOUSLY', $linkId, $askedMask)) {
return true;
}
@ -421,7 +436,8 @@ class ResourceNodeVoter extends Voter
}
foreach ($user->getRoles() as $role) {
if ($acl->isAllowed($role, $securityResource, $askedMask)) {
//var_dump($role, $acl->isAllowed($role, $linkId, $askedMask));
if ($acl->isAllowed($role, $linkId, $askedMask)) {
return true;
}
}

@ -0,0 +1,83 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Security\Authorization\Voter;
use Chamilo\CoreBundle\Entity\AbstractResource;
use Chamilo\CoreBundle\Entity\ResourceNode;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Acl\Permission\MaskBuilder;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Security;
class ResourceVoter extends Voter
{
public const VIEW = 'VIEW';
public const CREATE = 'CREATE';
public const EDIT = 'EDIT';
public const DELETE = 'DELETE';
public const EXPORT = 'EXPORT';
private $requestStack;
private $security;
public function __construct(Security $security, RequestStack $requestStack)
{
$this->security = $security;
$this->requestStack = $requestStack;
}
public static function getReaderMask(): int
{
$builder = new MaskBuilder();
$builder
->add(self::VIEW)
;
return $builder->get();
}
public static function getEditorMask(): int
{
$builder = new MaskBuilder();
$builder
->add(self::VIEW)
->add(self::EDIT)
;
return $builder->get();
}
protected function supports(string $attribute, $subject): bool
{
$options = [
self::VIEW,
self::CREATE,
self::EDIT,
self::DELETE,
self::EXPORT,
];
error_log('resource supports');
// if the attribute isn't one we support, return false
if (!in_array($attribute, $options)) {
return false;
}
// only vote on ResourceNode objects inside this voter
if (!$subject instanceof AbstractResource) {
return false;
}
return true;
}
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
error_log('resource voteOnAttribute');
$user = $token->getUser();
return true;
}
}

@ -102,8 +102,8 @@ Feature: Document tool
And I press "Yes"
And wait for the page to be loaded
Then I should see "Deleted"
And wait for the page to be loaded
Then I should not see "My first document"
# And wait for the page to be loaded
# Then I should not see "My first document"
# Scenario: Delete simple document
# Then I follow "document"

Loading…
Cancel
Save