| 1 | <?php |
| 2 | /*************************************************************************** |
| 3 | * Copyright (C) 2003-2014 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 | require_once 'Auth/OpenID/Discover.php'; |
| 23 | |
| 24 | // An helper class for using plat/al as an OpenId Identity Provider. |
| 25 | class OpenId |
| 26 | { |
| 27 | private $base_url; // Base url for all OpenId operations. |
| 28 | private $spool_store; // Location of the spool storage for OpenID. |
| 29 | |
| 30 | private $server = null; // Auth::OpenId::Server object. |
| 31 | private $request = null; // Request extracted by the Server object. |
| 32 | |
| 33 | public function __construct() |
| 34 | { |
| 35 | global $globals; |
| 36 | |
| 37 | $this->base_url = $globals->baseurl . '/openid'; |
| 38 | $this->spool_store = $globals->spoolroot . '/spool/openid/store'; |
| 39 | } |
| 40 | |
| 41 | // Initializes an OpenId Server object; it will use a defined spool-based |
| 42 | // directory to store OpenID secrets. Returns true on success. |
| 43 | public function Initialize() |
| 44 | { |
| 45 | require_once 'Auth/OpenID/FileStore.php'; |
| 46 | require_once 'Auth/OpenID/Server.php'; |
| 47 | |
| 48 | $store = new Auth_OpenID_FileStore($this->spool_store); |
| 49 | $this->server = new Auth_OpenID_Server($store, $this->base_url); |
| 50 | $this->request = $this->server->decodeRequest(); |
| 51 | |
| 52 | return !is_a($this->request, 'Auth_OpenID_ServerError'); |
| 53 | } |
| 54 | |
| 55 | // Authorization logic helpers --------------------------------------------- |
| 56 | |
| 57 | // Returns true iff the current request is a valid openid request. |
| 58 | public function IsOpenIdRequest() |
| 59 | { |
| 60 | return Env::has('openid_mode'); |
| 61 | } |
| 62 | |
| 63 | // Returns true iff the request needs to be handled directly by the calling |
| 64 | // code (ie. the current user needs to be authorized). |
| 65 | public function IsAuthorizationRequest() |
| 66 | { |
| 67 | return $this->request->mode == 'checkid_immediate' || |
| 68 | $this->request->mode == 'checkid_setup'; |
| 69 | } |
| 70 | |
| 71 | // Returns true iff the request requires an immediate answer (no user |
| 72 | // interaction is allowed). |
| 73 | public function IsImmediateRequest() |
| 74 | { |
| 75 | return $this->request->mode == 'checkid_immediate'; |
| 76 | } |
| 77 | |
| 78 | // Returns true iff the logged-in user is authorized for the current request. |
| 79 | // It checks that the user is logged in, and has the authorization to use |
| 80 | // that identity. |
| 81 | public function IsUserAuthorized(User $user) |
| 82 | { |
| 83 | return $user && ($user->login() == $this->request->identity || |
| 84 | $this->request->idSelect()); |
| 85 | } |
| 86 | |
| 87 | // SimpleRegistration helpers ---------------------------------------------- |
| 88 | |
| 89 | // Determines which SREG data are requested by the endpoint, and returns them. |
| 90 | public function GetSRegDataForRequest(User $user) |
| 91 | { |
| 92 | require_once 'Auth/OpenID/SReg.php'; |
| 93 | |
| 94 | // Other common SReg fields we could fill are: |
| 95 | // dob, country, language, timezone. |
| 96 | $sreg_request = Auth_OpenID_SRegRequest::fromOpenIDRequest($this->request); |
| 97 | return Auth_OpenID_SRegResponse::extractResponse($sreg_request, array( |
| 98 | 'fullname' => $user->fullName(), |
| 99 | 'nickname' => $user->displayName(), |
| 100 | 'email' => $user->bestEmail(), |
| 101 | 'gender' => $user->isFemale() ? 'F' : 'M', |
| 102 | )); |
| 103 | } |
| 104 | |
| 105 | // Handling and answering helpers ------------------------------------------ |
| 106 | |
| 107 | // Answers the current request, and renders the response. Appends the |sreg| |
| 108 | // data when not null. |
| 109 | public function AnswerRequest($is_authorized, $sreg_data = null) |
| 110 | { |
| 111 | // Creates the response. |
| 112 | if ($is_authorized && $this->request->idSelect()) { |
| 113 | $user = S::user(); |
| 114 | $response = $this->request->answer( |
| 115 | $is_authorized, null, $user->login(), $this->GetUserUrl($user)); |
| 116 | } else { |
| 117 | $response = $this->request->answer($is_authorized); |
| 118 | } |
| 119 | |
| 120 | // Clobbers response, and get it back to the Relaying Party. |
| 121 | if ($sreg_data) { |
| 122 | $sreg_data->toMessage($response->fields); |
| 123 | } |
| 124 | $this->RenderResponse($response); |
| 125 | } |
| 126 | |
| 127 | // Automatically handles the request without any user interaction. |
| 128 | public function HandleRequest() |
| 129 | { |
| 130 | $response = $this->server->handleRequest($this->request); |
| 131 | $this->RenderResponse($response); |
| 132 | } |
| 133 | |
| 134 | // Trust management helpers ------------------------------------------------ |
| 135 | |
| 136 | // Returns true iff the current endpoint is currently trusted by |user|. |
| 137 | public function IsEndpointTrusted(User $user) |
| 138 | { |
| 139 | $res = XDB::query( |
| 140 | "SELECT COUNT(*) |
| 141 | FROM account_auth_openid |
| 142 | WHERE (uid = {?} OR uid IS NULL) AND url = {?}", |
| 143 | $user->id(), $this->request->trust_root); |
| 144 | return ($res->fetchOneCell() > 0); |
| 145 | } |
| 146 | |
| 147 | // Updates the trust level for the given endpoint, based on the value pf |
| 148 | // |trusted| and |permanent_trust| (the latter is ignored when the former |
| 149 | // value is false). Returns true iff the current endpoint is trusted. |
| 150 | public function UpdateEndpointTrust(User $user, $trusted, $permanent_trust) { |
| 151 | $initial_trust = $this->IsEndpointTrusted($user); |
| 152 | if (!$initial_trust && $trusted && $permanent_trust) { |
| 153 | XDB::execute( |
| 154 | "INSERT IGNORE INTO account_auth_openid |
| 155 | SET uid = {?}, url = {?}", |
| 156 | $user->id(), $this->request->trust_root); |
| 157 | } |
| 158 | |
| 159 | return ($initial_trust || $trusted); |
| 160 | } |
| 161 | |
| 162 | // Page renderers ---------------------------------------------------------- |
| 163 | |
| 164 | // Renders the OpenId discovery page for |user|. |
| 165 | public function RenderDiscoveryPage($page, User $user) |
| 166 | { |
| 167 | $page->changeTpl('openid/openid.tpl'); |
| 168 | $page->setTitle($user->fullName()); |
| 169 | $page->addLink('openid.server openid2.provider', $this->base_url); |
| 170 | $page->addLink('openid.delegate openid2.local_id', $user->login()); |
| 171 | $page->assign_by_ref('user', $user); |
| 172 | |
| 173 | // Include the X-XRDS-Location header for Yadis discovery. |
| 174 | header('X-XRDS-Location: ' . $this->GetUserXrdsUrl($user)); |
| 175 | } |
| 176 | |
| 177 | // Renders the main XRDS page. |
| 178 | public function RenderMainXrdsPage($page) |
| 179 | { |
| 180 | pl_content_headers("application/xrds+xml"); |
| 181 | $page->changeTpl('openid/idp_xrds.tpl', NO_SKIN); |
| 182 | $page->assign('type2', Auth_OpenID_TYPE_2_0_IDP); |
| 183 | $page->assign('sreg', Auth_OpenID_SREG_URI); |
| 184 | $page->assign('provider', $this->base_url); |
| 185 | } |
| 186 | |
| 187 | // Renders the XRDS page of |user|. |
| 188 | public function RenderUserXrdsPage($page, User $user) |
| 189 | { |
| 190 | pl_content_headers("application/xrds+xml"); |
| 191 | $page->changeTpl('openid/user_xrds.tpl', NO_SKIN); |
| 192 | $page->assign('type2', Auth_OpenID_TYPE_2_0); |
| 193 | $page->assign('type1', Auth_OpenID_TYPE_1_1); |
| 194 | $page->assign('sreg', Auth_OpenID_SREG_URI); |
| 195 | $page->assign('provider', $this->base_url); |
| 196 | $page->assign('local_id', $user->login()); |
| 197 | } |
| 198 | |
| 199 | // Renders the OpenId response for the HTTP client. |
| 200 | public function RenderResponse($response) |
| 201 | { |
| 202 | if ($response) { |
| 203 | $web_response = $this->server->encodeResponse($response); |
| 204 | header(sprintf('%s %d', $_SERVER['SERVER_PROTOCOL'], $web_response->code), |
| 205 | true, $web_response->code); |
| 206 | |
| 207 | if (is_a($response, 'Auth_OpenID_ServerError')) { |
| 208 | print "Erreur lors de l'authentification OpenId: " . $response->toString(); |
| 209 | } else { |
| 210 | foreach ($web_response->headers as $key => $value) { |
| 211 | header(sprintf('%s: %s', $key, $value)); |
| 212 | } |
| 213 | |
| 214 | header('Connection: close'); |
| 215 | print $web_response->body; |
| 216 | } |
| 217 | } |
| 218 | exit; |
| 219 | } |
| 220 | |
| 221 | // URL providers ----------------------------------------------------------- |
| 222 | |
| 223 | // Returns the OpenId identity URL of the requested user. |
| 224 | private function GetUserUrl(User $user) |
| 225 | { |
| 226 | return $this->base_url . '/' . $user->login(); |
| 227 | } |
| 228 | |
| 229 | // Returns the private XRDS page of a user. |
| 230 | private function GetUserXrdsUrl(User $user) |
| 231 | { |
| 232 | return $this->base_url . '/xrds/' . $user->login(); |
| 233 | } |
| 234 | |
| 235 | // Returns the endpoint in the current request. |
| 236 | public function GetEndpoint() |
| 237 | { |
| 238 | return $this->request->trust_root; |
| 239 | } |
| 240 | |
| 241 | // Extracts the OpenId arguments available in the current request, and |
| 242 | // builds a query string with them. |
| 243 | public function GetQueryStringForRequest() |
| 244 | { |
| 245 | foreach (Auth_OpenID::getQuery() as $key => $value) { |
| 246 | if (strpos($key, 'openid.') === 0) { |
| 247 | $args[$key] = $value; |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | return http_build_query($args); |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | // vim:set et sw=4 sts=4 sws=4 foldmethod=marker fenc=utf-8: |
| 256 | ?> |