Merge pull request #23710 from eileenmcnaughton/civi_import
[civicrm-core.git] / Civi / Pipe / PublicMethods.php
CommitLineData
b4454468
TO
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
12namespace Civi\Pipe;
13
5e13f388
TO
14use Civi\Authx\AuthxException;
15
b4454468
TO
16/**
17 * Collection of methods to expose to the pipe session. Any public method will be accessible.
18 */
19class PublicMethods {
20
f1ab7a2e
TO
21 /**
22 * How should API errors be reported?
23 *
24 * @var string
25 * - 'array': Traditional array format from civicrm_api(). Maximizes consistency of error data.
26 * - 'exception': Converted to an exception. Somewhat lossy. Improves out-of-box DX on stricter JSON-RPC clients.
27 */
870ffb26 28 protected $apiError = 'exception';
f1ab7a2e 29
5e13f388
TO
30 /**
31 * Should API calls use permission checks?
32 *
33 * Note: This property is only consulted on trusted connections. It is ignored on untrusted connections.
34 *
35 * @var bool
36 */
37 protected $apiCheckPermissions = TRUE;
38
b4454468
TO
39 /**
40 * Send a request to APIv3.
41 *
25804d7a 42 * @param \Civi\Pipe\PipeSession $session
b4454468
TO
43 * @param array $request
44 * Tuple: [$entity, $action, $params]
45 * @return array|\Civi\Api4\Generic\Result|int
46 */
e98a5c1f 47 public function api3(PipeSession $session, array $request) {
5e13f388
TO
48 $request[2] = array_merge($request[2] ?? [], ['version' => 3]);
49 $request[2]['check_permissions'] = !$session->isTrusted() || $this->isCheckPermissions($request[2], 'check_permissions');
50 // ^^ Untrusted sessions MUST check perms. All sessions DEFAULT to checking perms. Trusted sessions MAY disable perms.
f1ab7a2e
TO
51 switch ($this->apiError) {
52 case 'array':
53 return civicrm_api(...$request);
54
55 case 'exception':
56 return civicrm_api3(...$request);
57
58 default:
59 throw new \CRM_Core_Exception("Invalid API error-handling mode: $this->apiError");
60 }
b4454468
TO
61 }
62
63 /**
64 * Send a request to APIv4.
65 *
25804d7a 66 * @param \Civi\Pipe\PipeSession $session
b4454468
TO
67 * @param array $request
68 * Tuple: [$entity, $action, $params]
69 * @return array|\Civi\Api4\Generic\Result|int
70 */
e98a5c1f 71 public function api4(PipeSession $session, array $request) {
5e13f388
TO
72 $request[2] = array_merge($request[2] ?? [], ['version' => 4]);
73 $request[2]['checkPermissions'] = !$session->isTrusted() || $this->isCheckPermissions($request[2], 'checkPermissions');
74 // ^^ Untrusted sessions MUST check perms. All sessions DEFAULT to checking perms. Trusted sessions MAY disable perms.
f1ab7a2e
TO
75 switch ($this->apiError) {
76 case 'array':
77 return civicrm_api(...$request);
78
79 case 'exception':
80 return civicrm_api4(...$request);
81
82 default:
83 throw new \CRM_Core_Exception("Invalid API error-handling mode: $this->apiError");
84 }
b4454468
TO
85 }
86
87 /**
88 * Simple test; send/receive a fragment of data.
89 *
25804d7a 90 * @param \Civi\Pipe\PipeSession $session
e98a5c1f
TO
91 * @param array $request
92 * @return array
b4454468 93 */
e98a5c1f 94 public function echo(PipeSession $session, array $request) {
b4454468
TO
95 return $request;
96 }
97
98 /**
99 * Set active user.
100 *
25804d7a 101 * @param \Civi\Pipe\PipeSession $session
02dc5c62 102 * @param array{contactId: int, userId: int, user: string, cred: string} $request
b4454468
TO
103 * @return array|\Civi\Api4\Generic\Result|int
104 */
e98a5c1f 105 public function login(PipeSession $session, array $request) {
b4454468 106 if (!function_exists('authx_login')) {
eaa0d7ac 107 throw new \CRM_Core_Exception('Cannot authenticate. Authx is not configured.');
b4454468 108 }
5e13f388
TO
109
110 $redact = function(?array $authx) {
111 return $authx ? \CRM_Utils_Array::subset($authx, ['contactId', 'userId']) : FALSE;
112 };
113
114 $principal = \CRM_Utils_Array::subset($request, ['contactId', 'userId', 'user']);
115 if ($principal && $session->isTrusted()) {
02dc5c62 116 return $redact(authx_login(['flow' => 'script', 'principal' => $principal]));
5e13f388
TO
117 }
118 elseif ($principal && !$session->isTrusted()) {
eaa0d7ac 119 throw new AuthxException('Session is not trusted.');
5e13f388
TO
120 }
121 elseif (isset($request['cred'])) {
122 $authn = new \Civi\Authx\Authenticator();
123 $authn->setRejectMode('exception');
02dc5c62 124 if ($authn->auth(NULL, ['flow' => 'pipe', 'cred' => $request['cred']])) {
eaa0d7ac 125 return $redact(\CRM_Core_Session::singleton()->get('authx'));
5e13f388
TO
126 }
127 }
128
eaa0d7ac 129 throw new AuthxException('Cannot authenticate. Must specify principal/credentials.');
b4454468
TO
130 }
131
132 /**
133 * Set ephemeral session options.
134 *
25804d7a 135 * @param \Civi\Pipe\PipeSession $session
9aa53a5d 136 * @param array{bufferSize: int, responsePrefix: int} $request
b4454468 137 * Any updates to perform. May be empty/omitted.
9aa53a5d 138 * @return array{bufferSize: int, responsePrefix: int}
b4454468
TO
139 * List of updated options.
140 * If the list of updates was empty, then return all options.
141 */
e98a5c1f 142 public function options(PipeSession $session, array $request) {
f1ab7a2e 143 $storageMap = [
5e13f388 144 'apiCheckPermissions' => $this,
f1ab7a2e 145 'apiError' => $this,
9aa53a5d 146 'bufferSize' => $session,
f1ab7a2e 147 'responsePrefix' => $session,
b4454468
TO
148 ];
149
5e13f388
TO
150 if (!$session->isTrusted() && array_key_exists('apiCheckPermissions', $request)) {
151 unset($request['apiCheckPermissions']);
152 }
153
f1ab7a2e
TO
154 $get = function($storage, $name) {
155 if (method_exists($storage, 'get' . ucfirst($name))) {
156 return $storage->{'get' . ucfirst($name)}();
157 }
158 else {
159 return $storage->{$name};
160 }
161 };
162
163 $set = function($storage, $name, $value) use ($get) {
164 if (method_exists($storage, 'set' . ucfirst($name))) {
165 $storage->{'set' . ucfirst($name)}($value);
166 }
167 else {
168 $storage->{$name} = $value;
169 }
170 return $get($storage, $name);
171 };
172
b4454468
TO
173 $result = [];
174 if (!empty($request)) {
f1ab7a2e
TO
175 foreach ($request as $name => $value) {
176 if (isset($storageMap[$name])) {
177 $result[$name] = $set($storageMap[$name], $name, $value);
b4454468
TO
178 }
179 }
180 }
181 else {
f1ab7a2e
TO
182 foreach ($storageMap as $name => $storage) {
183 $result[$name] = $get($storage, $name);
b4454468
TO
184 }
185 }
186 return $result;
187 }
188
5e13f388
TO
189 private function isCheckPermissions(array $params, string $field) {
190 return isset($params[$field]) ? $params[$field] : $this->apiCheckPermissions;
191 }
192
b4454468 193}