Commit | Line | Data |
---|---|---|
24cfa984 AA |
1 | <?php |
2 | /*************************************************************************** | |
8d84c630 | 3 | * Copyright (C) 2003-2009 Polytechnique.org * |
24cfa984 AA |
4 | * http://opensource.polytechnique.org/ * |
5 | * * | |
6 | * This program is free software; you can redistribute it and/or modify * | |
7 | * it under the terms of the GNU General Public License as published by * | |
8 | * the Free Software Foundation; either version 2 of the License, or * | |
9 | * (at your option) any later version. * | |
10 | * * | |
11 | * This program is distributed in the hope that it will be useful, * | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
14 | * GNU General Public License for more details. * | |
15 | * * | |
16 | * You should have received a copy of the GNU General Public License * | |
17 | * along with this program; if not, write to the Free Software * | |
18 | * Foundation, Inc., * | |
19 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * | |
20 | ***************************************************************************/ | |
21 | ||
a1af4a99 AA |
22 | |
23 | /* Definitions for the OpenId Specification | |
24 | * http://openid.net/specs/openid-authentication-2_0.html | |
7eaf07e9 | 25 | * |
a1af4a99 AA |
26 | * OP Endpoint URL: https://www.polytechnique.org/openid |
27 | * OP Identifier: https://www.polytechnique.org/openid | |
b21f0bb5 AA |
28 | * User Identifier: https://www.polytechnique.org/openid/{hruid} |
29 | * OP-Local Identifier: {hruid} | |
30 | */ | |
31 | ||
32 | /* This implementation supports two modes: | |
33 | * - entering the OP Identifier, which can simply be 'polytechnique.org' | |
34 | * - entering the User Identifier, or some URL that resolves there | |
35 | * In both cases, Yadis discovery is made possible through the X-XRDS-Location | |
36 | * header. | |
7eaf07e9 | 37 | * |
b21f0bb5 AA |
38 | * In the former case, Yadis discovery is performed on /, or where it redirects; |
39 | * see platal.php. It resolves to the XRDS for this OP, and User Identifier | |
40 | * selection is performed after forcing the user to log in. This only works for | |
41 | * version 2.0 of the OpenId protocol. | |
42 | * | |
43 | * In the latter cas, Yadis discovery is performed on /openid/{hruid}. It | |
44 | * resolves ta a user-specific XRDS. This page also features HTML-based | |
45 | * discovery. This works with any version of the protocol. | |
a1af4a99 AA |
46 | */ |
47 | ||
33536353 AA |
48 | /* Testing suite is here: |
49 | * http://openidenabled.com/resources/openid-test/ | |
b21f0bb5 AA |
50 | * It only supports User Indentifiers. |
51 | * | |
52 | * To test OP Identifiers, download the JanRain PHP library and use the | |
53 | * consumer provided as an example (although it appears that a failure is | |
54 | * mistakenly reported: 'Server denied check_authentication'). | |
55 | * Reading the source of the server can also help understanding the code below. | |
33536353 AA |
56 | */ |
57 | ||
33536353 | 58 | |
24cfa984 AA |
59 | class OpenidModule extends PLModule |
60 | { | |
61 | function handlers() | |
62 | { | |
63 | return array( | |
a1af4a99 AA |
64 | 'openid' => $this->make_hook('openid', AUTH_PUBLIC), |
65 | 'openid/trust' => $this->make_hook('trust', AUTH_COOKIE), | |
b8f1396f | 66 | 'openid/trusted' => $this->make_hook('trusted', AUTH_MDP), |
51e5bc7d | 67 | 'admin/openid/trusted' => $this->make_hook('admin_trusted', AUTH_MDP), |
ab66bf7f | 68 | 'openid/idp_xrds' => $this->make_hook('idp_xrds', AUTH_PUBLIC), |
b69727b4 | 69 | 'openid/user_xrds' => $this->make_hook('user_xrds', AUTH_PUBLIC), |
829fae6a | 70 | 'openid/melix' => $this->make_hook('melix', AUTH_PUBLIC), |
24cfa984 AA |
71 | ); |
72 | } | |
73 | ||
74 | function handler_openid(&$page, $x = null) | |
75 | { | |
a1af4a99 | 76 | $this->load('openid.inc.php'); |
b69727b4 | 77 | |
a8e858d3 | 78 | // Spec ยง4.1.2: if "openid.mode" is absent, we SHOULD assume that |
33536353 | 79 | // the request is not an OpenId message |
33536353 | 80 | if (!array_key_exists('openid_mode', $_REQUEST)) { |
b21f0bb5 | 81 | return $this->render_discovery_page($page, get_user($x)); |
24cfa984 AA |
82 | } |
83 | ||
7eaf07e9 | 84 | // Now, deal with the OpenId message |
a1af4a99 AA |
85 | $server = init_openid_server(); |
86 | $request = $server->decodeRequest(); | |
87 | ||
1bda7469 AA |
88 | // In modes 'checkid_immediate' and 'checkid_setup', the request |
89 | // needs some logic and can not be automatically answered by the server | |
90 | ||
91 | // Immediate mode | |
92 | if ($request->mode == 'checkid_immediate') { | |
93 | ||
94 | // We deny immediate requests, unless: | |
95 | // - the user identifier is known by the RP | |
96 | // - the user is logged in | |
97 | // - the user identifier matches the user logged in | |
a8e858d3 | 98 | // - the user has whitelisted the site |
1bda7469 AA |
99 | $answer = !$request->idSelect() |
100 | && S::logged() | |
101 | && $request->identity == S::user()->login() | |
102 | && is_trusted_site(S::user(), $request->trust_root); | |
103 | $response =& $request->answer($answer); | |
104 | ||
105 | // Setup mode | |
106 | } else if ($request->mode == 'checkid_setup') { | |
107 | ||
108 | // We redirect to a page where the user will authenticate | |
109 | // and confirm the use of his/her OpenId | |
2cf1f8a3 AA |
110 | $request_id = uniqid('openid-'); |
111 | S::set($request_id, serialize($request)); | |
112 | $query = 'request_id=' . urlencode($request_id); | |
9f97216c | 113 | pl_redirect('openid/trust', $query); |
1bda7469 | 114 | return; |
a1af4a99 | 115 | |
33536353 AA |
116 | // Other requests can be automatically handled by the server |
117 | } else { | |
a1af4a99 | 118 | $response =& $server->handleRequest($request); |
24cfa984 AA |
119 | } |
120 | ||
a1af4a99 | 121 | $webresponse =& $server->encodeResponse($response); |
33536353 | 122 | $this->render_openid_response($webresponse); |
a1af4a99 | 123 | } |
b69727b4 | 124 | |
a1af4a99 AA |
125 | function handler_trust(&$page, $x = null) |
126 | { | |
127 | $this->load('openid.inc.php'); | |
24cfa984 | 128 | |
a1af4a99 | 129 | // Recover request in session |
2cf1f8a3 AA |
130 | $request_id = $_GET['request_id']; |
131 | if (is_null($request_id) || !isset($_SESSION[$request_id])) { | |
a1af4a99 AA |
132 | // There is no authentication information, something went wrong |
133 | pl_redirect('/'); | |
134 | return; | |
a1af4a99 | 135 | } |
24cfa984 | 136 | |
b21f0bb5 | 137 | require_once 'Auth/OpenID/Server.php'; |
2cf1f8a3 | 138 | $request = unserialize($_SESSION[$request_id]); |
b21f0bb5 | 139 | |
a1af4a99 AA |
140 | $server = init_openid_server(); |
141 | $user = S::user(); | |
b21f0bb5 AA |
142 | $identity = null; |
143 | $claimed_id = null; | |
144 | ||
145 | // Set the identity to the user currently logged in | |
146 | // if an OP Identifier was initially used | |
147 | if ($request->identity == Auth_OpenID_IDENTIFIER_SELECT) { | |
148 | $identity = $user->hruid; | |
149 | $claimed_id = get_user_openid_url($user); | |
a1af4a99 | 150 | // Check that the identity matches the user currently logged in |
b21f0bb5 AA |
151 | // if an User Identifier was initially used |
152 | } else if ($request->identity != $user->hruid) { | |
a1af4a99 AA |
153 | $response =& $request->answer(false); |
154 | $webresponse =& $server->encodeResponse($response); | |
155 | $this->render_openid_response($webresponse); | |
156 | return; | |
157 | } | |
158 | ||
12d4424c AA |
159 | // Prepare Simple Registration response fields |
160 | require_once 'Auth/OpenID/SReg.php'; | |
161 | $sreg_request = Auth_OpenID_SRegRequest::fromOpenIDRequest($request); | |
162 | $sreg_response = Auth_OpenID_SRegResponse::extractResponse($sreg_request, get_sreg_data($user)); | |
163 | ||
f8a567b5 | 164 | $whitelisted = is_trusted_site($user, $request->trust_root); |
12d4424c | 165 | |
33536353 | 166 | // Ask the user for confirmation |
9f97216c AA |
167 | $from_trust_page = $_SERVER['REQUEST_METHOD'] == 'POST' |
168 | && (isset($_POST['openid_trust']) || isset($_POST['openid_cancel'])); | |
169 | if (!$whitelisted && !$from_trust_page) { | |
a1af4a99 AA |
170 | $page->changeTpl('openid/trust.tpl'); |
171 | $page->assign('relying_party', $request->trust_root); | |
12d4424c | 172 | $page->assign_by_ref('sreg_data', $sreg_response->data); |
2cf1f8a3 | 173 | $query = 'request_id=' . urlencode($request_id); |
9f97216c | 174 | $page->assign('query', $query); |
a1af4a99 AA |
175 | return; |
176 | } | |
177 | ||
9f97216c | 178 | // If this point is reached, the user has just validated the form on the 'trust' page |
2e5fbf5e | 179 | |
2cf1f8a3 AA |
180 | // Remove the request from session since an answer will be sent |
181 | S::kill($request_id); | |
182 | ||
2e5fbf5e | 183 | // Add 'always trusted' sites to whitelist |
9f97216c | 184 | if (isset($_POST['openid_trust']) && @$_POST['openid_always']) { |
2e5fbf5e AA |
185 | add_trusted_site($user, $request->trust_root); |
186 | } | |
187 | ||
b21f0bb5 | 188 | // Answer to the Relying Party |
9f97216c | 189 | if ($whitelisted || isset($_POST['openid_trust'])) { |
b21f0bb5 | 190 | $response =& $request->answer(true, null, $identity, $claimed_id); |
a1af4a99 | 191 | |
a1af4a99 AA |
192 | // Add the simple registration response values to the OpenID |
193 | // response message. | |
a1af4a99 AA |
194 | $sreg_response->toMessage($response->fields); |
195 | ||
9f97216c | 196 | } else { // !$whitelisted && isset($_POST['openid_cancel']) |
33536353 | 197 | $response =& $request->answer(false); |
a1af4a99 | 198 | } |
33536353 | 199 | |
33536353 AA |
200 | $webresponse =& $server->encodeResponse($response); |
201 | $this->render_openid_response($webresponse); | |
b69727b4 AA |
202 | } |
203 | ||
b8f1396f AA |
204 | function handler_trusted(&$page, $action = 'list', $id = null) |
205 | { | |
b8f1396f AA |
206 | $page->setTitle('Sites tiers de confiance'); |
207 | $page->assign('title', 'Mes sites tiers de confiance pour OpenId'); | |
208 | $table_editor = new PLTableEditor('openid/trusted', 'openid_trusted', 'id'); | |
209 | $table_editor->set_where_clause('user_id = ' . XDB::escape(S::user()->id())); | |
b8f1396f AA |
210 | $table_editor->vars['user_id']['display'] = false; |
211 | $table_editor->describe('url', 'site tiers', true); | |
212 | $page->assign('deleteonly', true); | |
51e5bc7d AA |
213 | $table_editor->apply($page, $action, $id); |
214 | } | |
215 | ||
216 | function handler_admin_trusted(&$page, $action = 'list', $id = null) | |
217 | { | |
218 | $page->setTitle('Sites tiers de confiance'); | |
219 | $page->assign('title', 'Sites tiers de confiance globaux pour OpenId'); | |
220 | $table_editor = new PLTableEditor('admin/openid/trusted', 'openid_trusted', 'id'); | |
221 | $table_editor->set_where_clause('user_id IS NULL'); | |
222 | $table_editor->vars['user_id']['display'] = false; | |
223 | $table_editor->describe('url', 'site tiers', true); | |
224 | $page->assign('readonly', true); | |
b8f1396f AA |
225 | $table_editor->apply($page, $action, $id); |
226 | } | |
227 | ||
ab66bf7f AA |
228 | function handler_idp_xrds(&$page) |
229 | { | |
ab66bf7f | 230 | $this->load('openid.inc.php'); |
ab66bf7f AA |
231 | header('Content-type: application/xrds+xml'); |
232 | $page->changeTpl('openid/idp_xrds.tpl', NO_SKIN); | |
ab66bf7f AA |
233 | $page->assign('type2', Auth_OpenID_TYPE_2_0_IDP); |
234 | $page->assign('sreg', Auth_OpenID_SREG_URI); | |
235 | $page->assign('provider', get_openid_url()); | |
236 | } | |
237 | ||
b69727b4 AA |
238 | function handler_user_xrds(&$page, $x = null) |
239 | { | |
a1af4a99 | 240 | $this->load('openid.inc.php'); |
24cfa984 | 241 | |
b17873a4 AA |
242 | $user = get_user($x); |
243 | if (is_null($user)) { | |
244 | return PL_NOT_FOUND; | |
245 | } | |
246 | ||
b69727b4 AA |
247 | header('Content-type: application/xrds+xml'); |
248 | $page->changeTpl('openid/user_xrds.tpl', NO_SKIN); | |
b17873a4 AA |
249 | $page->assign('type2', Auth_OpenID_TYPE_2_0); |
250 | $page->assign('type1', Auth_OpenID_TYPE_1_1); | |
ab2a43ac | 251 | $page->assign('sreg', Auth_OpenID_SREG_URI); |
b17873a4 AA |
252 | $page->assign('provider', get_openid_url()); |
253 | $page->assign('local_id', $user->hruid); | |
24cfa984 AA |
254 | } |
255 | ||
2d8779e2 AA |
256 | function handler_melix(&$page, $x = null) |
257 | { | |
258 | $this->load('openid.inc.php'); | |
259 | $user = get_user_by_alias($x); | |
260 | ||
261 | // This will redirect to the canonic URL, which was not used | |
262 | // if this hook was triggered | |
829fae6a | 263 | return $this->render_discovery_page(&$page, $user); |
2d8779e2 AA |
264 | } |
265 | ||
a1af4a99 AA |
266 | //--------------------------------------------------------------------// |
267 | ||
268 | function render_discovery_page(&$page, $user) | |
269 | { | |
270 | ||
33536353 | 271 | // Show the documentation if this is not the OpenId page of an user |
a1af4a99 | 272 | if (is_null($user)) { |
33536353 | 273 | pl_redirect('Xorg/OpenId'); |
a1af4a99 AA |
274 | } |
275 | ||
2d8779e2 AA |
276 | // Redirect to the canonic URL if we are using an alias |
277 | // There might be a risk of redirection loop here | |
278 | // if $_SERVER was not exactly what we expect | |
279 | $current_url = 'http' . (empty($_SERVER['HTTPS']) ? '' : 's') . '://' | |
280 | . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']; | |
281 | $canonic_url = get_user_openid_url($user); | |
282 | if ($current_url != $canonic_url) { | |
283 | http_redirect($canonic_url); | |
284 | } | |
285 | ||
a1af4a99 AA |
286 | // Include X-XRDS-Location response-header for Yadis discovery |
287 | header('X-XRDS-Location: ' . get_user_xrds_url($user)); | |
288 | ||
a1af4a99 | 289 | $page->changeTpl('openid/openid.tpl'); |
a1af4a99 | 290 | $page->setTitle($user->fullName()); |
a8e858d3 | 291 | // Set the <link> tags for HTML-Based Discovery |
a1af4a99 AA |
292 | $page->addLink('openid.server openid2.provider', get_openid_url()); |
293 | $page->addLink('openid.delegate openid2.local_id', $user->hruid); | |
a1af4a99 AA |
294 | $page->assign_by_ref('user', $user); |
295 | ||
296 | return; | |
297 | } | |
298 | ||
33536353 | 299 | function render_openid_response($webresponse) |
a1af4a99 AA |
300 | { |
301 | if ($webresponse->code != AUTH_OPENID_HTTP_OK) { | |
302 | header(sprintf("HTTP/1.1 %d ", $webresponse->code), | |
303 | true, $webresponse->code); | |
304 | } | |
a1af4a99 AA |
305 | foreach ($webresponse->headers as $k => $v) { |
306 | header("$k: $v"); | |
307 | } | |
33536353 | 308 | header('Connection: close'); |
a1af4a99 AA |
309 | print $webresponse->body; |
310 | exit; | |
311 | } | |
24cfa984 AA |
312 | } |
313 | ||
314 | // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: | |
2d8779e2 | 315 | ?> |