d12a03a5d07a76b81113c5d936195b8a43f241ef
[civicrm-core.git] / ext / oauth-client / CRM / OAuth / MailSetup.php
1 <?php
2
3 class CRM_OAuth_MailSetup {
4
5 /**
6 * Return a list of setup-options based on OAuth2 services.
7 *
8 * @see CRM_Utils_Hook::mailSetupActions()
9 */
10 public static function buildSetupLinks() {
11 $clients = Civi\Api4\OAuthClient::get(0)->addWhere('is_active', '=', 1)->execute();
12 $providers = Civi\Api4\OAuthProvider::get(0)->execute()->indexBy('name');
13
14 $setupActions = [];
15 foreach ($clients as $client) {
16 $provider = $providers[$client['provider']] ?? NULL;
17 if ($provider === NULL) {
18 continue;
19 }
20 // v api OptionValue.get option_group_id=mail_protocol
21 if (!empty($provider['mailSettingsTemplate'])) {
22 $setupActions['oauth_' . $client['id']] = [
23 'title' => sprintf('%s (ID #%s)', $provider['title'] ?? $provider['name'] ?? ts('OAuth2'), $client['id']),
24 'callback' => ['CRM_OAuth_MailSetup', 'setup'],
25 'oauth_client_id' => $client['id'],
26 ];
27 }
28 }
29
30 return $setupActions;
31 }
32
33 /**
34 * When a user chooses to add one of our mail options, we kick off
35 * the authorization-code workflow.
36 *
37 * @param array $setupAction
38 * The chosen descriptor from mailSetupActions.
39 * @return array
40 * With keys:
41 * - url: string, the final URL to go to.
42 * @see CRM_Utils_Hook::mailSetupActions()
43 */
44 public static function setup($setupAction) {
45 $authCode = Civi\Api4\OAuthClient::authorizationCode(0)
46 ->addWhere('id', '=', $setupAction['oauth_client_id'])
47 ->setStorage('OAuthSysToken')
48 ->setTag('MailSettings:setup')
49 ->execute()
50 ->single();
51
52 return [
53 'url' => $authCode['url'],
54 ];
55 }
56
57 /**
58 * When the user returns with a token, we add a new record to
59 * civicrm_mail_settings with defaults and redirect to the edit screen.
60 *
61 * @param array $token
62 * OAuthSysToken
63 * @param string $nextUrl
64 */
65 public static function onReturn($token, &$nextUrl) {
66 if ($token['tag'] !== 'MailSettings:setup') {
67 return;
68 }
69
70 $client = \Civi\Api4\OAuthClient::get(0)->addWhere('id', '=', $token['client_id'])->execute()->single();
71 $provider = \Civi\Api4\OAuthProvider::get(0)->addWhere('name', '=', $client['provider'])->execute()->single();
72
73 $vars = ['token' => $token, 'client' => $client, 'provider' => $provider];
74 $mailSettings = civicrm_api4('MailSettings', 'create', [
75 'values' => self::evalArrayTemplate($provider['mailSettingsTemplate'], $vars),
76 ])->single();
77
78 \Civi\Api4\OAuthSysToken::update(0)
79 ->addWhere('id', '=', $token['id'])
80 ->setValues(['tag' => 'MailSettings:' . $mailSettings['id']])
81 ->execute();
82
83 CRM_Core_Session::setStatus(
84 ts('Here are the account defaults we detected for %1. Please check them carefully.', [
85 1 => $mailSettings['name'],
86 ]),
87 ts('Account created!'),
88 'info'
89 );
90
91 $nextUrl = CRM_Utils_System::url('civicrm/admin/mailSettings', [
92 'action' => 'update',
93 'id' => $mailSettings['id'],
94 'reset' => 1,
95 ], TRUE, NULL, FALSE);
96 }
97
98 /**
99 * @param array $template
100 * List of key-value expressions.
101 * Ex: ['name' => '{{person.first}} {{person.last}}']
102 * Expressions begin with the dotted-name of a variable.
103 * Optionally, the value may be piped through other functions
104 * @param array $vars
105 * Array tree of data to interpolate.
106 * @return array
107 * The template array, with '{{...}}' expressions evaluated.
108 */
109 public static function evalArrayTemplate($template, $vars) {
110 $filters = [
111 'getMailDomain' => function($v) {
112 $parts = explode('@', $v);
113 return $parts[1] ?? NULL;
114 },
115 'getMailUser' => function($v) {
116 $parts = explode('@', $v);
117 return $parts[0] ?? NULL;
118 },
119 ];
120
121 $lookupVars = function($m) use ($vars, $filters) {
122 $parts = explode('|', $m[1]);
123 $value = (string) CRM_Utils_Array::pathGet($vars, explode('.', array_shift($parts)));
124 foreach ($parts as $part) {
125 if (isset($filters[$part])) {
126 $value = $filters[$part]($value);
127 }
128 else {
129 $value = NULL;
130 }
131 }
132 return $value;
133 };
134
135 $values = [];
136 foreach ($template as $key => $value) {
137 $values[$key] = is_string($value)
138 ? preg_replace_callback(';{{([a-zA-Z0-9_\.\|]+)}};', $lookupVars, $value)
139 : $value;
140 }
141 return $values;
142 }
143
144 /**
145 * If we have a stored token for this for this, then use it.
146 *
147 * @see CRM_Utils_Hook::alterMailStore()
148 */
149 public static function alterMailStore(&$mailSettings) {
150 $token = civicrm_api4('OAuthSysToken', 'refresh', [
151 'checkPermissions' => FALSE,
152 'where' => [['tag', '=', 'MailSettings:' . $mailSettings['id']]],
153 'orderBy' => ['id' => 'DESC'],
154 ])->first();
155
156 if ($token === NULL) {
157 return;
158 }
159 // Not certain if 'refresh' will complain about staleness. Doesn't hurt to double-check.
160 if (empty($token['access_token']) || $token['expires'] < CRM_Utils_Time::getTimeRaw()) {
161 throw new \OAuthException("Found invalid token for mail store #" . $mailSettings['id']);
162 }
163
164 $mailSettings['auth'] = 'XOAuth2';
165 $mailSettings['password'] = $token['access_token'];
166 }
167
168 }