Merge pull request #15834 from francescbassas/patch-16
[civicrm-core.git] / CRM / Contact / BAO / Contact / Permission.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 */
17class CRM_Contact_BAO_Contact_Permission {
18
0978cb9c
SL
19 /**
20 * @var bool
21 */
22 public static $useTempTable = TRUE;
23
24 /**
25 * Set whether to use a temporary table or not when building ACL Cache
26 * @param bool $useTemporaryTable
27 */
28 public static function setUseTemporaryTable($useTemporaryTable = TRUE) {
29 self::$useTempTable = $useTemporaryTable;
30 }
31
32 /**
33 * Get variable for determining if we should use Temporary Table or not
34 * @return bool
35 */
36 public static function getUseTemporaryTable() {
37 return self::$useTempTable;
38 }
39
dddf4bf6 40 /**
e4541c56 41 * Check which of the given contact IDs the logged in user
340be2e7 42 * has permissions for the operation type according to:
43 * - general permissions (e.g. 'edit all contacts')
44 * - deletion status (unless you have 'access deleted contacts')
45 * - ACL
46 * - permissions inherited through relationships (also second degree if enabled)
dddf4bf6 47 *
48 * @param array $contact_ids
49 * Contact IDs.
340be2e7 50 * @param int $type the type of operation (view|edit)
dddf4bf6 51 *
52 * @see CRM_Contact_BAO_Contact_Permission::allow
53 *
54 * @return array
69078420 55 * list of contact IDs the logged in user has the given permission for
dddf4bf6 56 */
57 public static function allowList($contact_ids, $type = CRM_Core_Permission::VIEW) {
be2fb01f 58 $result_set = [];
dddf4bf6 59 if (empty($contact_ids)) {
60 // empty contact lists would cause trouble in the SQL. And be pointless.
61 return $result_set;
62 }
63
64 // make sure the the general permissions are given
e4541c56 65 if (CRM_Core_Permission::check('edit all contacts')
19f13a7c 66 || $type == CRM_Core_Permission::VIEW && CRM_Core_Permission::check('view all contacts')
dddf4bf6 67 ) {
67df1408 68
dddf4bf6 69 // if the general permission is there, all good
67df1408 70 if (CRM_Core_Permission::check('access deleted contacts')) {
e8a0f9e0 71 // if user can access deleted contacts -> fine
67df1408 72 return $contact_ids;
e4541c56 73 }
74 else {
67df1408 75 // if the user CANNOT access deleted contacts, these need to be filtered
340be2e7 76 $contact_id_list = implode(',', $contact_ids);
67df1408 77 $filter_query = "SELECT DISTINCT(id) FROM civicrm_contact WHERE id IN ($contact_id_list) AND is_deleted = 0";
78 $query = CRM_Core_DAO::executeQuery($filter_query);
79 while ($query->fetch()) {
80 $result_set[(int) $query->id] = TRUE;
81 }
82 return array_keys($result_set);
83 }
dddf4bf6 84 }
85
86 // get logged in user
340be2e7 87 $contactID = CRM_Core_Session::getLoggedInContactID();
dddf4bf6 88 if (empty($contactID)) {
be2fb01f 89 return [];
dddf4bf6 90 }
91
92 // make sure the cache is filled
93 self::cache($contactID, $type);
94
95 // compile query
dddf4bf6 96 $operation = ($type == CRM_Core_Permission::VIEW) ? 'View' : 'Edit';
97
98 // add clause for deleted contacts, if the user doesn't have the permission to access them
163bfad3 99 $LEFT_JOIN_DELETED = $AND_CAN_ACCESS_DELETED = '';
dddf4bf6 100 if (!CRM_Core_Permission::check('access deleted contacts')) {
67df1408 101 $LEFT_JOIN_DELETED = "LEFT JOIN civicrm_contact ON civicrm_contact.id = contact_id";
e4541c56 102 $AND_CAN_ACCESS_DELETED = "AND civicrm_contact.is_deleted = 0";
dddf4bf6 103 }
104
105 // RUN the query
340be2e7 106 $contact_id_list = implode(',', $contact_ids);
dddf4bf6 107 $query = "
108SELECT contact_id
109 FROM civicrm_acl_contact_cache
110 {$LEFT_JOIN_DELETED}
9aea8e14 111WHERE contact_id IN ({$contact_id_list})
dddf4bf6 112 AND user_id = {$contactID}
113 AND operation = '{$operation}'
114 {$AND_CAN_ACCESS_DELETED}";
115 $result = CRM_Core_DAO::executeQuery($query);
116 while ($result->fetch()) {
67df1408 117 $result_set[(int) $result->contact_id] = TRUE;
dddf4bf6 118 }
119
e4541c56 120 // if some have been rejected, double check for permissions inherited by relationship
dddf4bf6 121 if (count($result_set) < count($contact_ids)) {
69078420 122 $rejected_contacts = array_diff_key($contact_ids, $result_set);
98445ac5 123 // @todo consider storing these to the acl cache for next time, since we have fetched.
f871c3a9 124 $allowed_by_relationship = self::relationshipList($rejected_contacts, $type);
67df1408 125 foreach ($allowed_by_relationship as $contact_id) {
126 $result_set[(int) $contact_id] = TRUE;
127 }
dddf4bf6 128 }
129
67df1408 130 return array_keys($result_set);
dddf4bf6 131 }
132
6a488035 133 /**
fe482240 134 * Check if the logged in user has permissions for the operation type.
6a488035 135 *
77c5b619
TO
136 * @param int $id
137 * Contact id.
77b97be7 138 * @param int|string $type the type of operation (view|edit)
6a488035 139 *
acb1052e 140 * @return bool
a6c01b45 141 * true if the user has permission, false otherwise
6a488035 142 */
00be9182 143 public static function allow($id, $type = CRM_Core_Permission::VIEW) {
9a41e168 144 // get logged in user
340be2e7 145 $contactID = CRM_Core_Session::getLoggedInContactID();
9a41e168 146
2b8d25f0 147 // first: check if contact is trying to view own contact
135367a6 148 if ($contactID == $id && ($type == CRM_Core_Permission::VIEW && CRM_Core_Permission::check('view my contact')
149 || $type == CRM_Core_Permission::EDIT && CRM_Core_Permission::check('edit my contact'))
2b8d25f0 150 ) {
151 return TRUE;
152 }
6a488035 153
e97c66ff 154 // FIXME: push this somewhere below, to not give this permission so many rights
6a488035
TO
155 $isDeleted = (bool) CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $id, 'is_deleted');
156 if (CRM_Core_Permission::check('access deleted contacts') && $isDeleted) {
157 return TRUE;
158 }
159
160 // short circuit for admin rights here so we avoid unneeeded queries
161 // some duplication of code, but we skip 3-5 queries
162 if (CRM_Core_Permission::check('edit all contacts') ||
163 ($type == CRM_ACL_API::VIEW && CRM_Core_Permission::check('view all contacts'))
164 ) {
165 return TRUE;
166 }
167
9a41e168 168 // check permission based on relationship, CRM-2963
be2fb01f 169 if (self::relationshipList([$id], $type)) {
6a488035
TO
170 return TRUE;
171 }
172
8210399a 173 // We should probably do a cheap check whether it's in the cache first.
9a41e168 174 // check permission based on ACL
be2fb01f
CW
175 $tables = [];
176 $whereTables = [];
6a488035 177
8d0a0fd0 178 $permission = CRM_ACL_API::whereClause($type, $tables, $whereTables, NULL, FALSE, FALSE, TRUE);
6a488035
TO
179 $from = CRM_Contact_BAO_Query::fromClause($whereTables);
180
181 $query = "
680a52de 182SELECT contact_a.id
6a488035 183 $from
680a52de 184WHERE contact_a.id = %1 AND $permission
185 LIMIT 1
186";
6a488035 187
be2fb01f 188 if (CRM_Core_DAO::singleValueQuery($query, [1 => [$id, 'Integer']])) {
680a52de 189 return TRUE;
190 }
5f652ac7 191 return FALSE;
6a488035
TO
192 }
193
194 /**
5b27235a 195 * Fill the acl contact cache for this ACLed contact id if empty.
6a488035 196 *
5b27235a 197 * @param int $userID - contact_id of the ACLed user
dd244018 198 * @param int|string $type the type of operation (view|edit)
5b27235a
SL
199 * @param bool $force - Should we force a recompute.
200 *
6a488035 201 */
00be9182 202 public static function cache($userID, $type = CRM_Core_Permission::VIEW, $force = FALSE) {
340be2e7 203 // FIXME: maybe find a better way of keeping track of this. @eileen pointed out
204 // that somebody might flush the cache away from under our feet,
5af77f03 205 // but the alternative would be a SQL call every time this is called,
340be2e7 206 // and a complete rebuild if the result was an empty set...
04adc5f0 207 if (!isset(Civi::$statics[__CLASS__]['processed'])) {
208 Civi::$statics[__CLASS__]['processed'] = [
209 CRM_Core_Permission::VIEW => [],
210 CRM_Core_Permission::EDIT => [],
211 ];
212 }
6a488035 213
c0e87307 214 if ($type == CRM_Core_Permission::VIEW) {
6a488035
TO
215 $operationClause = " operation IN ( 'Edit', 'View' ) ";
216 $operation = 'View';
217 }
218 else {
219 $operationClause = " operation = 'Edit' ";
220 $operation = 'Edit';
221 }
be2fb01f 222 $queryParams = [1 => [$userID, 'Integer']];
6a488035
TO
223
224 if (!$force) {
c0e87307 225 // skip if already calculated
04adc5f0 226 if (!empty(Civi::$statics[__CLASS__]['processed'][$type][$userID])) {
6a488035
TO
227 return;
228 }
229
230 // run a query to see if the cache is filled
231 $sql = "
21ca2cb6 232SELECT count(*)
6a488035
TO
233FROM civicrm_acl_contact_cache
234WHERE user_id = %1
235AND $operationClause
236";
9aea8e14 237 $count = CRM_Core_DAO::singleValueQuery($sql, $queryParams);
6a488035 238 if ($count > 0) {
04adc5f0 239 Civi::$statics[__CLASS__]['processed'][$type][$userID] = 1;
6a488035
TO
240 return;
241 }
242 }
243
e5faffc4
SL
244 // grab a lock so other processes don't compete and do the same query
245 $lock = Civi::lockManager()->acquire("data.core.aclcontact.{$userID}");
246 if (!$lock->isAcquired()) {
247 // this can cause inconsistent results since we don't know if the other process
248 // will fill up the cache before our calling routine needs it.
249 // The default 3 second timeout should be enough for the other process to finish.
250 // However this routine does not return the status either, so basically
251 // its a "lets return and hope for the best"
252 return;
253 }
254
be2fb01f
CW
255 $tables = [];
256 $whereTables = [];
6a488035 257
9aea8e14 258 $permission = CRM_ACL_API::whereClause($type, $tables, $whereTables, $userID, FALSE, FALSE, TRUE);
6a488035
TO
259
260 $from = CRM_Contact_BAO_Query::fromClause($whereTables);
e5faffc4
SL
261 /* Ends up something like this:
262 CREATE TEMPORARY TABLE civicrm_temp_acl_contact_cache1310 (SELECT DISTINCT 2960 as user_id, contact_a.id as contact_id, 'View' as operation
263 FROM civicrm_contact contact_a LEFT JOIN civicrm_group_contact_cache `civicrm_group_contact_cache-ACL` ON contact_a.id = `civicrm_group_contact_cache-ACL`.contact_id
264 LEFT JOIN civicrm_acl_contact_cache ac ON ac.user_id = 2960 AND ac.contact_id = contact_a.id AND ac.operation = 'View'
265 WHERE ( `civicrm_group_contact_cache-ACL`.group_id IN (14, 25, 46, 47, 48, 49, 50, 51) ) AND (contact_a.is_deleted = 0)
266 AND ac.user_id IS NULL*/
267 /*$sql = "SELECT DISTINCT $userID as user_id, contact_a.id as contact_id, '{$operation}' as operation
268 $from
269 LEFT JOIN civicrm_acl_contact_cache ac ON ac.user_id = $userID AND ac.contact_id = contact_a.id AND ac.operation = '{$operation}'
270 WHERE $permission
271 AND ac.user_id IS NULL
272 ";*/
f6e78cf3 273 $sql = " $from WHERE $permission";
0978cb9c
SL
274 $useTempTable = self::getUseTemporaryTable();
275 if ($useTempTable) {
276 $aclContactsTempTable = CRM_Utils_SQL_TempTable::build()->setCategory('aclccache')->setMemory();
277 $tempTable = $aclContactsTempTable->getName();
f6e78cf3
SL
278 $aclContactsTempTable->createWithColumns('contact_id int, UNIQUE INDEX UI_contact (contact_id)');
279 CRM_Core_DAO::executeQuery("INSERT INTO {$tempTable} (contact_id) SELECT DISTINCT contact_a.id {$sql}");
280 CRM_Core_DAO::executeQuery("INSERT IGNORE INTO civicrm_acl_contact_cache (user_id, contact_id, operation) SELECT {$userID}, contact_id, '{$operation}' FROM {$tempTable}");
0978cb9c
SL
281 $aclContactsTempTable->drop();
282 }
283 else {
f6e78cf3 284 $sql = "SELECT DISTINCT $userID as user_id, contact_a.id as contact_id, '{$operation}' as operation" . $sql;
0978cb9c
SL
285 CRM_Core_DAO::executeQuery("INSERT IGNORE INTO civicrm_acl_contact_cache (user_id, contact_id, operation) {$sql}");
286 }
9aea8e14 287
288 // Add in a row for the logged in contact. Do not try to combine with the above query or an ugly OR will appear in
289 // the permission clause.
290 if (CRM_Core_Permission::check('edit my contact') ||
291 ($type == CRM_Core_Permission::VIEW && CRM_Core_Permission::check('view my contact'))) {
d0f057f4
EM
292 if (!CRM_Core_DAO::singleValueQuery("
293 SELECT count(*) FROM civicrm_acl_contact_cache WHERE user_id = %1 AND contact_id = %1 AND operation = '{$operation}' LIMIT 1", $queryParams)) {
e5faffc4 294 CRM_Core_DAO::executeQuery("INSERT IGNORE INTO civicrm_acl_contact_cache ( user_id, contact_id, operation ) VALUES(%1, %1, '{$operation}')", $queryParams);
9aea8e14 295 }
296 }
04adc5f0 297 Civi::$statics[__CLASS__]['processed'][$type][$userID] = 1;
e5faffc4 298 $lock->release();
6a488035
TO
299 }
300
86538308
EM
301 /**
302 * @param string $contactAlias
86538308
EM
303 *
304 * @return array
305 */
188fd46b 306 public static function cacheClause($contactAlias = 'contact_a') {
6a488035
TO
307 if (CRM_Core_Permission::check('view all contacts') ||
308 CRM_Core_Permission::check('edit all contacts')
309 ) {
310 if (is_array($contactAlias)) {
be2fb01f 311 $wheres = [];
6a488035
TO
312 foreach ($contactAlias as $alias) {
313 // CRM-6181
314 $wheres[] = "$alias.is_deleted = 0";
315 }
be2fb01f 316 return [NULL, '(' . implode(' AND ', $wheres) . ')'];
6a488035
TO
317 }
318 else {
319 // CRM-6181
be2fb01f 320 return [NULL, "$contactAlias.is_deleted = 0"];
6a488035
TO
321 }
322 }
323
188fd46b 324 $contactID = (int) CRM_Core_Session::getLoggedInContactID();
6a488035
TO
325 self::cache($contactID);
326
327 if (is_array($contactAlias) && !empty($contactAlias)) {
328 //More than one contact alias
be2fb01f 329 $clauses = [];
6a488035
TO
330 foreach ($contactAlias as $k => $alias) {
331 $clauses[] = " INNER JOIN civicrm_acl_contact_cache aclContactCache_{$k} ON {$alias}.id = aclContactCache_{$k}.contact_id AND aclContactCache_{$k}.user_id = $contactID ";
332 }
333
334 $fromClause = implode(" ", $clauses);
335 $whereClase = NULL;
336 }
337 else {
338 $fromClause = " INNER JOIN civicrm_acl_contact_cache aclContactCache ON {$contactAlias}.id = aclContactCache.contact_id ";
b49db103 339 $whereClase = " aclContactCache.user_id = $contactID AND $contactAlias.is_deleted = 0";
6a488035
TO
340 }
341
be2fb01f 342 return [$fromClause, $whereClase];
6a488035
TO
343 }
344
188fd46b 345 /**
5af77f03 346 * Generate acl subquery that can be placed in the WHERE clause of a query or the ON clause of a JOIN.
347 *
348 * This is specifically for VIEW operations.
188fd46b 349 *
0b80f0b4 350 * @return string|null
188fd46b 351 */
b53bcc5d 352 public static function cacheSubquery() {
be2fb01f 353 if (!CRM_Core_Permission::check([['view all contacts', 'edit all contacts']])) {
188fd46b
CW
354 $contactID = (int) CRM_Core_Session::getLoggedInContactID();
355 self::cache($contactID);
0b80f0b4 356 return "IN (SELECT contact_id FROM civicrm_acl_contact_cache WHERE user_id = $contactID)";
188fd46b 357 }
0b80f0b4 358 return NULL;
188fd46b
CW
359 }
360
dddf4bf6 361 /**
362 * Filter a list of contact_ids by the ones that the
363 * currently active user as a permissioned relationship with
364 *
365 * @param array $contact_ids
366 * List of contact IDs to be filtered
367 *
f871c3a9
AS
368 * @param int $type
369 * access type CRM_Core_Permission::VIEW or CRM_Core_Permission::EDIT
370 *
dddf4bf6 371 * @return array
372 * List of contact IDs that the user has permissions for
373 */
f871c3a9 374 public static function relationshipList($contact_ids, $type) {
be2fb01f 375 $result_set = [];
e4541c56 376
dddf4bf6 377 // no processing empty lists (avoid SQL errors as well)
378 if (empty($contact_ids)) {
be2fb01f 379 return [];
dddf4bf6 380 }
381
382 // get the currently logged in user
340be2e7 383 $contactID = CRM_Core_Session::getLoggedInContactID();
dddf4bf6 384 if (empty($contactID)) {
be2fb01f 385 return [];
dddf4bf6 386 }
387
388 // compile a list of queries (later to UNION)
be2fb01f 389 $queries = [];
dddf4bf6 390 $contact_id_list = implode(',', $contact_ids);
391
2f5aa3cd 392 // add a select statement for each direction
be2fb01f 393 $directions = [['from' => 'a', 'to' => 'b'], ['from' => 'b', 'to' => 'a']];
67df1408 394
f871c3a9 395 // CRM_Core_Permission::VIEW is satisfied by either CRM_Contact_BAO_Relationship::VIEW or CRM_Contact_BAO_Relationship::EDIT
da2c7679
AS
396 if ($type == CRM_Core_Permission::VIEW) {
397 $is_perm_condition = ' IN ( ' . CRM_Contact_BAO_Relationship::EDIT . ' , ' . CRM_Contact_BAO_Relationship::VIEW . ' ) ';
398 }
399 else {
400 $is_perm_condition = ' = ' . CRM_Contact_BAO_Relationship::EDIT;
401 }
f871c3a9 402
67df1408 403 // NORMAL/SINGLE DEGREE RELATIONSHIPS
dddf4bf6 404 foreach ($directions as $direction) {
405 $user_id_column = "contact_id_{$direction['from']}";
406 $contact_id_column = "contact_id_{$direction['to']}";
407
408 // add clause for deleted contacts, if the user doesn't have the permission to access them
67df1408 409 $LEFT_JOIN_DELETED = $AND_CAN_ACCESS_DELETED = '';
dddf4bf6 410 if (!CRM_Core_Permission::check('access deleted contacts')) {
67df1408 411 $LEFT_JOIN_DELETED = "LEFT JOIN civicrm_contact ON civicrm_contact.id = {$contact_id_column} ";
e4541c56 412 $AND_CAN_ACCESS_DELETED = "AND civicrm_contact.is_deleted = 0";
dddf4bf6 413 }
414
415 $queries[] = "
340be2e7 416SELECT civicrm_relationship.{$contact_id_column} AS contact_id
9aea8e14 417 FROM civicrm_relationship
dddf4bf6 418 {$LEFT_JOIN_DELETED}
419 WHERE civicrm_relationship.{$user_id_column} = {$contactID}
420 AND civicrm_relationship.{$contact_id_column} IN ({$contact_id_list})
421 AND civicrm_relationship.is_active = 1
f871c3a9 422 AND civicrm_relationship.is_permission_{$direction['from']}_{$direction['to']} {$is_perm_condition}
dddf4bf6 423 $AND_CAN_ACCESS_DELETED";
e4541c56 424 }
dddf4bf6 425
340be2e7 426 // FIXME: secondDegRelPermissions should be a setting
427 $config = CRM_Core_Config::singleton();
dddf4bf6 428 if ($config->secondDegRelPermissions) {
340be2e7 429 foreach ($directions as $first_direction) {
430 foreach ($directions as $second_direction) {
431 // add clause for deleted contacts, if the user doesn't have the permission to access them
432 $LEFT_JOIN_DELETED = $AND_CAN_ACCESS_DELETED = '';
433 if (!CRM_Core_Permission::check('access deleted contacts')) {
434 $LEFT_JOIN_DELETED = "LEFT JOIN civicrm_contact first_degree_contact ON first_degree_contact.id = second_degree_relationship.contact_id_{$second_direction['from']}\n";
435 $LEFT_JOIN_DELETED .= "LEFT JOIN civicrm_contact second_degree_contact ON second_degree_contact.id = second_degree_relationship.contact_id_{$second_direction['to']} ";
436 $AND_CAN_ACCESS_DELETED = "AND first_degree_contact.is_deleted = 0\n";
437 $AND_CAN_ACCESS_DELETED .= "AND second_degree_contact.is_deleted = 0 ";
dddf4bf6 438 }
340be2e7 439
440 $queries[] = "
441SELECT second_degree_relationship.contact_id_{$second_direction['to']} AS contact_id
442 FROM civicrm_relationship first_degree_relationship
2f5aa3cd 443 LEFT JOIN civicrm_relationship second_degree_relationship ON first_degree_relationship.contact_id_{$first_direction['to']} = second_degree_relationship.contact_id_{$second_direction['from']}
340be2e7 444 {$LEFT_JOIN_DELETED}
445 WHERE first_degree_relationship.contact_id_{$first_direction['from']} = {$contactID}
446 AND second_degree_relationship.contact_id_{$second_direction['to']} IN ({$contact_id_list})
447 AND first_degree_relationship.is_active = 1
f871c3a9 448 AND first_degree_relationship.is_permission_{$first_direction['from']}_{$first_direction['to']} {$is_perm_condition}
340be2e7 449 AND second_degree_relationship.is_active = 1
f871c3a9 450 AND second_degree_relationship.is_permission_{$second_direction['from']}_{$second_direction['to']} {$is_perm_condition}
340be2e7 451 $AND_CAN_ACCESS_DELETED";
dddf4bf6 452 }
453 }
454 }
455
456 // finally UNION the queries and call
340be2e7 457 $query = "(" . implode(")\nUNION DISTINCT (", $queries) . ")";
dddf4bf6 458 $result = CRM_Core_DAO::executeQuery($query);
459 while ($result->fetch()) {
67df1408 460 $result_set[(int) $result->contact_id] = TRUE;
dddf4bf6 461 }
67df1408 462 return array_keys($result_set);
dddf4bf6 463 }
464
86538308 465 /**
100fef9d 466 * @param int $contactID
c490a46a 467 * @param CRM_Core_Form $form
86538308
EM
468 * @param bool $redirect
469 *
470 * @return bool
471 */
00be9182 472 public static function validateOnlyChecksum($contactID, &$form, $redirect = TRUE) {
6a488035
TO
473 // check if this is of the format cs=XXX
474 if (!CRM_Contact_BAO_Contact_Utils::validChecksum($contactID,
353ffa53
TO
475 CRM_Utils_Request::retrieve('cs', 'String', $form, FALSE)
476 )
477 ) {
6a488035
TO
478 if ($redirect) {
479 // also set a message in the UF framework
480 $message = ts('You do not have permission to edit this contact record. Contact the site administrator if you need assistance.');
481 CRM_Utils_System::setUFMessage($message);
482
483 $config = CRM_Core_Config::singleton();
484 CRM_Core_Error::statusBounce($message,
485 $config->userFrameworkBaseURL
486 );
487 // does not come here, we redirect in the above statement
488 }
489 return FALSE;
490 }
491
a9a1ea2c 492 // set appropriate AUTH source
e8f14831 493 self::initChecksumAuthSrc(TRUE, $form);
a9a1ea2c 494
6a488035
TO
495 // so here the contact is posing as $contactID, lets set the logging contact ID variable
496 // CRM-8965
497 CRM_Core_DAO::executeQuery('SET @civicrm_user_id = %1',
be2fb01f 498 [1 => [$contactID, 'Integer']]
6a488035 499 );
77b97be7 500
6a488035
TO
501 return TRUE;
502 }
503
86538308
EM
504 /**
505 * @param bool $checkSumValidationResult
506 * @param null $form
507 */
00be9182 508 public static function initChecksumAuthSrc($checkSumValidationResult = FALSE, $form = NULL) {
a9a1ea2c 509 $session = CRM_Core_Session::singleton();
e8f14831 510 if ($checkSumValidationResult && $form && CRM_Utils_Request::retrieve('cs', 'String', $form, FALSE)) {
a9a1ea2c
DS
511 // if result is already validated, and url has cs, set the flag.
512 $session->set('authSrc', CRM_Core_Permission::AUTH_SRC_CHECKSUM);
0db6c3e1 513 }
4c9b6178 514 elseif (($session->get('authSrc') & CRM_Core_Permission::AUTH_SRC_CHECKSUM) == CRM_Core_Permission::AUTH_SRC_CHECKSUM) {
77b97be7 515 // if checksum wasn't present in REQUEST OR checksum result validated as FALSE,
a9a1ea2c
DS
516 // and flag was already set exactly as AUTH_SRC_CHECKSUM, unset it.
517 $session->set('authSrc', CRM_Core_Permission::AUTH_SRC_UNKNOWN);
518 }
519 }
520
86538308 521 /**
100fef9d 522 * @param int $contactID
c490a46a 523 * @param CRM_Core_Form $form
86538308
EM
524 * @param bool $redirect
525 *
526 * @return bool
527 */
00be9182 528 public static function validateChecksumContact($contactID, &$form, $redirect = TRUE) {
6a488035
TO
529 if (!self::allow($contactID, CRM_Core_Permission::EDIT)) {
530 // check if this is of the format cs=XXX
531 return self::validateOnlyChecksum($contactID, $form, $redirect);
532 }
533 return TRUE;
534 }
96025800 535
6a488035 536}