2 /** External authentication via WebID for FusionForge
3 * Copyright 2011, Roland Mas
4 * Copyright 2011, Olivier Berger & Institut Telecom
6 * This program was developped in the frame of the COCLICO project
7 * (http://www.coclico-project.org/) with financial support of the Paris
10 * This file is part of FusionForge. FusionForge is free software;
11 * you can redistribute it and/or modify it under the terms of the
12 * GNU General Public License as published by the Free Software
13 * Foundation; either version 2 of the Licence, or (at your option)
16 * FusionForge is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License along
22 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 require_once $GLOBALS['gfcommon'].'include/User.class.php';
29 require_once 'WebIDDelegatedAuth/lib/Authentication.php';
32 * WebID Authentication manager Plugin for FusionForge
35 class AuthWebIDPlugin extends ForgeAuthPlugin {
37 var $delegatedAuthentifier;
39 var $delegate_webid_auth_to;
41 var $idp_delegation_link;
45 function AuthWebIDPlugin () {
47 $this->ForgeAuthPlugin() ;
48 $this->name = "authwebid";
49 $this->text = "WebID authentication";
51 $this->_addHook('display_auth_form');
52 $this->_addHook("check_auth_session");
53 $this->_addHook("fetch_authenticated_user");
54 $this->_addHook("close_auth_session");
55 $this->_addHook("usermenu") ;
56 $this->_addHook("userisactivecheckbox") ; // The "use ..." checkbox in user account
57 $this->_addHook("userisactivecheckboxpost") ; //
59 $this->saved_login = '';
60 $this->saved_user = NULL;
62 $this->delegatedAuthentifier = FALSE;
64 $this->webid_identity = FALSE;
66 $this->declareConfigVars();
68 // The IdP to use is configured in the .ini file
69 $this->delegate_webid_auth_to = forge_get_config ('delegate_webid_auth_to', $this->name);
70 $this->idp_delegation_link = forge_get_config('idp_delegation_link', $this->name);
75 * Display a link redirecting to a WebID IdP, to test a delegated auth
76 * @param string $callback : callback which the IdP will invoke through with signed parameters
77 * @param string $message : alternative message for the link
80 function displayAuthentifyViaIdPLink($callback, $message = FALSE) {
82 $message = sprintf( _('Click here to delegate authentication of your WebID to %s'), $this->delegate_webid_auth_to);
84 $html = '<a href="' . $this->idp_delegation_link . '?authreqissuer='. $callback .'">';
85 $html .= $message .'</a>';
90 * Display a form to redirect to the WebID IdP
91 * @param unknown_type $params
94 function displayAuthForm(&$params) {
95 if (!$this->isRequired() && !$this->isSufficient()) {
98 $return_to = $params['return_to'];
103 $result .= _('Cookies must be enabled past this point.');
106 // TODO Use a trusted IdP that was configured previously by the forge admin, and which is trusted by the libAuthentication checks
107 //$result .= '<a href="https://foafssl.org/srv/idp?authreqissuer='. util_make_url('/plugins/authwebid/post-login.php') .'">Click here to Login via foafssl.org</a>';
109 $result .= '<b>'. $this->displayAuthentifyViaIdPLink( util_make_url('/plugins/authwebid/post-login.php') ) . '</b>';
110 $result .= ' ('. _('You need to have bound such a WebID to your existing fusionforge account in advance') .')';
112 $params['html_snippets'][$this->name] = $result;
117 * Is there a valid session?
118 * @param unknown_type $params
120 function checkAuthSession(&$params) {
121 $this->saved_user = NULL;
124 if (isset($params['auth_token']) && $params['auth_token'] != '') {
125 $user_id = $this->checkSessionToken($params['auth_token']);
127 $user_id = $this->checkSessionCookie();
130 $user = user_get_object($user_id);
132 if ($this->delegatedAuthentifier && $this->delegatedAuthentifier->identity) {
133 $username = $this->getUserNameFromWebIDIdentity($this->delegatedAuthentifier->identity);
135 $user = $this->startSession($username);
141 if ($this->isSufficient()) {
142 $this->saved_user = $user;
143 $params['results'][$this->name] = FORGE_AUTH_AUTHORITATIVE_ACCEPT;
146 $params['results'][$this->name] = FORGE_AUTH_NOT_AUTHORITATIVE;
149 if ($this->isRequired()) {
150 $params['results'][$this->name] = FORGE_AUTH_AUTHORITATIVE_REJECT;
152 $params['results'][$this->name] = FORGE_AUTH_NOT_AUTHORITATIVE;
158 * Retrieve the user_name for a WebID URI stored in DB as a known ID
159 * @param string $webid_identity
162 public function getUserNameFromWebIDIdentity($webid_identity) {
164 $res = db_query_params('SELECT users.user_name FROM users, plugin_authwebid_user_identities WHERE users.user_id = plugin_authwebid_user_identities.user_id AND webid_identity=$1',
165 array($webid_identity));
167 $row = db_fetch_array_by_row($res, 0);
169 $user_name = $row['user_name'];
176 * Check if a WebID is already used and bound to an account
177 * @param string $webid_identity
180 public function existStoredWebID($webid_identity) {
181 $res = db_query_params('SELECT webid_identity FROM plugin_authwebid_user_identities WHERE webid_identity =$1',
182 array($webid_identity));
183 if ($res && db_numrows($res) > 0) {
192 * Load WebIDs already bound to an account (not the pending ones)
193 * @param string $user_id
196 public function getStoredBoundWebIDs($user_id) {
197 $boundwebids = array();
198 $res = db_query_params('SELECT webid_identity FROM plugin_authwebid_user_identities WHERE user_id =$1',
203 while ($row = db_fetch_array($res)) {
204 $webid_identity = $row['webid_identity'];
205 // filter out the pending ones, prefixes by 'pending:'
206 if (substr($webid_identity, 0, 8) != 'pending:') {
207 $boundwebids[] = $webid_identity;
215 * Check if a WebID is pending confirmation of binding for a user
216 * @param string $user_id
217 * @param string $webid_identity
220 public function isStoredPendingWebID($user_id, $webid_identity) {
221 // the pending WebIDs will be prefixed in the DB by 'pending:'
222 $webid_identity = 'pending:' . $webid_identity;
223 $res = db_query_params('SELECT COUNT(*) FROM plugin_authwebid_user_identities WHERE user_id =$1 AND webid_identity =$2',
224 array ($user_id, $webid_identity));
225 if ($res && db_numrows($res) > 0) {
226 $arr = db_fetch_array($res);
227 if ($arr[0] == '1') {
239 * Load WebIDs already stored, but pending confirmation by a user
240 * @param string $user_id
243 public function getStoredPendingWebIDs($user_id) {
244 $pendingwebids = array();
245 $res = db_query_params('SELECT webid_identity FROM plugin_authwebid_user_identities WHERE user_id =$1',
250 while ($row = db_fetch_array($res)) {
251 $webid_identity = $row['webid_identity'];
252 // return them as plain WebIDs without the 'pending:' prefix
253 if (substr($webid_identity, 0, 8) == 'pending:') {
254 $pendingwebids[] = substr($webid_identity, 8);
258 return $pendingwebids;
262 * Convert a WebID pending binding to a bound one
263 * @param string $user_id
264 * @param string $webid_identity
267 public function bindStoredWebID($user_id, $webid_identity) {
269 // remove the 'pending:' prefix
270 $res = db_query_params('UPDATE plugin_authwebid_user_identities SET webid_identity=$1 WHERE user_id =$2 AND webid_identity =$3',
271 array ($webid_identity, $user_id, 'pending:'.$webid_identity)) ;
273 $error_msg = sprintf(_('Cannot bind new identity: %s'), db_error());
279 * Store a WebID as pending binding to an account
280 * @param string $user_id
281 * @param string $webid_identity
284 public function addStoredPendingWebID($user_id, $webid_identity) {
286 // make sure not to add as pending to one account an already bound WebID for another
287 if ($this->existStoredWebID($webid_identity)) {
288 $error_msg = _('WebID already used');
291 // prefix it with the 'pending:' prefix
292 $webid_identity = 'pending:' . $webid_identity;
293 // make sure to not add the same pending WebID for two different accounts
294 if ($this->existStoredWebID($webid_identity)) {
295 $error_msg = _('WebID already pending binding');
297 $res = db_query_params('INSERT INTO plugin_authwebid_user_identities (user_id, webid_identity) VALUES ($1,$2)',
298 array ($user_id, $webid_identity)) ;
299 if (!$res || db_affected_rows($res) < 1) {
300 $error_msg = sprintf(_('Cannot insert new identity: %s'), db_error());
307 * Remove a WebID (possibly pending) from the table
308 * @param string $user_id
309 * @param string $webid_identity
312 public function removeStoredWebID($user_id, $webid_identity) {
314 $res = db_query_params('DELETE FROM plugin_authwebid_user_identities WHERE user_id=$1 AND webid_identity=$2',
315 array($user_id, $webid_identity));
316 if (!$res || db_affected_rows($res) < 1) {
317 $error_msg = sprintf(_('Cannot delete identity: %s'), db_error());
323 * Check if we just got invoked back as a callback by the IdP which validated a WebID
326 public function justBeenAuthenticatedByIdP() {
328 // We should trust lib WebIDDelegatedAuth unless the admin wants to play by customizing by doing something like the commented code below
330 // initialize the WebID lib handler which will read the posted args
331 $IDPCertificates = array ( 'foafssl.org' =>
332 "-----BEGIN PUBLIC KEY-----
333 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhFboiwS5HzsQAAerGOj8
334 Zk6qvEf2QVarlm+c1fxd6f3OoQ9ezib1LjXitw+z2xcLG8lzaTmKOU0jw7KZp6WL
335 W6gqhAWj2BQ1Lkl9R7aAUpA3ypk52gik8u/5JiWpTt1EV99DP5XNzzQ/QVjkvBlj
336 rY+1ZeM+XtKzGfbK7eWh583xn3AE6maprXfLAo3BjUWJOQe0VHGYgrBVOcRQrSQ6
337 34/f+jk22tmYZRzdTT/ZCadeLd7NryIeJbEu0W105JYvKodawSM3/zjt4fXFIPyB
338 z8vHHmHRd2syDWqUy46YVQfqCfUBdXkHbvVQBtAfvRGUhYbFQm926an6z9uRE5LC
340 -----END PUBLIC KEY-----
343 //$certRepository = new Authentication_X509CertRepo($IDPCertificates);
346 // We don't rely on the PHP session, as we're in FusionForge
347 $create_session = FALSE;
348 //$this->delegatedAuthentifier = new Authentication_Delegated($create_session, NULL, NULL, $certRepository);
349 $this->delegatedAuthentifier = new Authentication_Delegated($create_session);
351 return $this->delegatedAuthentifier->isAuthenticated();
355 * Return current WebID if the delegated Auth has proceeded
358 public function getCurrentWebID() {
360 if ($this->delegatedAuthentifier) {
361 $webid = $this->delegatedAuthentifier->webid;
366 protected function declareConfigVars() {
367 parent::declareConfigVars();
370 forge_define_config_item ('required', $this->name, 'no');
371 forge_set_config_item_bool ('required', $this->name) ;
374 forge_define_config_item ('sufficient', $this->name, 'no');
375 forge_set_config_item_bool ('sufficient', $this->name) ;
377 // Default delegated WebID IdP to use
378 forge_define_config_item ('delegate_webid_auth_to', $this->name, 'auth.my-profile.eu');
380 //URL of the delegated auth on the IdP which accepts a ?authreqissuer=callback invocation
381 // for ex, for : https://auth.my-profile.eu/auth/?authreqissuer=http://fusionforge.example.com/callback.php :
382 forge_define_config_item ('idp_delegation_link', $this->name, 'https://auth.my-profile.eu/auth/');
387 * Displays link to WebID identities management tab in user's page ('usermenu' hook)
388 * @param unknown_type $params
390 public function usermenu($params) {
391 global $G_SESSION, $HTML;
392 $text = $this->text; // this is what shows in the tab
393 if ($G_SESSION->usesPlugin($this->name)) {
394 //$param = '?type=user&id=' . $G_SESSION->getId() . "&pluginname=" . $this->name; // we indicate the part we�re calling is the user one
395 echo $HTML->PrintSubMenu (array ($text), array ('/plugins/authwebid/index.php'), array(_('coin pan')));
402 // c-file-style: "bsd"