Completed
Push — master ( 06c1ce...67d37c )
by Jeroen
06:20
created

Helper/Security/Acl/AclNativeHelper.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Kunstmaan\AdminBundle\Helper\Security\Acl;
4
5
use Doctrine\DBAL\Query\QueryBuilder;
6
use Doctrine\ORM\EntityManager;
7
use Kunstmaan\AdminBundle\Helper\Security\Acl\Permission\MaskBuilder;
8
use Kunstmaan\AdminBundle\Helper\Security\Acl\Permission\PermissionDefinition;
9
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
10
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
11
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
12
13
/**
14
 * AclHelper is a helper class to help setting the permissions when querying using native queries
15
 *
16
 * @see https://gist.github.com/1363377
17
 */
18
class AclNativeHelper
19
{
20
    /**
21
     * @var EntityManager
22
     */
23
    private $em = null;
24
25
    /**
26
     * @var TokenStorageInterface
27
     */
28
    private $tokenStorage = null;
29
30
    /**
31
     * @var RoleHierarchyInterface
32
     */
33
    private $roleHierarchy = null;
34
35
    /**
36
     * @var bool
37
     */
38
    private $permissionsEnabled;
39
40
    /**
41
     * Constructor.
42
     *
43
     * @param EntityManager          $em           The entity manager
44
     * @param TokenStorageInterface  $tokenStorage The security context
45
     * @param RoleHierarchyInterface $rh           The role hierarchies
46
     */
47 3 View Code Duplication
    public function __construct(EntityManager $em, TokenStorageInterface $tokenStorage, RoleHierarchyInterface $rh, $permissionsEnabled = true)
48
    {
49 3
        $this->em = $em;
50 3
        $this->tokenStorage = $tokenStorage;
51 3
        $this->roleHierarchy = $rh;
52 3
        $this->permissionsEnabled = $permissionsEnabled;
53 3
    }
54
55
    /**
56
     * Apply the ACL constraints to the specified query builder, using the permission definition
57
     *
58
     * @param QueryBuilder         $queryBuilder  The query builder
59
     * @param PermissionDefinition $permissionDef The permission definition
60
     *
61
     * @return QueryBuilder
62
     */
63 2
    public function apply(QueryBuilder $queryBuilder, PermissionDefinition $permissionDef)
64
    {
65 2
        if (!$this->permissionsEnabled) {
66
            return $queryBuilder;
67
        }
68
69 2
        $aclConnection = $this->em->getConnection();
70
71 2
        $databasePrefix = is_file($aclConnection->getDatabase()) ? '' : $aclConnection->getDatabase().'.';
72 2
        $rootEntity = $permissionDef->getEntity();
73 2
        $linkAlias = $permissionDef->getAlias();
74
        // Only tables with a single ID PK are currently supported
75 2
        $linkField = $this->em->getClassMetadata($rootEntity)->getSingleIdentifierColumnName();
76
77 2
        $rootEntity = '"' . str_replace('\\', '\\\\', $rootEntity) . '"';
78 2
        $query = $queryBuilder;
79
80 2
        $builder = new MaskBuilder();
81 2 View Code Duplication
        foreach ($permissionDef->getPermissions() as $permission) {
82 2
            $mask = \constant(\get_class($builder) . '::MASK_' . strtoupper($permission));
83 2
            $builder->add($mask);
84
        }
85 2
        $mask = $builder->get();
86
87
        /* @var $token TokenInterface */
88 2
        $token = $this->tokenStorage->getToken();
89 2
        $userRoles = array();
90 2 View Code Duplication
        if (!\is_null($token)) {
91 2
            $user = $token->getUser();
92 2
            if (method_exists($this->roleHierarchy, 'getReachableRoleNames')) {
93 2
                $userRoles = $this->roleHierarchy->getReachableRoleNames($token->getRoleNames());
94
            } else {
95
                // Symfony 3.4 compatibility
96
                $userRoles = $this->roleHierarchy->getReachableRoles($token->getRoles());
97
            }
98
        }
99
100
        // Security context does not provide anonymous role automatically.
101 2
        $uR = array('"IS_AUTHENTICATED_ANONYMOUSLY"');
102
103 2 View Code Duplication
        foreach ($userRoles as $role) {
104
            // The reason we ignore this is because by default FOSUserBundle adds ROLE_USER for every user
105 2
            if (is_string($role)) {
106 2
                if ($role !== 'ROLE_USER') {
107 2
                    $uR[] = '"' . $role . '"';
108
                }
109
            } else {
110
                // Symfony 3.4 compatibility
111
                if ($role->getRole() !== 'ROLE_USER') {
112
                    $uR[] = '"' . $role->getRole() . '"';
113
                }
114
            }
115
        }
116 2
        $uR = array_unique($uR);
117 2
        $inString = implode(' OR s.identifier = ', $uR);
118
119 2 View Code Duplication
        if (\is_object($user)) {
120 1
            $inString .= ' OR s.identifier = "' . str_replace(
121 1
                '\\',
122 1
                '\\\\',
123 1
                \get_class($user)
124 1
            ) . '-' . $user->getUserName() . '"';
0 ignored issues
show
The method getUserName does only exist in Symfony\Component\Security\Core\User\UserInterface, but not in Stringable.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
125
        }
126
127
        $joinTableQuery = <<<SELECTQUERY
128 2
SELECT DISTINCT o.object_identifier as id FROM {$databasePrefix}acl_object_identities as o
129 2
INNER JOIN {$databasePrefix}acl_classes c ON c.id = o.class_id
130 2
LEFT JOIN {$databasePrefix}acl_entries e ON (
131
    e.class_id = o.class_id AND (e.object_identity_id = o.id
132 2
    OR {$aclConnection->getDatabasePlatform()->getIsNullExpression('e.object_identity_id')})
133
)
134 2
LEFT JOIN {$databasePrefix}acl_security_identities s ON (
135
s.id = e.security_identity_id
136
)
137 2
WHERE c.class_type = {$rootEntity}
138 2
AND (s.identifier = {$inString})
139 2
AND e.mask & {$mask} > 0
140
SELECTQUERY;
141
142 2
        $query->join($linkAlias, '(' . $joinTableQuery . ')', 'perms_', 'perms_.id = ' . $linkAlias . '.' . $linkField);
143
144 2
        return $query;
145
    }
146
147
    /**
148
     * @return null|TokenStorageInterface
149
     */
150 1
    public function getTokenStorage()
151
    {
152 1
        return $this->tokenStorage;
153
    }
154
}
155