Bug 463293 - Babel should use OpenID
Provide a new login method using Eclipse OpenID. To keep the number of
changes low the users table is still used. When a user logged in through
OpenID we request some user details from account API to insert/update
the user in database. (but only parts of the existing user table)
Change-Id: Ifad61fe7fc3b3df3c1a89258b83da6735607fb7e
Signed-off-by: Paul Pazderski <paul-eclipse@ppazderski.de>
Signed-off-by: droy <denis.roy@eclipse-foundation.org>
diff --git a/addons/babel.eclipse.org/backend_functions.php b/addons/babel.eclipse.org/backend_functions.php
index 07defba..a6f21cf 100644
--- a/addons/babel.eclipse.org/backend_functions.php
+++ b/addons/babel.eclipse.org/backend_functions.php
@@ -123,6 +123,21 @@
'db_read_user' => $ini['db_read_user'],
'db_read_pass' => $ini['db_read_pass'],
'db_read_name' => $ini['db_read_name']);
+ }
+
+ /**
+ * Returns a hash of the oauth parameters.
+ */
+ function oauth_params() {
+ $ini = @parse_ini_file(dirname(__FILE__) . '/base.conf');
+ if (! $ini) {
+ die("Could not read the configuration file " . dirname(__FILE__) . "/base.conf");
+ }
+ return array(
+ 'client_id' => $ini['oauth_client_id'],
+ 'client_secret' => $ini['oauth_client_secret'],
+ 'client_callback' => $ini['oauth_client_callback']
+ );
}
/**
@@ -146,7 +161,8 @@
$addon->register('syncup_user', array('BabelEclipseOrg_backend', 'syncupUser'));
$addon->register('genie_user', array('BabelEclipseOrg_backend', 'genieUser'));
$addon->register('context', array('BabelEclipseOrg_backend', 'context'));
- $addon->register('db_params', array('BabelEclipseOrg_backend', 'db_params'));
+ $addon->register('db_params', array('BabelEclipseOrg_backend', 'db_params'));
+ $addon->register('oauth_params', array('BabelEclipseOrg_backend', 'oauth_params'));
$addon->register('error_log', array('BabelEclipseOrg_backend', 'error_log'));
$addon->register('babel_working', array('BabelEclipseOrg_backend', 'babel_working'));
}
diff --git a/addons/babel.eclipse.org/html/head.php b/addons/babel.eclipse.org/html/head.php
old mode 100755
new mode 100644
index db072b6..a2bfffe
--- a/addons/babel.eclipse.org/html/head.php
+++ b/addons/babel.eclipse.org/html/head.php
@@ -93,5 +93,5 @@
<script src='js/translationHint.js' type='text/javascript'></script>
<script language="javascript">
- document.getElementById("header-utils").innerHTML = "<ul><li><a href='login.php<?= $LoginAction ?>'><?= $LoginString ?></a></li></ul>";
+ document.getElementById("header-utils").innerHTML = "<ul><li><a href='login_oauth.php<?= $LoginAction ?>'><?= $LoginString ?></a></li></ul>";
</script>
\ No newline at end of file
diff --git a/addons/reference/backend_functions.php b/addons/reference/backend_functions.php
index 42b0353..9619e83 100644
--- a/addons/reference/backend_functions.php
+++ b/addons/reference/backend_functions.php
@@ -52,6 +52,17 @@
'db_read_user' => '',
'db_read_pass' => '',
'db_read_name' => '');
+ }
+
+ /**
+ * Returns a hash of the oauth parameters.
+ */
+ function oauth_params() {
+ return array(
+ 'client_id' => '',
+ 'client_secret' => '',
+ 'client_callback' => ''
+ );
}
/**
@@ -76,6 +87,7 @@
$addon->register('genie_user', array('Reference_backend', 'genieUser'));
$addon->register('context', array('Reference_backend', 'context'));
$addon->register('db_params', array('Reference_backend', 'db_parameters'));
+ $addon->register('oauth_params', array('Reference_backend', 'oauth_params'));
$addon->register('error_log', array('Reference_backend', 'error_log'));
$addon->register('babel_working', array('Reference_backend', 'babel_working'));
}
diff --git a/babel-setup.sql b/babel-setup.sql
index f402393..22c533c 100644
--- a/babel-setup.sql
+++ b/babel-setup.sql
@@ -405,7 +405,7 @@
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
- `userid` int(10) unsigned NOT NULL,
+ `userid` int(10) unsigned NOT NULL auto_increment,
`username` varchar(256) NOT NULL default '',
`first_name` varchar(256) NOT NULL default '',
`last_name` varchar(256) NOT NULL default '',
@@ -418,8 +418,10 @@
`updated_at` time NOT NULL,
`created_on` date NOT NULL,
`created_at` time NOT NULL,
+ `sub` varchar(128),
PRIMARY KEY (`userid`),
- KEY `primary_language_id` (`primary_language_id`)
+ KEY `primary_language_id` (`primary_language_id`),
+ UNIQUE KEY `sub_idx` (`sub`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `scoreboard`;
diff --git a/classes/system/user.class.php b/classes/system/user.class.php
index 5031bb0..16903f0 100644
--- a/classes/system/user.class.php
+++ b/classes/system/user.class.php
@@ -1,6 +1,6 @@
<?php
/*******************************************************************************
- * Copyright (c) 2007-2008 Eclipse Foundation and others.
+ * Copyright (c) 2007-2019 Eclipse Foundation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -10,6 +10,7 @@
* Paul Colton (Aptana)- initial API and implementation
* Eclipse Foundation
* Matthew Mazaika <mmazaik us.ibm.com> - bug 242011
+ * Paul Pazderski - bug 463293: load user info from Eclipse account api
*******************************************************************************/
require_once(dirname(__FILE__) . "/backend_functions.php");
@@ -47,6 +48,104 @@
$Event->add();
}
return $this->userid;
+ }
+
+ // Update user information in database by requesting account api with authorized oauth token. Return user id.
+ function updateUser($access_token) {
+ $this->userid = $this->doUpdateUser($access_token);
+ if ($this->userid > 0) {
+ $Event = new EventLog("users", "userid", $this->userid, "__auth_success");
+ $Event->add();
+ } else {
+ $Event = new EventLog("users", "userid", $_SERVER['REMOTE_ADDR'], "__auth_failure");
+ $Event->add();
+ }
+ return $this->userid;
+ }
+
+ function doUpdateUser($access_token) {
+ $eclipse_profile_url = "https://accounts.eclipse.org/oauth2/UserInfo";
+
+ $options = array(
+ 'http' => array(
+ 'header' => array(
+ "Authorization: Bearer $access_token"
+ )
+ )
+ );
+ $context = stream_context_create($options);
+ $result = file_get_contents($eclipse_profile_url, false, $context);
+ if ($result === false) {
+ $GLOBALS['g_ERRSTRS'][1] = error_get_last()["message"];
+ return 0;
+ }
+
+ $profile = json_decode($result, true, 10);
+ if ($profile === null) {
+ $GLOBALS['g_ERRSTRS'][1] = error_get_last()["message"];
+ return 0;
+ }
+
+ $_sub = $profile["sub"];
+ $_username = $profile["name"];
+ $_first_name = $profile["given_name"];
+ $_last_name = $profile["family_name"];
+ $_is_committer = $profile["is_committer"] ? 1 : 0;
+
+ // check if user already exist or logged in for the first time
+ global $dbh;
+ $sql = "SELECT userid FROM users WHERE sub = '" . sqlSanitize($_sub, $dbh) . "'";
+ $result = mysqli_query($dbh, $sql);
+ if ($result === false) {
+ $GLOBALS['g_ERRSTRS'][1] = mysqli_error($dbh);
+ return 0;
+ }
+ $row = mysqli_fetch_array($result);
+ $_userid = $row !== null ? $row[0] : 0;
+ $first_login = ! $_userid;
+
+ if ($first_login) {
+ // try to match existing username to OpenID subject
+ $sql = "UPDATE users SET sub = '" . sqlSanitize($_sub, $dbh) . "' WHERE username = '" . sqlSanitize($_username, $dbh) . "' AND userid > 3 LIMIT 1";
+ $result = mysqli_query($dbh, $sql);
+ if ($result === false) {
+ $GLOBALS['g_ERRSTRS'][1] = mysqli_error($dbh);
+ return 0;
+ }
+ if (mysqli_affected_rows($dbh)) {
+ $sql = "SELECT userid FROM users WHERE sub = '" . sqlSanitize($_sub, $dbh) . "'";
+ $result = mysqli_query($dbh, $sql);
+ if ($result === false) {
+ $GLOBALS['g_ERRSTRS'][1] = mysqli_error($dbh);
+ return 0;
+ }
+ $row = mysqli_fetch_array($result);
+ $_userid = $row !== null ? $row[0] : 0;
+ $first_login = ! $_userid;
+ }
+ }
+
+ $sql = ($first_login ? "INSERT INTO " : "UPDATE ");
+ $sql .= "users SET ";
+ $sql .= "username = '" . sqlSanitize($_username, $dbh) . "', ";
+ $sql .= "first_name = '" . sqlSanitize($_first_name, $dbh) . "', ";
+ $sql .= "last_name = '" . sqlSanitize($_last_name, $dbh) . "', ";
+ $sql .= "is_committer = $_is_committer, ";
+ $sql .= "updated_on = NOW(), ";
+ $sql .= "updated_at = NOW()";
+ if ($first_login) {
+ $sql .= ", created_on = NOW(), ";
+ $sql .= "created_at = NOW(), ";
+ $sql .= "sub = '" . sqlSanitize($_sub, $dbh) . "'";
+ } else {
+ $sql .= " WHERE sub = '" . sqlSanitize($_sub, $dbh) . "'";
+ }
+ $result = mysqli_query($dbh, $sql);
+ if ($result === false) {
+ $GLOBALS['g_ERRSTRS'][1] = mysqli_error($dbh);
+ return 0;
+ }
+ return $first_login ? mysqli_insert_id($dbh) : $_userid;
}
function loadFromID($_userid) {
diff --git a/html/content/en_login_oauth.php b/html/content/en_login_oauth.php
new file mode 100644
index 0000000..5a5f651
--- /dev/null
+++ b/html/content/en_login_oauth.php
@@ -0,0 +1,42 @@
+<div id="maincontent">
+<div id="midcolumn">
+<h1><?= $pageTitle ?></h1>
+
+<div id="index-page">
+
+ <a href="https://accounts.eclipse.org/"><img src="<?php echo imageRoot() ?>/large_icons/categories/preferences-desktop-peripherals.png"> <h2>An Eclipse account is all you need</h2></a>
+ <br style='clear: both;'>
+ <p>If you don't already have an Eclipse account then <a href="https://accounts.eclipse.org/">create one today</a>.
+ If logging in doesn't work after a few minutes, please contact <a href="mailto:webmaster@eclipse.org">webmaster@eclipse.org</a>.</p>
+
+ <br style='clear: both;'>
+ <p>If you already have an Eclipse account, then authenticate and start helping Eclipse speak your language.</p>
+
+<form style="margin-left: 35px;" name="frmLogin" method="POST">
+<div>
+
+ <?php
+ if($GLOBALS['g_ERRSTRS'][0] || $GLOBALS['g_ERRSTRS'][1]){
+ ?>
+ <img style='margin-left: 70px;' src='<?php echo imageRoot() ?>/small_icons/actions/process-stop.png'>
+ <div style='color: red; font-weight: bold; '><?=$GLOBALS['g_ERRSTRS'][0]?></div>
+ <div style='color: red; font-weight: bold; '><?=$GLOBALS['g_ERRSTRS'][1]?></div>
+ <br style='clear: both;'>
+ <?php
+ }else{
+ ?>
+ <br style='clear: both;'>
+ <?php
+ }
+ ?>
+</div>
+
+<div style='margin-left: 65px;'>
+<input type="submit" name="oauth" value="Authenticate with Eclipse account" style="font-size:14px;" />
+</div>
+
+</form>
+</div>
+</div>
+<br class='clearing'>
+</div>
diff --git a/html/global.php b/html/global.php
index 225407c..9c718c3 100644
--- a/html/global.php
+++ b/html/global.php
@@ -39,6 +39,7 @@
if (isset($_SERVER['REQUEST_URI']) &&
(strpos($_SERVER['REQUEST_URI'], "login.php") == FALSE) &&
+ (strpos($_SERVER['REQUEST_URI'], "login_oauth.php") == FALSE) &&
(strpos($_SERVER['REQUEST_URI'], "callback") == FALSE)) {
SetSessionVar('s_pageLast', $_SERVER['REQUEST_URI']);
}
@@ -56,7 +57,7 @@
$Session = new Session();
if(!$Session->validate()) {
- exitTo("login.php");
+ exitTo("login_oauth.php");
}
else {
$User = new User();
@@ -65,7 +66,7 @@
}
}
else {
- exitTo("login.php");
+ exitTo("login_oauth.php");
}
}
diff --git a/html/importing.php b/html/importing.php
index 87d630c..66375e6 100644
--- a/html/importing.php
+++ b/html/importing.php
@@ -45,13 +45,13 @@
<li>Submit the bug.
</ol>
- <a href="login.php"><img src="<?php echo imageRoot() ?>/large_icons/apps/preferences-desktop-theme.png"><h2>Project Leads</h2></a>
+ <a href="login_oauth.php"><img src="<?php echo imageRoot() ?>/large_icons/apps/preferences-desktop-theme.png"><h2>Project Leads</h2></a>
<br style='clear: both;'>
<p>
If you are a project lead and your project is not included in Babel then follow the steps below.
</p>
<ol>
- <li><a href="login.php">Log into Babel</a>.
+ <li><a href="login_oauth.php">Log into Babel</a>.
<li>Click on the 'FOR COMMITTERS' link at the top left hand corner of the web page.
<li>Follow the instructions on that page, good luck!
</ol>
diff --git a/html/login_oauth.php b/html/login_oauth.php
new file mode 100644
index 0000000..8dfd4ee
--- /dev/null
+++ b/html/login_oauth.php
@@ -0,0 +1,118 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2019 Paul Pazderski and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Paul Pazderski - initial API and implementation
+ *******************************************************************************/
+include ("global.php");
+InitPage("");
+
+require_once (dirname(__FILE__) . "/../classes/system/user.class.php");
+require_once (dirname(__FILE__) . "/../classes/system/session.class.php");
+
+$pageTitle = "Contribute Translations to Babel";
+$pageKeywords = "translation,language,nlpack,pack,eclipse,babel";
+
+$eclipse_oauth_api_url = "https://accounts.eclipse.org/oauth2/authorize";
+$eclipse_oauth_token_url = "https://accounts.eclipse.org/oauth2/token";
+
+$OAUTH = getHTTPParameter("oauth", "POST");
+$CODE = getHTTPParameter("code", "GET");
+$STATE = getHTTPParameter("state", "GET");
+$SUBMIT = getHTTPParameter("submit", "GET");
+$ERROR = getHTTPParameter("error", "GET");
+if (! empty($OAUTH)) {
+ global $addon;
+ $oauth_params = $addon->callHook("oauth_params");
+ $state = createNonce();
+ SetSessionVar("oauth_state", $state);
+ $params = array(
+ 'response_type' => 'code',
+ 'client_id' => $oauth_params["client_id"],
+ 'redirect_uri' => $oauth_params["client_callback"],
+ 'scope' => 'openid profile',
+ 'state' => $state
+ );
+ exitTo($eclipse_oauth_api_url . "?" . http_build_query($params));
+} else if ($ERROR == "consent_required") {
+ // do nothing; user aborted login
+} else if (! empty($CODE)) {
+ // check state
+ $saved_state = GetSessionVar("oauth_state");
+ if ($STATE !== $saved_state) {
+ $GLOBALS['g_ERRSTRS'][0] = "Authentication failed.";
+ $GLOBALS['g_ERRSTRS'][1] = "Request was not started from login page.";
+ } else {
+ global $addon;
+ $oauth_params = $addon->callHook("oauth_params");
+ $params = array(
+ 'grant_type' => 'authorization_code',
+ 'client_id' => $oauth_params["client_id"],
+ 'client_secret' => $oauth_params["client_secret"],
+ 'code' => $CODE,
+ 'redirect_uri' => $oauth_params["client_callback"],
+ 'state' => $STATE
+ );
+ $options = array(
+ 'http' => array(
+ 'header' => array(
+ "Content-type: application/json"
+ ),
+ 'method' => 'POST',
+ 'content' => json_encode($params)
+ )
+ );
+ $context = stream_context_create($options);
+ $result = file_get_contents($eclipse_oauth_token_url, false, $context);
+ if ($result === false) {
+ $GLOBALS['g_ERRSTRS'][0] = "Login failed.";
+ $GLOBALS['g_ERRSTRS'][1] = error_get_last()["message"];
+ } else {
+ $result = json_decode($result, true, 10);
+ if ($result === null) {
+ $GLOBALS['g_ERRSTRS'][0] = "Login failed.";
+ } else {
+ $User = new User();
+ $uid = $User->updateUser($result["access_token"]);
+ if ($uid <= 0) {
+ $GLOBALS['g_ERRSTRS'][0] = "Login failed.";
+ } else {
+ $User->loadFromID($uid);
+
+ $Session = new Session();
+ $Session->create($User->userid, true);
+ SetSessionVar('User', $User);
+ if (isset($_SESSION['s_pageLast']) && ! empty($_SESSION['s_pageLast'])) {
+ exitTo($_SESSION['s_pageLast']);
+ }
+ exitTo("translate.php");
+ }
+ }
+ }
+ }
+} else if ($SUBMIT == "Logout") {
+ $Session = new Session();
+ $Session->destroy();
+ // we're logging out, therefore we don't have a user anymore
+ $User = null;
+ $GLOBALS['g_ERRSTRS'][0] = "You have successfully logged out. You can login again using the button below.";
+}
+
+global $addon;
+$addon->callHook("head");
+
+include ("content/en_login_oauth.php");
+
+global $addon;
+$addon->callHook("footer");
+
+// Function to create a simple unguessable random string.
+function createNonce() {
+ return md5(openssl_random_pseudo_bytes(20));
+}
+?>
\ No newline at end of file
diff --git a/html/map_files.php b/html/map_files.php
index 6c68c2d..b9d6e0e 100644
--- a/html/map_files.php
+++ b/html/map_files.php
@@ -23,7 +23,7 @@
}
if($User->is_committer != 1) {
- exitTo("login.php?errNo=3214","error: 3214 - you must be an Eclipse committer to access this page.");
+ exitTo("login_oauth.php?errNo=3214","error: 3214 - you must be an Eclipse committer to access this page.");
}
require(dirname(__FILE__) . "/../classes/file/file.class.php");
diff --git a/html/project_source_locations.php b/html/project_source_locations.php
index 666187b..a870ee4 100644
--- a/html/project_source_locations.php
+++ b/html/project_source_locations.php
@@ -22,7 +22,7 @@
}
if($User->is_committer != 1) {
- exitTo("login.php?errNo=3214","error: 3214 - you must be an Eclipse committer to access this page.");
+ exitTo("login_oauth.php?errNo=3214","error: 3214 - you must be an Eclipse committer to access this page.");
}
require(dirname(__FILE__) . "/../classes/file/file.class.php");