1
|
<?php
|
2
|
|
3
|
/**
|
4
|
* @file
|
5
|
* An API module providing by-user access control lists.
|
6
|
*
|
7
|
* This module handles ACLs on behalf of other modules. The two main reasons
|
8
|
* to do this are so that modules using ACLs can share them with each
|
9
|
* other without having to actually know much about them, and so that
|
10
|
* ACLs can easily co-exist with the existing node_access system.
|
11
|
*/
|
12
|
|
13
|
/**
|
14
|
* Create a new ACL.
|
15
|
*
|
16
|
* The client module will have to keep track of the ACL. For that it can
|
17
|
* assign either a $name or a $number to this ACL.
|
18
|
*
|
19
|
* @param $module
|
20
|
* The name of the client module.
|
21
|
* @param $name
|
22
|
* An arbitrary name for this ACL, freely defined by the client module.
|
23
|
* @param $number
|
24
|
* An arbitrary number for this ACL, freely defined by the client module.
|
25
|
* @return
|
26
|
* The ID of the newly created ACL.
|
27
|
*/
|
28
|
function acl_create_acl($module, $name = NULL, $number = NULL) {
|
29
|
$acl = array(
|
30
|
'module' => $module,
|
31
|
'name' => $name,
|
32
|
'number' => $number
|
33
|
);
|
34
|
drupal_write_record('acl', $acl);
|
35
|
return $acl['acl_id'];
|
36
|
}
|
37
|
|
38
|
/**
|
39
|
* Create a new ACL (obsolete).
|
40
|
*
|
41
|
* @see acl_create_acl()
|
42
|
*/
|
43
|
function acl_create_new_acl($module, $name = NULL, $number = NULL) {
|
44
|
return acl_create_acl($module, $name, $number);
|
45
|
}
|
46
|
|
47
|
/**
|
48
|
* Delete an existing ACL.
|
49
|
*/
|
50
|
function acl_delete_acl($acl_id) {
|
51
|
db_delete('acl')
|
52
|
->condition('acl_id', $acl_id)
|
53
|
->execute();
|
54
|
db_delete('acl_user')
|
55
|
->condition('acl_id', $acl_id)
|
56
|
->execute();
|
57
|
db_delete('acl_node')
|
58
|
->condition('acl_id', $acl_id)
|
59
|
->execute();
|
60
|
}
|
61
|
|
62
|
/**
|
63
|
* Add the specified UID to an ACL.
|
64
|
*/
|
65
|
function acl_add_user($acl_id, $uid) {
|
66
|
$test_uid = db_query("SELECT uid FROM {acl_user} WHERE acl_id = :acl_id AND uid = :uid", array(
|
67
|
'acl_id' => $acl_id,
|
68
|
'uid' => $uid,
|
69
|
))->fetchField();
|
70
|
if (!$test_uid) {
|
71
|
db_insert('acl_user')
|
72
|
->fields(array(
|
73
|
'acl_id' => $acl_id,
|
74
|
'uid' => $uid,
|
75
|
))
|
76
|
->execute();
|
77
|
}
|
78
|
}
|
79
|
|
80
|
/**
|
81
|
* Remove the specified UID from an ACL.
|
82
|
*/
|
83
|
function acl_remove_user($acl_id, $uid) {
|
84
|
db_delete('acl_user')
|
85
|
->condition('acl_id', $acl_id)
|
86
|
->condition('uid', $uid)
|
87
|
->execute();
|
88
|
}
|
89
|
|
90
|
/**
|
91
|
* Provide a form to edit the ACL that can be embedded in other forms.
|
92
|
* Pass $new_acl=TRUE if you have no ACL yet, but do supply a string
|
93
|
* like 'my_module_new_acl' as $acl_id anyway.
|
94
|
*/
|
95
|
function acl_edit_form(&$form_state, $acl_id, $label = NULL, $new_acl = FALSE) {
|
96
|
$form_state['build_info']['files'][] = _acl_module_load_include('admin.inc');
|
97
|
return _acl_edit_form($acl_id, $label, $new_acl);
|
98
|
}
|
99
|
|
100
|
/**
|
101
|
* Provide access control to all nodes selected by a subquery, based upon an ACL id.
|
102
|
*/
|
103
|
function acl_add_nodes($subselect, $acl_id, $view, $update, $delete, $priority = 0) {
|
104
|
db_delete('acl_node')
|
105
|
->condition('acl_id', $acl_id)
|
106
|
->condition('nid', $subselect, 'IN')
|
107
|
->execute();
|
108
|
$subselect->addExpression($acl_id, 'acl_id');
|
109
|
$subselect->addExpression((int) $view, 'grant_view');
|
110
|
$subselect->addExpression((int) $update, 'grant_update');
|
111
|
$subselect->addExpression((int) $delete, 'grant_delete');
|
112
|
$subselect->addExpression($priority, 'priority');
|
113
|
db_insert('acl_node')
|
114
|
->from($subselect)
|
115
|
->execute();
|
116
|
}
|
117
|
|
118
|
/**
|
119
|
* Provide access control to a node based upon an ACL id.
|
120
|
*/
|
121
|
function acl_node_add_acl($nid, $acl_id, $view, $update, $delete, $priority = 0) {
|
122
|
acl_node_add_acl_record(array(
|
123
|
'acl_id' => $acl_id,
|
124
|
'nid' => $nid,
|
125
|
'grant_view' => (int) $view,
|
126
|
'grant_update' => (int) $update,
|
127
|
'grant_delete' => (int) $delete,
|
128
|
'priority' => $priority,
|
129
|
));
|
130
|
}
|
131
|
|
132
|
/**
|
133
|
* Provide access control to a node based upon an ACL id.
|
134
|
*/
|
135
|
function acl_node_add_acl_record(array $record) {
|
136
|
db_delete('acl_node')
|
137
|
->condition('acl_id', $record['acl_id'])
|
138
|
->condition('nid', $record['nid'])
|
139
|
->execute();
|
140
|
db_insert('acl_node')
|
141
|
->fields($record)
|
142
|
->execute();
|
143
|
}
|
144
|
|
145
|
/**
|
146
|
* Remove an ACL completely from a node.
|
147
|
*/
|
148
|
function acl_node_remove_acl($nid, $acl_id) {
|
149
|
db_delete('acl_node')
|
150
|
->condition('acl_id', $acl_id)
|
151
|
->condition('nid', $nid)
|
152
|
->execute();
|
153
|
}
|
154
|
|
155
|
/**
|
156
|
* Clear all of a module's ACLs from a node.
|
157
|
*/
|
158
|
function acl_node_clear_acls($nid, $module) {
|
159
|
$select = db_select('acl', 'a')
|
160
|
->fields('a', array('acl_id'))
|
161
|
->condition('a.module', $module);
|
162
|
db_delete('acl_node')
|
163
|
->condition('nid', $nid)
|
164
|
->condition('acl_id', $select, 'IN')
|
165
|
->execute();
|
166
|
}
|
167
|
|
168
|
/**
|
169
|
* Get the id of an ACL by name (+ optionally number).
|
170
|
*/
|
171
|
function acl_get_id_by_name($module, $name, $number = NULL) {
|
172
|
$query = db_select('acl', 'a')
|
173
|
->fields('a', array('acl_id'))
|
174
|
->condition('a.module', $module)
|
175
|
->condition('a.name', $name);
|
176
|
if (isset($number)) {
|
177
|
$query->condition('a.number', $number);
|
178
|
}
|
179
|
return $query->execute()->fetchField();
|
180
|
}
|
181
|
|
182
|
/**
|
183
|
* Get the id of an ACL by number.
|
184
|
*/
|
185
|
function acl_get_id_by_number($module, $number) {
|
186
|
$query = db_select('acl', 'a')
|
187
|
->fields('a', array('acl_id'))
|
188
|
->condition('a.module', $module)
|
189
|
->condition('a.number', $number);
|
190
|
return $query->execute()->fetchField();
|
191
|
}
|
192
|
|
193
|
/**
|
194
|
* Determine whether an ACL has some assigned users.
|
195
|
*/
|
196
|
function acl_has_users($acl_id) {
|
197
|
return db_query("SELECT COUNT(uid) FROM {acl_user} WHERE acl_id = :acl_id", array(
|
198
|
'acl_id' => $acl_id,
|
199
|
))->fetchField();
|
200
|
}
|
201
|
|
202
|
/**
|
203
|
* Determine whether an ACL has a specific assigned user.
|
204
|
*/
|
205
|
function acl_has_user($acl_id, $uid) {
|
206
|
return db_query("SELECT COUNT(uid) FROM {acl_user} WHERE acl_id = :acl_id AND uid = :uid", array(
|
207
|
'acl_id' => $acl_id,
|
208
|
'uid' => $uid,
|
209
|
))->fetchField();
|
210
|
}
|
211
|
|
212
|
/**
|
213
|
* Get an array of acl_ids held by a user.
|
214
|
*/
|
215
|
function acl_get_ids_by_user($module, $uid, $name = NULL, $number = NULL) {
|
216
|
$query = db_select('acl', 'a');
|
217
|
$query->join('acl_user', 'au', 'a.acl_id = au.acl_id');
|
218
|
$query
|
219
|
->fields('a', array('acl_id'))
|
220
|
->condition('a.module', $module)
|
221
|
->condition('au.uid', $uid);
|
222
|
if (isset($name)) {
|
223
|
$query->condition('a.name', $name);
|
224
|
}
|
225
|
if (isset($number)) {
|
226
|
$query->condition('a.number', $number);
|
227
|
}
|
228
|
$acl_ids = $query->execute()->fetchCol();
|
229
|
return $acl_ids;
|
230
|
}
|
231
|
|
232
|
/**
|
233
|
* Get the uids of an ACL.
|
234
|
*/
|
235
|
function acl_get_uids($acl_id) {
|
236
|
$uids = db_query("SELECT uid FROM {acl_user} WHERE acl_id = :acl_id", array(
|
237
|
'acl_id' => $acl_id,
|
238
|
))->fetchCol();
|
239
|
return $uids;
|
240
|
}
|
241
|
|
242
|
/**
|
243
|
* Implements hook_node_access_records().
|
244
|
*/
|
245
|
function acl_node_access_records($node) {
|
246
|
if (!$node->nid) {
|
247
|
return;
|
248
|
}
|
249
|
$result = db_query("SELECT n.*, 'acl' AS realm, n.acl_id AS gid, a.module FROM {acl_node} n INNER JOIN {acl} a ON n.acl_id = a.acl_id WHERE nid = :nid", array(
|
250
|
'nid' => $node->nid,
|
251
|
), array('fetch' => PDO::FETCH_ASSOC));
|
252
|
$grants = array();
|
253
|
foreach ($result as $grant) {
|
254
|
if (module_invoke($grant['module'], 'enabled')) {
|
255
|
if (acl_has_users($grant['gid'])) {
|
256
|
$grants[] = $grant;
|
257
|
}
|
258
|
else {
|
259
|
//just deny access
|
260
|
$grants[] = array(
|
261
|
'realm' => 'acl',
|
262
|
'gid' => $grant['gid'],
|
263
|
'grant_view' => 0,
|
264
|
'grant_update' => 0,
|
265
|
'grant_delete' => 0,
|
266
|
'priority' => $grant['priority'],
|
267
|
);
|
268
|
}
|
269
|
}
|
270
|
}
|
271
|
return $grants;
|
272
|
}
|
273
|
|
274
|
/**
|
275
|
* Implements hook_node_grants().
|
276
|
*/
|
277
|
function acl_node_grants($account, $op) {
|
278
|
$acl_ids = db_query("SELECT acl_id FROM {acl_user} WHERE uid = :uid", array(
|
279
|
'uid' => $account->uid,
|
280
|
))->fetchCol();
|
281
|
return (!empty($acl_ids) ? array('acl' => $acl_ids) : NULL);
|
282
|
}
|
283
|
|
284
|
/**
|
285
|
* Implements hook_node_delete().
|
286
|
*/
|
287
|
function acl_node_delete($node) {
|
288
|
db_delete('acl_node')
|
289
|
->condition('nid', $node->nid)
|
290
|
->execute();
|
291
|
}
|
292
|
|
293
|
/**
|
294
|
* Implements hook_user_cancel().
|
295
|
*/
|
296
|
function acl_user_cancel($edit, $account, $method) {
|
297
|
db_delete('acl_user')
|
298
|
->condition('uid', $account->uid)
|
299
|
->execute();
|
300
|
}
|
301
|
|
302
|
/**
|
303
|
* Helper function to load include files.
|
304
|
*/
|
305
|
function _acl_module_load_include($type) {
|
306
|
static $loaded = array();
|
307
|
|
308
|
if (!isset($loaded[$type])) {
|
309
|
$path = module_load_include($type, 'acl');
|
310
|
$loaded[$type] = drupal_get_path('module', 'acl') . "/acl.$type";
|
311
|
}
|
312
|
return $loaded[$type];
|
313
|
}
|
314
|
|
315
|
/**
|
316
|
* Implements hook_node_access_explain().
|
317
|
*/
|
318
|
function acl_node_access_explain($row) {
|
319
|
static $interpretations = array();
|
320
|
if ($row->realm == 'acl') {
|
321
|
if (!isset($interpretations[$row->gid])) {
|
322
|
$acl = db_query("SELECT * FROM {acl} WHERE acl_id = :acl_id", array(
|
323
|
'acl_id' => $row->gid,
|
324
|
))->fetchObject();
|
325
|
$acl->tag = '?';
|
326
|
if (!isset($acl->name)) {
|
327
|
$acl->tag = $acl->number;
|
328
|
}
|
329
|
elseif (!isset($acl->number)) {
|
330
|
$acl->tag = $acl->name;
|
331
|
}
|
332
|
else {
|
333
|
$acl->tag = $acl->name . '-' . $acl->number;
|
334
|
}
|
335
|
$result = db_query("SELECT u.name FROM {acl_user} au, {users} u WHERE au.acl_id = :acl_id AND au.uid = u.uid", array(
|
336
|
'acl_id' => $row->gid,
|
337
|
));
|
338
|
foreach ($result as $user) {
|
339
|
$users[] = $user->name;
|
340
|
}
|
341
|
if (isset($users)) {
|
342
|
$users = implode(', ', $users);
|
343
|
$interpretations[$row->gid] = _acl_get_explanation("$acl->module/$acl->tag: $users", $acl->acl_id, $acl->module, $acl->name, $acl->number, $users);
|
344
|
}
|
345
|
elseif ($row->gid == 0) {
|
346
|
$result = db_query("SELECT an.acl_id, a.module, a.name FROM {acl_node} an JOIN {acl} a ON an.acl_id = a.acl_id LEFT JOIN {acl_user} au ON a.acl_id = au.acl_id WHERE an.nid = :nid AND au.uid IS NULL", array(
|
347
|
'nid' => $row->nid,
|
348
|
));
|
349
|
foreach ($result as $acl) {
|
350
|
$rows[] = _acl_get_explanation("$acl->acl_id: $acl->module/$acl->tag", $acl->acl_id, $acl->module, $acl->name, $acl->number);
|
351
|
}
|
352
|
if (!empty($rows)) {
|
353
|
return implode('<br />', $rows);
|
354
|
}
|
355
|
return 'No access via ACL.';
|
356
|
}
|
357
|
else {
|
358
|
$interpretations[$row->gid] = _acl_get_explanation("$acl->module/$acl->tag: no users!", $acl->acl_id, $acl->module, $acl->name, $acl->number);
|
359
|
}
|
360
|
}
|
361
|
return $interpretations[$row->gid];
|
362
|
}
|
363
|
}
|
364
|
|
365
|
/**
|
366
|
* Helper function to ask the client for its interpretation of the given
|
367
|
* grant record.
|
368
|
*/
|
369
|
function _acl_get_explanation($text, $acl_id, $module, $name, $number, $users = NULL) {
|
370
|
$hook = $module .'_acl_explain';
|
371
|
if (function_exists($hook)) {
|
372
|
return '<span title="'. $hook($acl_id, $name, $number, $users) .'">'. $text .'</span>';
|
373
|
}
|
374
|
return $text;
|
375
|
}
|
376
|
|