blob: 3982be0f6e5d577dfc9fdfcae76f4ef94e898112 [file] [log] [blame]
Nico Huberee52fbc2023-06-24 11:52:57 +00001<?php
2/*
3 * SimpleID
4 *
5 * Copyright (C) Kelvin Mo 2009
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 * $Id$
22 */
23
24/**
25 * Implements the popup and icon modes from the User Interface extension
26 *
27 * @package simpleid
28 * @subpackage extensions
29 * @filesource
30 */
31
32/** Namespace for the User Interface extension */
33define('OPENID_NS_UI', 'http://specs.openid.net/extensions/ui/1.0');
34
35/**
36 * Returns the popup mode in SimpleID XRDS document
37 *
38 * @return array
39 * @see hook_xrds_types()
40 */
41function ui_xrds_types() {
42 return array(
43 'http://specs.openid.net/extensions/ui/1.0/mode/popup',
44 'http://specs.openid.net/extensions/ui/1.0/icon'
45 );
46}
47
48/**
49 * Detects the openid.ui.x-has-session parameter and processes it accordingly.
50 *
51 * @return array
52 * @see hook_response()
53 */
54function ui_response($assertion, $request) {
55 global $user;
56 global $version;
57
58 // We only deal with negative assertions
59 if ($assertion) return array();
60
61 // We only respond if the extension is requested
62 if (!openid_extension_requested(OPENID_NS_UI, $request)) return array();
63
64 // We only deal with openid.ui.x-has-session requests
65 $filtered_request = openid_extension_filter_request(OPENID_NS_UI, $request);
66 if (!isset($filtered_request['mode']) || ($filtered_request['mode'] != 'x-has-session')) return array();
67
68 // If user is null, there is no active session
69 if ($user == NULL) return array();
70
71 // There is an active session
72 $alias = openid_extension_alias(OPENID_NS_UI);
73 $response = array();
74
75 $response['openid.ns.' . $alias] = OPENID_NS_UI;
76 $response['openid.' . $alias . '.mode'] = 'x-has-session';
77
78 return $response;
79}
80
81/**
82 * Returns an array of fields that need signing.
83 *
84 * @see hook_signed_fields()
85 */
86function ui_signed_fields($response) {
87 // We only respond if the extension is requested
88 if (!openid_extension_requested(OPENID_NS_UI, $response)) return array();
89
90 $fields = array_keys(openid_extension_filter_request(OPENID_NS_UI, $response));
91 $alias = openid_extension_alias(OPENID_NS_UI);
92 $signed_fields = array();
93
94 if (isset($response['openid.ns.' . $alias])) $signed_fields[] = 'ns.' . $alias;
95 foreach ($fields as $field) {
96 if (isset($response['openid.' . $alias . '.' . $field])) $signed_fields[] = $alias . '.' . $field;
97 }
98
99 return $signed_fields;
100}
101
102/**
103 * Detects the presence of the UI extension and modifies the login form
104 * accordingly.
105 *
106 * @param string $destination
107 * @param string $state
108 * @see hook_user_login_form()
109 */
110function ui_user_login_form($destination, $state) {
111 if (($destination != 'continue') || (!$state)) return;
112
113 $request = unpickle($state);
114 openid_parse_request($request);
115
116 // Skip if popup does not exist
117 if (!openid_extension_requested(OPENID_NS_UI, $request)) return;
118
119 $filtered_request = openid_extension_filter_request(OPENID_NS_UI, $request);
120
121 if (isset($filtered_request['mode']) && ($filtered_request['mode'] == 'popup')) _ui_insert_css_js();
122
123 return;
124}
125
126/**
127 * Detects the presence of the UI extension and modifies the relying party
128 * verification form accordingly.
129 *
130 * @param array $request
131 * @param array $response
132 * @param array $rp
133 * @return string
134 * @see hook_consent_form()
135 */
136function ui_consent_form($request, $response, $rp) {
137 // Skip if popup does not exist
138 if (!openid_extension_requested(OPENID_NS_UI, $request)) return '';
139
140 $filtered_request = openid_extension_filter_request(OPENID_NS_UI, $request);
141
142 if (isset($filtered_request['mode']) && ($filtered_request['mode'] == 'popup')) _ui_insert_css_js();
143
144 if (isset($filtered_request['icon']) && ($filtered_request['icon'] == 'true')) {
145 global $xtpl;
146
147 $realm = $request['openid.realm'];
148 $icon_url = simpleid_url('ui/icon', 'realm=' . rawurlencode($realm) . '&tk=' . _ui_icon_token($realm));
149
150 $xtpl->assign('icon_url', htmlspecialchars($icon_url, ENT_QUOTES, 'UTF-8'));
151 $xtpl->parse('main.openid_consent.icon');
152 }
153
154 return '';
155}
156
157/**
158 * Specifies that the OpenID response should be sent via the fragment
159 *
160 */
161function ui_indirect_response($url, $response) {
162 global $openid_ns_to_alias;
163 if (!array_key_exists(OPENID_NS_UI, $openid_ns_to_alias)) return NULL;
164
165 // Cheat - if we run this, then the redirect page will also be themed!
166 _ui_insert_css_js();
167
168 if (strstr($url, '#')) {
169 return OPENID_RESPONSE_FRAGMENT;
170 } else {
171 return NULL;
172 }
173}
174
175/**
176 * Adds an extra route to the SimpleWeb framework.
177 */
178function ui_routes() {
179 return array('ui/icon' => 'ui_icon');
180}
181
182/**
183 * Returns an icon.
184 */
185function ui_icon() {
186 if (!isset($_GET['realm']) || !isset($_GET['tk']) || ($_GET['tk'] != _ui_icon_token($_GET['realm']))) {
187 header_response_code('404 Not Found');
188 indirect_fatal_error(t('Invalid UI icon parameters.'));
189 }
190
191 $realm = $_GET['realm'];
192 $icon_res = _ui_get_icon($realm);
193
194 if ($icon_res === NULL) {
195 header_response_code('404 Not Found');
196 indirect_fatal_error(t('Unable to get icon.'));
197 }
198
199 header('Via: ' . $icon_res['protocol'] . ' simpleid-ui-icon-' . md5($realm));
200 header('Cache-Control: max-age=86400');
201 header('Content-Type: ' . $icon_res['headers']['content-type']);
202 if (isset($icon_res['headers']['content-encoding'])) header('Content-Encoding: ' . $icon_res['headers']['content-encoding']);
203 print $icon_res['data'];
204}
205
206/**
207 * Inserts the necessary CSS and JavaScript code to implement the popup mode
208 * from the User Interface extension.
209 */
210function _ui_insert_css_js() {
211 global $xtpl;
212
213 $css = (isset($xtpl->vars['css'])) ? $xtpl->vars['css'] : '';
214 $js = (isset($xtpl->vars['javascript'])) ? $xtpl->vars['javascript'] : '';
215
216 $xtpl->assign('css', $css . '@import url(' . get_base_path() . 'extensions/ui/ui.css);');
217 $xtpl->assign('javascript', $js . '<script src="' . get_base_path() . 'extensions/ui/ui.js" type="text/javascript"></script>');
218}
219
220/**
221 * Attempts to obtain an icon from a RP
222 *
223 * @param string $realm the openid.realm parameter
224 * @return array the response from {@link http_make_request()} with the discovered URL of the
225 * RP's icon
226 */
227function _ui_get_icon($realm) {
228 $rp_info = simpleid_get_rp_info($realm);
229
230 if (isset($rp_info['ui_icon'])) return $rp_info['ui_icon'];
231
232 $services = discovery_xrds_services_by_type($rp_info['services'], 'http://specs.openid.net/extensions/ui/icon');
233
234 if ($services) {
235 $icon_url = $services[0]['uri'];
236
237 $icon_res = http_make_request($icon_url);
238 if (isset($icon_res['http-error'])) {
239 return NULL;
240 }
241
242 $rp_info['ui_icon'] = $icon_res;
243 simpleid_set_rp_info($realm, $rp_info);
244 } else {
245 return NULL;
246 }
247}
248
249/**
250 * Returns a token to be used when requesting the icon.
251 *
252 * The token is used to prevent flooding SimpleID with external requests.
253 *
254 * @param string $realm the openid.realm parameter
255 * @return string the token
256 */
257function _ui_icon_token($realm) {
258 return get_form_token('q=ui/icon&realm=' . rawurlencode($realm));
259}
260?>