Merge pull request #16126 from civicrm/5.21
[civicrm-core.git] / CRM / Core / Key.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
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 |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 * $Id$
17 *
18 */
19 class CRM_Core_Key {
20 public static $_key = NULL;
21
22 public static $_sessionID = NULL;
23
24 /**
25 * Generate a private key per session and store in session.
26 *
27 * @return string
28 * private key for this session
29 */
30 public static function privateKey() {
31 if (!self::$_key) {
32 $session = CRM_Core_Session::singleton();
33 self::$_key = $session->get('qfPrivateKey');
34 if (!self::$_key) {
35 self::$_key = md5(uniqid(mt_rand(), TRUE)) . md5(uniqid(mt_rand(), TRUE));
36 $session->set('qfPrivateKey', self::$_key);
37 }
38 }
39 return self::$_key;
40 }
41
42 /**
43 * @return mixed|null|string
44 */
45 public static function sessionID() {
46 if (!self::$_sessionID) {
47 $session = CRM_Core_Session::singleton();
48 self::$_sessionID = $session->get('qfSessionID');
49 if (!self::$_sessionID) {
50 self::$_sessionID = session_id();
51 $session->set('qfSessionID', self::$_sessionID);
52 }
53 }
54 return self::$_sessionID;
55 }
56
57 /**
58 * Generate a form key based on form name, the current user session
59 * and a private key. Modelled after drupal's form API
60 *
61 * @param string $name
62 * @param bool $addSequence
63 * Should we add a unique sequence number to the end of the key.
64 *
65 * @return string
66 * valid formID
67 */
68 public static function get($name, $addSequence = FALSE) {
69 $privateKey = self::privateKey();
70 $sessionID = self::sessionID();
71 $key = md5($sessionID . $name . $privateKey);
72
73 if ($addSequence) {
74 // now generate a random number between 1 and 100K and add it to the key
75 // so that we can have forms in mutiple tabs etc
76 $key = $key . '_' . mt_rand(1, 10000);
77 }
78 return $key;
79 }
80
81 /**
82 * Validate a form key based on the form name.
83 *
84 * @param string $key
85 * @param string $name
86 * @param bool $addSequence
87 *
88 * @return string
89 * if valid, else null
90 */
91 public static function validate($key, $name, $addSequence = FALSE) {
92 if (!is_string($key)) {
93 return NULL;
94 }
95
96 if ($addSequence) {
97 list($k, $t) = explode('_', $key);
98 if ($t < 1 || $t > 10000) {
99 return NULL;
100 }
101 }
102 else {
103 $k = $key;
104 }
105
106 $privateKey = self::privateKey();
107 $sessionID = self::sessionID();
108 if ($k != md5($sessionID . $name . $privateKey)) {
109 return NULL;
110 }
111 return $key;
112 }
113
114 /**
115 * @param $key
116 *
117 * @return bool
118 */
119 public static function valid($key) {
120 // a valid key is a 32 digit hex number
121 // followed by an optional _ and a number between 1 and 10000
122 if (strpos('_', $key) !== FALSE) {
123 list($hash, $seq) = explode('_', $key);
124
125 // ensure seq is between 1 and 10000
126 if (!is_numeric($seq) ||
127 $seq < 1 ||
128 $seq > 10000
129 ) {
130 return FALSE;
131 }
132 }
133 else {
134 $hash = $key;
135 }
136
137 // ensure that hash is a 32 digit hex number
138 return preg_match('#[0-9a-f]{32}#i', $hash) ? TRUE : FALSE;
139 }
140
141 }