4
4
Access Control Lists (ACLs)
5
5
===========================
6
6
7
- In complex applications, you will often face the problem that access decisions
8
- cannot only be based on the person (``Token ``) who is requesting access, but
7
+ In complex applications, you will often face the problem that access decisions
8
+ cannot only be based on the person (``Token ``) who is requesting access, but
9
9
also involve a domain object that access is being requested for. This is where
10
10
the ACL system comes in.
11
11
12
- Imagine you are designing a blog system where your users can comment on your
13
- posts. Now, you want a user to be able to edit his own comments, but not those
14
- of other users; besides, you yourself want to be able to edit all comments.
15
- In this scenario, ``Comment `` would be our domain object that you want to
16
- restrict access to. You could take several approaches to accomplish this using
12
+ Imagine you are designing a blog system where your users can comment on your
13
+ posts. Now, you want a user to be able to edit his own comments, but not those
14
+ of other users; besides, you yourself want to be able to edit all comments. In
15
+ this scenario, ``Comment `` would be our domain object that you want to
16
+ restrict access to. You could take several approaches to accomplish this using
17
17
Symfony2, two basic approaches are (non-exhaustive):
18
18
19
- - *Enforce security in your business methods *: Basically, that means keeping
20
- a reference inside each ``Comment `` to all users who have access, and then
19
+ - *Enforce security in your business methods *: Basically, that means keeping a
20
+ reference inside each ``Comment `` to all users who have access, and then
21
21
compare these users to the provided ``Token ``.
22
- - *Enforce security with roles *: In this approach, you would add a role for
22
+ - *Enforce security with roles *: In this approach, you would add a role for
23
23
each ``Comment `` object, i.e. ``ROLE_COMMENT_1 ``, ``ROLE_COMMENT_2 ``, etc.
24
24
25
- Both approaches are perfectly valid. However, they couple your authorization
26
- logic to your business code which makes it less reusable elsewhere, and also
27
- increases the difficulty of unit testing. Besides, you could run into
25
+ Both approaches are perfectly valid. However, they couple your authorization
26
+ logic to your business code which makes it less reusable elsewhere, and also
27
+ increases the difficulty of unit testing. Besides, you could run into
28
28
performance issues if many users would have access to a single domain object.
29
29
30
30
Fortunately, there is a better way, which we will talk about now.
31
31
32
32
Bootstrapping
33
33
-------------
34
- Now, before we finally can get into action, we need to do some bootstrapping.
34
+
35
+ Now, before we finally can get into action, we need to do some bootstrapping.
35
36
First, we need to configure the connection the ACL system is supposed to use:
36
37
37
- .. configuration_block ::
38
-
39
- .. code_block :: yaml
40
-
38
+ .. configuration-block ::
39
+
40
+ .. code-block :: yaml
41
+
41
42
# app/config/security.yml
42
43
security.acl :
43
44
connection : default
@@ -54,18 +55,21 @@ First, we need to configure the connection the ACL system is supposed to use:
54
55
// app/config/security.php
55
56
$container->loadFromExtension('security', 'acl', array(
56
57
'connection' => 'default',
57
- ));
58
+ ));
58
59
59
60
60
61
After the connection is configured. We have to import the database structure.
61
62
Fortunately, we have a task for this. Simply run the following command:
62
63
63
- `` php app/console init:acl ``
64
+ .. code-block :: text
64
65
66
+ php app/console init:acl
65
67
66
68
Getting Started
67
69
---------------
68
- Coming back to our small example from the beginning, let's implement ACL for it.
70
+
71
+ Coming back to our small example from the beginning, let's implement ACL for
72
+ it.
69
73
70
74
Creating an ACL, and adding an ACE
71
75
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -76,89 +80,91 @@ Creating an ACL, and adding an ACE
76
80
public function addCommentAction(Post $post)
77
81
{
78
82
$comment = new Comment();
79
-
83
+
80
84
// setup $form, and bind data
81
85
// ...
82
-
86
+
83
87
if ($form->isValid()) {
84
88
$entityManager = $this->container->get('doctrine.orm.default_entity_manager');
85
89
$entityManager->persist($comment);
86
90
$entityManager->flush();
87
-
91
+
88
92
// creating the ACL
89
93
$aclProvider = $this->container->get('security.acl.provider');
90
94
$objectIdentity = ObjectIdentity::fromDomainObject($comment);
91
95
$acl = $aclProvider->createAcl($objectIdentity);
92
-
96
+
93
97
// retrieving the security identity of the currently logged-in user
94
98
$securityContext = $this->container->get('security.context');
95
99
$user = $securityContext->getToken()->getUser();
96
100
$securityIdentity = new UserSecurityIdentity($user);
97
-
101
+
98
102
// grant owner access
99
103
$acl->insertObjectAce($securityIdentity, MaskBuilder::MASK_OWNER);
100
104
$aclProvider->updateAcl($acl);
101
105
}
102
106
}
103
107
104
- There are a couple of important implementation decisions in this code snippet. For now,
105
- I only want to highlight two:
108
+ There are a couple of important implementation decisions in this code snippet.
109
+ For now, I only want to highlight two:
106
110
107
- First, you may have noticed that ``->createAcl() `` does not accept domain objects
108
- directly, but only implementations of the ``ObjectIdentityInterface ``. This
109
- additional step of indirection allows you to work with ACLs even when you have
110
- no actual domain object instance at hand. This will be extremely helpful if you
111
- want to check permissions for a large number of objects without actually hydrating
112
- these objects.
111
+ First, you may have noticed that ``->createAcl() `` does not accept domain
112
+ objects directly, but only implementations of the ``ObjectIdentityInterface ``.
113
+ This additional step of indirection allows you to work with ACLs even when you
114
+ have no actual domain object instance at hand. This will be extremely helpful
115
+ if you want to check permissions for a large number of objects without
116
+ actually hydrating these objects.
113
117
114
- The other interesting part is the ``->insertObjectAce() `` call. In our example,
115
- we are granting the user who is currently logged in owner access to the Comment.
116
- The ``MaskBuilder::MASK_OWNER `` is a pre-defined integer bitmask; don't worry
117
- the mask builder will abstract away most of the technical details, but using
118
- this technique we can store many different permissions in one database row
119
- which gives us a considerable boost in performance.
118
+ The other interesting part is the ``->insertObjectAce() `` call. In our
119
+ example, we are granting the user who is currently logged in owner access to
120
+ the Comment. The ``MaskBuilder::MASK_OWNER `` is a pre-defined integer bitmask;
121
+ don't worry the mask builder will abstract away most of the technical details,
122
+ but using this technique we can store many different permissions in one
123
+ database row which gives us a considerable boost in performance.
120
124
121
125
.. tip ::
122
126
123
- The order in which ACEs are checked is significant. As a general rule, you
127
+ The order in which ACEs are checked is significant. As a general rule, you
124
128
should place more specific entries at the beginning.
125
129
126
130
Checking Access
127
131
~~~~~~~~~~~~~~~
128
132
129
133
.. code-block :: php
130
-
134
+
131
135
// BlogController.php
132
136
public function editCommentAction(Comment $comment)
133
137
{
134
138
$securityContext = $this->container->get('security.context');
135
-
139
+
136
140
// check for edit access
137
141
if (false === $securityContext->vote('EDIT', $comment))
138
142
{
139
143
throw new AccessDeniedException();
140
144
}
141
-
145
+
142
146
// do your editing here
143
147
}
144
148
145
- In this example, we check whether the user has the ``EDIT `` permission. Internally,
146
- Symfony2 maps the permission to several integer bitmasks, and checks whether the
147
- user has any of them.
149
+ In this example, we check whether the user has the ``EDIT `` permission.
150
+ Internally, Symfony2 maps the permission to several integer bitmasks, and
151
+ checks whether the user has any of them.
148
152
149
153
.. note ::
150
154
151
- You can define up to 32 base permissions (depending on your OS PHP might vary
152
- between 30 to 32). In addition, you can also define cumulative permissions.
155
+ You can define up to 32 base permissions (depending on your OS PHP might
156
+ vary between 30 to 32). In addition, you can also define cumulative
157
+ permissions.
153
158
154
159
Cumulative Permissions
155
160
----------------------
161
+
156
162
In our first example above, we only granted the user the ``OWNER `` base
157
- permission. While this effectively also allows the user to perform any operation
158
- such as view, edit, etc. on the domain object, there are cases where we want to
159
- grant these permissions explicitly.
163
+ permission. While this effectively also allows the user to perform any
164
+ operation such as view, edit, etc. on the domain object, there are cases where
165
+ we want to grant these permissions explicitly.
160
166
161
- The ``MaskBuilder `` can be used for creating bit masks easily by combining
167
+ The ``MaskBuilder `` can be used for creating bit masks easily by combining
162
168
several base permissions:
163
169
164
170
.. code-block :: php
@@ -171,8 +177,8 @@ several base permissions:
171
177
->add('undelete')
172
178
;
173
179
$mask = $builder->get(); // int(15)
174
-
175
- This integer bitmask can then be used to grant a user the base permissions you
180
+
181
+ This integer bitmask can then be used to grant a user the base permissions you
176
182
added above:
177
183
178
184
.. code-block :: php
0 commit comments