Merge pull request #22277 from demeritcowboy/isdir2
[civicrm-core.git] / ext / authx / authx.php
CommitLineData
7946d659
TO
1<?php
2
3require_once 'authx.civix.php';
4// phpcs:disable
5use CRM_Authx_ExtensionUtil as E;
6// phpcs:enable
7
3a429e3f 8Civi::dispatcher()->addListener('civi.invoke.auth', function($e) {
476ba2b9
TO
9 $params = ($_SERVER['REQUEST_METHOD'] === 'GET') ? $_GET : $_POST;
10 $siteKey = $_SERVER['HTTP_X_CIVI_KEY'] ?? $params['_authxSiteKey'] ?? NULL;
11
3a429e3f 12 if (!empty($_SERVER['HTTP_X_CIVI_AUTH'])) {
476ba2b9 13 return (new \Civi\Authx\Authenticator())->auth($e, ['flow' => 'xheader', 'cred' => $_SERVER['HTTP_X_CIVI_AUTH'], 'siteKey' => $siteKey]);
3a429e3f
TO
14 }
15
16 if (!empty($_SERVER['HTTP_AUTHORIZATION'])) {
476ba2b9 17 return (new \Civi\Authx\Authenticator())->auth($e, ['flow' => 'header', 'cred' => $_SERVER['HTTP_AUTHORIZATION'], 'siteKey' => $siteKey]);
3a429e3f
TO
18 }
19
3a429e3f
TO
20 if (!empty($params['_authx'])) {
21 if ((implode('/', $e->args) === 'civicrm/authx/login')) {
476ba2b9 22 (new \Civi\Authx\Authenticator())->auth($e, ['flow' => 'login', 'cred' => $params['_authx'], 'useSession' => TRUE, 'siteKey' => $siteKey]);
3a429e3f
TO
23 _authx_redact(['_authx']);
24 }
25 elseif (!empty($params['_authxSes'])) {
476ba2b9 26 (new \Civi\Authx\Authenticator())->auth($e, ['flow' => 'auto', 'cred' => $params['_authx'], 'useSession' => TRUE, 'siteKey' => $siteKey]);
a196e838
TO
27 if ($_SERVER['REQUEST_METHOD'] === 'GET') {
28 _authx_reload(implode('/', $e->args), $_SERVER['QUERY_STRING']);
29 }
30 else {
31 _authx_redact(['_authx', '_authxSes']);
32 }
3a429e3f
TO
33 }
34 else {
476ba2b9 35 (new \Civi\Authx\Authenticator())->auth($e, ['flow' => 'param', 'cred' => $params['_authx'], 'siteKey' => $siteKey]);
3a429e3f
TO
36 _authx_redact(['_authx']);
37 }
38 }
df9b24b2 39
8d5feabb
TO
40 // Accept legacy auth (?key=...&api_key=...) for 'civicrm/ajax/rest' and 'civicrm/ajax/api4/*'.
41 // The use of `?key=` could clash on some endpoints. Only accept on a small list of endpoints that are compatible with it.
42 if (count($e->args) > 2 && $e->args[1] === 'ajax' && in_array($e->args[2], ['rest', 'api4'])) {
43 if ((!empty($_REQUEST['api_key']) || !empty($_REQUEST['key']))) {
44 return (new \Civi\Authx\LegacyRestAuthenticator())->auth($e, ['flow' => 'legacyrest', 'cred' => 'Bearer ' . $_REQUEST['api_key'] ?? '', 'siteKey' => $_REQUEST['key'] ?? NULL]);
45 }
df9b24b2 46 }
3a429e3f
TO
47});
48
97209e61
TO
49/**
50 * Perform a system login.
51 *
52 * This is useful for backend scripts that need to switch to a specific user.
53 *
54 * As needed, this will update the Civi session and CMS data.
55 *
56 * @param array{flow: ?string, useSession: ?bool, principal: ?array, cred: ?string,} $details
57 * Describe the authentication process with these properties:
58 *
59 * - string $flow (default 'script');
60 * The type of authentication flow being used
61 * Ex: 'param', 'header', 'auto'
62 * - bool $useSession (default FALSE)
63 * If TRUE, then the authentication should be persistent (in a session variable).
64 * If FALSE, then the authentication should be ephemeral (single page-request).
65 *
66 * And then ONE of these properties to describe the user/principal:
67 *
68 * - string $cred
69 * The credential, as formatted in the 'Authorization' header.
70 * Ex: 'Bearer 12345', 'Basic ASDFFDSA=='
71 * - array $principal
72 * Description of a validated principal.
73 * Must include 'contactId', 'userId', xor 'user'
74 * @return array{contactId: int, userId: ?int, flow: string, credType: string, useSession: bool}
75 * An array describing the authenticated session.
76 * @throws \Civi\Authx\AuthxException
77 */
78function authx_login(array $details): array {
79 $defaults = ['flow' => 'script', 'useSession' => FALSE];
80 $details = array_merge($defaults, $details);
81 $auth = new \Civi\Authx\Authenticator();
82 $auth->setRejectMode('exception');
83 $auth->auth(NULL, array_merge($defaults, $details));
84 return \CRM_Core_Session::singleton()->get("authx");
85}
86
3a429e3f
TO
87/**
88 * @return \Civi\Authx\AuthxInterface
89 */
90function _authx_uf() {
91 $class = 'Civi\\Authx\\' . CIVICRM_UF;
92 return class_exists($class) ? new $class() : new \Civi\Authx\None();
93}
94
95/**
96 * For parameter-based authentication, this option will hide parameters.
97 * This is mostly a precaution, hedging against the possibility that some routes
98 * make broad use of $_GET or $_PARAMS.
99 *
100 * @param array $keys
101 */
102function _authx_redact(array $keys) {
103 foreach ($keys as $key) {
104 unset($_POST[$key], $_GET[$key], $_REQUEST[$key]);
105 }
106}
107
a196e838
TO
108/**
109 * Reload the current page-view.
110 *
111 * @param string $route
112 * @param string $queryString
113 */
114function _authx_reload($route, $queryString) {
115 parse_str($queryString, $query);
116 foreach (array_keys($query) as $key) {
117 if (CRM_Utils_String::startsWith($key, '_authx')) {
118 unset($query[$key]);
119 }
120 }
121 $url = CRM_Utils_System::url($route, $query, TRUE, NULL, FALSE, CRM_Core_Config::singleton()->userSystem->isFrontEndPage());
122 CRM_Utils_System::redirect($url);
123}
124
7946d659
TO
125/**
126 * Implements hook_civicrm_config().
127 *
128 * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config/
129 */
130function authx_civicrm_config(&$config) {
131 _authx_civix_civicrm_config($config);
132}
133
7946d659
TO
134/**
135 * Implements hook_civicrm_install().
136 *
137 * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install
138 */
139function authx_civicrm_install() {
140 _authx_civix_civicrm_install();
141}
142
143/**
144 * Implements hook_civicrm_postInstall().
145 *
146 * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
147 */
148function authx_civicrm_postInstall() {
149 _authx_civix_civicrm_postInstall();
150}
151
152/**
153 * Implements hook_civicrm_uninstall().
154 *
155 * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall
156 */
157function authx_civicrm_uninstall() {
158 _authx_civix_civicrm_uninstall();
159}
160
161/**
162 * Implements hook_civicrm_enable().
163 *
164 * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable
165 */
166function authx_civicrm_enable() {
167 _authx_civix_civicrm_enable();
168}
169
170/**
171 * Implements hook_civicrm_disable().
172 *
173 * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable
174 */
175function authx_civicrm_disable() {
176 _authx_civix_civicrm_disable();
177}
178
179/**
180 * Implements hook_civicrm_upgrade().
181 *
182 * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade
183 */
184function authx_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
185 return _authx_civix_civicrm_upgrade($op, $queue);
186}
187
7946d659
TO
188/**
189 * Implements hook_civicrm_entityTypes().
190 *
191 * Declare entity types provided by this module.
192 *
193 * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes
194 */
195function authx_civicrm_entityTypes(&$entityTypes) {
196 _authx_civix_civicrm_entityTypes($entityTypes);
197}
198
476ba2b9
TO
199/**
200 * Implements hook_civicrm_permission().
201 *
202 * @see CRM_Utils_Hook::permission()
203 */
204function authx_civicrm_permission(&$permissions) {
27212330 205 $permissions['authenticate with password'] = E::ts('AuthX: Authenticate to services with password');
206 $permissions['authenticate with api key'] = E::ts('AuthX: Authenticate to services with API key');
476ba2b9
TO
207}
208
7946d659
TO
209// --- Functions below this ship commented out. Uncomment as required. ---
210
211/**
212 * Implements hook_civicrm_preProcess().
213 *
214 * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_preProcess
215 */
216//function authx_civicrm_preProcess($formName, &$form) {
217//
218//}
219
220/**
221 * Implements hook_civicrm_navigationMenu().
222 *
223 * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_navigationMenu
224 */
225//function authx_civicrm_navigationMenu(&$menu) {
226// _authx_civix_insert_navigation_menu($menu, 'Mailings', array(
227// 'label' => E::ts('New subliminal message'),
228// 'name' => 'mailing_subliminal_message',
229// 'url' => 'civicrm/mailing/subliminal',
230// 'permission' => 'access CiviMail',
231// 'operator' => 'OR',
232// 'separator' => 0,
233// ));
234// _authx_civix_navigationMenu($menu);
235//}