| 1 | <?php |
| 2 | /*************************************************************************** |
| 3 | * Copyright (C) 2003-2011 Polytechnique.org * |
| 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 | |
| 22 | |
| 23 | /* Definitions for the OpenId Specification |
| 24 | * http://openid.net/specs/openid-authentication-2_0.html |
| 25 | * |
| 26 | * OP Endpoint URL: https://www.polytechnique.org/openid |
| 27 | * OP Identifier: https://www.polytechnique.org/openid |
| 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. |
| 37 | * |
| 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. |
| 46 | */ |
| 47 | |
| 48 | /* Testing suite is here: |
| 49 | * http://openidenabled.com/resources/openid-test/ |
| 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. |
| 56 | */ |
| 57 | |
| 58 | |
| 59 | class OpenidModule extends PLModule |
| 60 | { |
| 61 | function handlers() |
| 62 | { |
| 63 | return array( |
| 64 | 'openid' => $this->make_hook('openid', AUTH_PUBLIC), |
| 65 | 'openid/melix' => $this->make_hook('melix', AUTH_PUBLIC), |
| 66 | 'openid/xrds' => $this->make_hook('xrds', AUTH_PUBLIC), |
| 67 | 'openid/trust' => $this->make_hook('trust', AUTH_MDP), |
| 68 | 'openid/trusted' => $this->make_hook('trusted', AUTH_MDP), |
| 69 | 'admin/openid/trusted' => $this->make_hook('admin_trusted', AUTH_MDP, 'admin'), |
| 70 | ); |
| 71 | } |
| 72 | |
| 73 | function handler_openid(&$page, $login = null) |
| 74 | { |
| 75 | $this->load('openid.inc.php'); |
| 76 | $requested_user = User::getSilent($login); |
| 77 | $server = new OpenId(); |
| 78 | |
| 79 | // Spec §4.1.2: if "openid.mode" is absent, we SHOULD assume that |
| 80 | // the request is not an OpenId message. |
| 81 | if (!$server->IsOpenIdRequest()) { |
| 82 | if ($requested_user) { |
| 83 | $server->RenderDiscoveryPage($page, $requested_user); |
| 84 | return; |
| 85 | } else { |
| 86 | pl_redirect('Xorg/OpenId'); |
| 87 | } |
| 88 | exit; |
| 89 | } |
| 90 | |
| 91 | // Initializes the OpenId environment from the request. |
| 92 | $server->Initialize(); |
| 93 | |
| 94 | // In modes 'checkid_immediate' and 'checkid_setup', we need to check |
| 95 | // by ourselves that we want to allow the user to be authenticated. |
| 96 | // Otherwise it can simply be forwarded to the Server object. |
| 97 | if ($server->IsAuthorizationRequest()) { |
| 98 | $authorized = S::logged() && |
| 99 | $server->IsUserAuthorized(S::user()) && |
| 100 | $server->IsEndpointTrusted(S::user()); |
| 101 | |
| 102 | if ($authorized) { |
| 103 | // TODO(vzanotti): SReg requests are currently not honored if |
| 104 | // the website is already trusted. We may want to redirect SReg |
| 105 | // requests to /openid/trust, to allow the user to choose. |
| 106 | $server->AnswerRequest(true); |
| 107 | } else if ($server->IsImmediateRequest()) { |
| 108 | $server->AnswerRequest(false); |
| 109 | } else { |
| 110 | // The user is currently not authorized to get her authorization |
| 111 | // request approved. Two possibilities: |
| 112 | // * the endpoint is not yet trusted => redirect to openid/trust |
| 113 | // * the user is not logged in => log in the user. |
| 114 | // |
| 115 | // The second case requires a special handling when the request |
| 116 | // was POSTed, as our current log in mechanism does not preserve |
| 117 | // POST arguments. |
| 118 | $openid_args = $server->GetQueryStringForRequest(); |
| 119 | if (S::logged()) { |
| 120 | pl_redirect('openid/trust', $openid_args); |
| 121 | } else if (Post::has('openid_mode')) { |
| 122 | pl_redirect('openid', $openid_args); |
| 123 | } else { |
| 124 | return PL_DO_AUTH; |
| 125 | } |
| 126 | } |
| 127 | } else { |
| 128 | $server->HandleRequest(); |
| 129 | } |
| 130 | |
| 131 | // All requests should have been answered at this point. The best here |
| 132 | // is to get the user back to a safe page. |
| 133 | pl_redirect(''); |
| 134 | } |
| 135 | |
| 136 | function handler_melix(&$page, $login = null) |
| 137 | { |
| 138 | $this->load('openid.inc.php'); |
| 139 | |
| 140 | global $globals; |
| 141 | $melix = ($login ? $login . '@' . $globals->mail->alias_dom : null); |
| 142 | |
| 143 | if ($melix && ($requested_user = User::getSilent($melix))) { |
| 144 | $server = new OpenId(); |
| 145 | $server->RenderDiscoveryPage($page, $requested_user); |
| 146 | } else { |
| 147 | pl_redirect('Xorg/OpenId'); |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | function handler_xrds(&$page, $login = null) |
| 152 | { |
| 153 | $this->load('openid.inc.php'); |
| 154 | $requested_user = User::getSilent($login); |
| 155 | $server = new OpenId(); |
| 156 | |
| 157 | if (!$login) { |
| 158 | $server->RenderMainXrdsPage($page); |
| 159 | } else if ($requested_user) { |
| 160 | $server->RenderUserXrdsPage($page, $requested_user); |
| 161 | } else { |
| 162 | return PL_NOT_FOUND; |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | function handler_trust(&$page) |
| 167 | { |
| 168 | $this->load('openid.inc.php'); |
| 169 | $server = new OpenId(); |
| 170 | $user = S::user(); |
| 171 | |
| 172 | // Initializes the OpenId environment from the request. |
| 173 | if (!$server->Initialize() || !$server->IsAuthorizationRequest()) { |
| 174 | $page->kill("Ta requête OpenID a échoué, merci de réessayer."); |
| 175 | } |
| 176 | |
| 177 | // Prepares the SREG data, if any is required. |
| 178 | $sreg_response = $server->GetSRegDataForRequest($user); |
| 179 | |
| 180 | // Asks the user about her trust level of the current request, if not |
| 181 | // done yet. |
| 182 | if (!Post::has('trust_accept') && !Post::has('trust_cancel')) { |
| 183 | $page->changeTpl('openid/trust.tpl'); |
| 184 | $page->assign('openid_query', $server->GetQueryStringForRequest()); |
| 185 | $page->assign('relying_party', $server->GetEndpoint()); |
| 186 | $page->assign('sreg_data', $sreg_response->contents()); |
| 187 | |
| 188 | return; |
| 189 | } |
| 190 | |
| 191 | // Interprets the form results, and updates the user whitelist. |
| 192 | S::assert_xsrf_token(); |
| 193 | $trusted = $server->UpdateEndpointTrust( |
| 194 | $user, |
| 195 | Post::b('trust_accept') && !Post::b('trust_cancel'), |
| 196 | Post::b('trust_always')); |
| 197 | |
| 198 | // Finally answers the request. |
| 199 | if ($server->IsUserAuthorized($user) && $trusted) { |
| 200 | $server->AnswerRequest(true, Post::b('trust_sreg') ? $sreg_response : null); |
| 201 | } else { |
| 202 | $server->AnswerRequest(false); |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | function handler_trusted(&$page, $action = 'list', $id = null) |
| 207 | { |
| 208 | $page->setTitle('Sites tiers de confiance'); |
| 209 | $page->assign('title', 'Mes sites tiers de confiance pour OpenId'); |
| 210 | $table_editor = new PLTableEditor('openid/trusted', 'account_auth_openid', 'id'); |
| 211 | $table_editor->set_where_clause(XDB::format('uid = {?}', S::user()->id())); |
| 212 | $table_editor->vars['uid']['display'] = false; |
| 213 | $table_editor->describe('url', 'site tiers', true); |
| 214 | $page->assign('deleteonly', true); |
| 215 | $table_editor->apply($page, $action, $id); |
| 216 | } |
| 217 | |
| 218 | function handler_admin_trusted(&$page, $action = 'list', $id = null) |
| 219 | { |
| 220 | $page->setTitle('Sites tiers de confiance'); |
| 221 | $page->assign('title', 'Sites tiers de confiance globaux pour OpenId'); |
| 222 | $table_editor = new PLTableEditor('admin/openid/trusted', 'account_auth_openid', 'id'); |
| 223 | $table_editor->set_where_clause('uid IS NULL'); |
| 224 | $table_editor->vars['uid']['display'] = false; |
| 225 | $table_editor->describe('url', 'site tiers', true); |
| 226 | $page->assign('readonly', true); |
| 227 | $table_editor->apply($page, $action, $id); |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: |
| 232 | ?> |