1: <?php
2: /*
3: * SimpleID
4: *
5: * Copyright (C) Kelvin Mo 2014-2025
6: *
7: * This program is free software; you can redistribute it and/or
8: * modify it under the terms of the GNU General Public
9: * License as published by the Free Software Foundation; either
10: * version 2 of the License, or (at your option) any later version.
11: *
12: * This program is distributed in the hope that it will be useful,
13: * but WITHOUT ANY WARRANTY; without even the implied warranty of
14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15: * General Public License for more details.
16: *
17: * You should have received a copy of the GNU General Public
18: * License along with this program; if not, write to the Free
19: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20: *
21: */
22:
23: namespace SimpleID\Protocols\OpenID\Extensions;
24:
25: use SimpleID\Module;
26: use SimpleID\Auth\AuthManager;
27: use SimpleID\Protocols\OpenID\Message;
28: use SimpleID\Protocols\OpenID\OpenIDResponseBuildEvent;
29: use SimpleID\Util\Events\UIBuildEvent;
30: use SimpleID\Util\Forms\FormBuildEvent;
31: use SimpleID\Util\Forms\FormSubmitEvent;
32: use SimpleID\Util\UI\Template;
33:
34: /**
35: * Implements the Simple Registration extension.
36: *
37: *
38: * @package simpleid
39: * @subpackage extensions
40: * @filesource
41: */
42: class SRegOpenIDExtensionModule extends Module {
43: /** Namespace for the Simple Registration extension */
44: const OPENID_NS_SREG = 'http://openid.net/extensions/sreg/1.1';
45:
46: /** @var AuthManager */
47: private $auth;
48:
49: public function __construct() {
50: parent::__construct();
51: $this->auth = AuthManager::instance();
52: }
53:
54: /**
55: * @see SimpleID\Protocols\OpenID\OpenIDResponseBuildEvent
56: * @return void
57: */
58: public function onOpenIDResponseBuildEvent(OpenIDResponseBuildEvent $event) {
59: // We only deal with positive assertions
60: if (!$event->isPositiveAssertion()) return;
61:
62: // We only respond if the extension is requested
63: $request = $event->getRequest();
64: $response = $event->getResponse();
65:
66: if (!$request->hasExtension(self::OPENID_NS_SREG)) return;
67:
68: $user = $this->auth->getUser();
69:
70: $sreg_request = $request->getParamsForExtension(self::OPENID_NS_SREG);
71: $required = (isset($sreg_request['required'])) ? explode(',', $sreg_request['required']) : [];
72: $optional = (isset($sreg_request['optional'])) ? explode(',', $sreg_request['optional']) : [];
73: $fields = array_merge($required, $optional);
74:
75: $alias = $response->getAliasForExtension(self::OPENID_NS_SREG, 'sreg');
76:
77: if ($request->getVersion() == Message::OPENID_VERSION_2) $response['ns.' . $alias] = self::OPENID_NS_SREG;
78:
79: foreach ($fields as $field) {
80: $value = $this->getValue($user, $field);
81: if ($value != NULL) $response[$alias . '.' . $field] = $value;
82: }
83: }
84:
85: /**
86: * @see SimpleID\Util\Forms\FormBuildEvent
87: * @return void
88: */
89: function onOpenidConsentFormBuild(FormBuildEvent $event) {
90: $form_state = $event->getFormState();
91: /** @var \SimpleID\Protocols\OpenID\Request */
92: $request = $form_state->getRequest();
93: /** @var \SimpleID\Protocols\OpenID\Response */
94: $response = $form_state->getResponse();
95: $prefs = $form_state['prefs'];
96:
97: // We only respond if the extension is requested
98: if (!$request->hasExtension(self::OPENID_NS_SREG)) return;
99:
100: $user = $this->auth->getUser();
101:
102: $sreg_request = $request->getParamsForExtension(self::OPENID_NS_SREG);
103: $required = (isset($sreg_request['required'])) ? explode(',', $sreg_request['required']) : [];
104: $optional = (isset($sreg_request['optional'])) ? explode(',', $sreg_request['optional']) : [];
105: $fields = array_merge($required, $optional);
106:
107: // Check we have any response to consent to
108: if (!count($response->getParamsForExtension(self::OPENID_NS_SREG))) return;
109:
110: $tpl = new Template();
111: $hive = [
112: 'module' => 'sreg',
113: 'userinfo_label' => $this->f3->get('intl.common.consent.send_label'),
114: 'name_label' => $this->f3->get('intl.common.name'),
115: 'value_label' => $this->f3->get('intl.common.value'),
116: 'fields' => []
117: ];
118:
119: if (isset($sreg_request['policy_url'])) {
120: $hive['policy_url'] = $sreg_request['policy_url'];
121: }
122:
123: foreach ($fields as $field) {
124: $value = $this->getValue($user, $field);
125:
126: if ($value != NULL) {
127: $form_field = [
128: 'id' => $field,
129: 'html_id' => $field,
130: 'name' => $field,
131: 'value' => $value,
132: ];
133:
134: if (in_array($field, $required)) {
135: $form_field['required'] = true;
136: } else {
137: $form_field['required'] = false;
138: $form_field['checked'] = (!isset($prefs['consents']['sreg']) || in_array($field, $prefs['consents']['sreg']));
139: }
140:
141: $hive['fields'][] = $form_field;
142: }
143: }
144:
145: $event->addBlock('openid_consent_sreg', $tpl->render('openid_userinfo_consent.html', false, $hive), 0);
146: }
147:
148: /**
149: * @see SimpleID\Util\Forms\FormSubmitEvent
150: * @return void
151: */
152: function onOpenidConsentFormSubmit(FormSubmitEvent $event) {
153: $form_state = $event->getFormState();
154: /** @var \SimpleID\Protocols\OpenID\Response */
155: $response = $form_state->getResponse();
156: $prefs =& $form_state->ref('prefs');
157:
158: // We only respond if the extension is requested
159: if (!$response->hasExtension(self::OPENID_NS_SREG)) return;
160:
161: $fields = array_keys($response->getParamsForExtension(self::OPENID_NS_SREG));
162: $alias = $response->getAliasForExtension(self::OPENID_NS_SREG, 'sreg');
163: $form = $this->f3->get('POST.prefs.consents.sreg');
164:
165: foreach ($fields as $field) {
166: if (isset($response[$alias . '.' . $field])) {
167: if (!in_array($field, $form)) {
168: unset($response[$alias . '.' . $field]);
169: }
170: }
171: }
172:
173: if (count(array_keys($response->getParamsForExtension(self::OPENID_NS_SREG))) == 0) {
174: // We have removed all the responses, so we remove the namespace as well
175: unset($response['ns.' . $alias]);
176: }
177:
178: $prefs['consents']['sreg'] = $form;
179: }
180:
181: /**
182: * @return void
183: */
184: public function onProfileBlocks(UIBuildEvent $event) {
185: $user = $this->auth->getUser();
186:
187: if (!isset($user['sreg'])) return;
188:
189: $tpl = new Template();
190: $hive = [
191: 'userinfo_label' => $this->f3->get('intl.core.openid.sreg.profile_block'),
192: 'name_label' => $this->f3->get('intl.common.name'),
193: 'value_label' => $this->f3->get('intl.common.value'),
194: 'info' => $user['sreg']
195: ];
196:
197: $event->addBlock('sreg', $tpl->render('openid_userinfo_profile.html', false, $hive), 0, [
198: 'title' => $this->f3->get('intl.core.openid.sreg.sreg_title')
199: ]);
200: }
201:
202:
203: /**
204: * Looks up the value of a specified Simple Registration Extension field.
205: *
206: * This function looks up the sreg section of the user's identity file. If the
207: * specified field cannot be found, it looks up the corresponding field in the
208: * OpenID Connect user information (user_info section).
209: *
210: * @param \SimpleID\Models\User $user the user to look up
211: * @param string $field the field to look up
212: * @return string|null the value or NULL if not found
213: */
214: protected function getValue($user, $field) {
215: $sreg = (isset($user['sreg'])) ? $user['sreg'] : [];
216: $userinfo = (isset($user['userinfo'])) ? $user['userinfo'] : [];
217:
218: if (isset($sreg[$field])) {
219: return $sreg[$field];
220: } else {
221: switch ($field) {
222: case 'nickname':
223: case 'email':
224: if (isset($userinfo[$field])) return $userinfo[$field];
225: break;
226: case 'fullname':
227: if (isset($userinfo['name'])) return $userinfo['name'];
228: break;
229: case 'timezone':
230: if (isset($userinfo['zoneinfo'])) return $userinfo['zoneinfo'];
231: break;
232: case 'gender':
233: if (isset($userinfo['gender'])) return strtoupper(substr($userinfo['gender'], 0, 1));
234: break;
235: case 'postcode':
236: if (isset($userinfo['address']['postal_code'])) return $userinfo['address']['postcal_code'];
237: break;
238: default:
239: return NULL;
240: }
241: return NULL;
242: }
243: }
244: }
245:
246: ?>
247: