Skip to content

Commit

Permalink
TICKET 8093: Google OAuth 2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
AnTopch committed Dec 21, 2017
1 parent 5209ed8 commit 8218662
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 94 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ a `memory_limit` value of 256MB.
### Oauth configuration

In 1.9.17 there is new authorization method - using OAuth providers.
Currently it configured to work with Google OAuth, but you can add
any OAuth server that support protocol 2.0 and 2-step authentication:
get access code, then get access token.
Authentication against multiple oauth providers is supported.
Currently it configured to work with Google OAuth and Github, but you can add
any OAuth server that support protocol 2.0 and 2-step authentication.

#### There are some restrictions in using OAuth:
1. OAuth should not be specified as Default Authentication method
Expand All @@ -108,6 +108,7 @@ logon via regular email/password into Testlink.
oauth_client_secret - secret code
oauth_grant_type - authorization_code is default value
oauth_url - url of OAuth server
token_url - url for getting token
oauth_profile - url of OAuth profile page

oauth_grant_type, oauth_scope - specific parameters for several OAuth providers. They are not necessary
Expand Down
39 changes: 29 additions & 10 deletions config.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -373,17 +373,36 @@
$tlCfg->noExpDateUsers = array('admin');

/**
* OAUTH
* OAUTH auth
*/
$tlCfg->authentication['oauth_enabled'] = true;
$tlCfg->authentication['oauth_client_id'] = 'CLIENT_ID';
$tlCfg->authentication['oauth_client_secret'] = 'CLIENT_SECRET';
$tlCfg->authentication['oauth_grant_type'] = 'authorization_code'; //Can be authorization_code (by default), client_credentials or password
$tlCfg->authentication['oauth_url'] = 'https://accounts.google.com/o/oauth2';
$tlCfg->authentication['oauth_force_single'] = false; //if false then the only user will be selected automatically (applied for google)
//$tlCfg->authentication['oauth_domain'] = 'google.com'; //the domain you want to whitelist
$tlCfg->authentication['oauth_profile'] = 'https://www.googleapis.com/oauth2/v1/userinfo';
$tlCfg->authentication['oauth_scope'] = 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile';

//Google
$tlCfg->authentication['oauth'] = array();
$tlCfg->authentication['oauth'][1]['oauth_enabled'] = true;
$tlCfg->authentication['oauth'][1]['oauth_name'] = 'google';
$tlCfg->authentication['oauth'][1]['oauth_icon'] = 'google.png'; //Get from /gui/themes/default/images
$tlCfg->authentication['oauth'][1]['oauth_client_id'] = 'CLIENT_ID';
$tlCfg->authentication['oauth'][1]['oauth_client_secret'] = 'CLIENT_SECRET';
$tlCfg->authentication['oauth'][1]['oauth_grant_type'] = 'authorization_code'; //Can be authorization_code (by default), client_credentials or password
$tlCfg->authentication['oauth'][1]['oauth_url'] = 'https://accounts.google.com/o/oauth2/auth';
$tlCfg->authentication['oauth'][1]['token_url'] = 'https://accounts.google.com/o/oauth2/token';
$tlCfg->authentication['oauth'][1]['oauth_force_single'] = false; //if false then the only user will be selected automatically (applied for google)
//$tlCfg->authentication['oauth'][1]['oauth_domain'] = 'google.com'; //the domain you want to whitelist
$tlCfg->authentication['oauth'][1]['oauth_profile'] = 'https://www.googleapis.com/oauth2/v1/userinfo';
$tlCfg->authentication['oauth'][1]['oauth_scope'] = 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile';

//Github
$tlCfg->authentication['oauth'][2]['oauth_enabled'] = true;
$tlCfg->authentication['oauth'][2]['oauth_name'] = 'github';
$tlCfg->authentication['oauth'][2]['oauth_icon'] = 'github.png'; //Get from /gui/themes/default/images
$tlCfg->authentication['oauth'][2]['oauth_client_id'] = 'CLIENT_ID';
$tlCfg->authentication['oauth'][2]['oauth_client_secret'] = 'CLIENT_SECRET';
$tlCfg->authentication['oauth'][2]['oauth_grant_type'] = 'authorization_code'; //Can be authorization_code (by default), client_credentials or password
$tlCfg->authentication['oauth'][2]['oauth_url'] = 'https://github.com/login/oauth/authorize';
$tlCfg->authentication['oauth'][2]['token_url'] = 'https://github.com/login/oauth/access_token';
$tlCfg->authentication['oauth'][2]['oauth_force_single'] = false; //if false then the only user will be selected automatically (applied for google)
$tlCfg->authentication['oauth'][2]['oauth_profile'] = 'https://api.github.com/user';
$tlCfg->authentication['oauth'][2]['oauth_scope'] = 'user:email';

/**
* Single Sign On authentication
Expand Down
15 changes: 7 additions & 8 deletions gui/templates/login-model-marcobiedermann.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,13 @@
<input type="submit" value="{$labels.btn_login}">
</div>

{if $tlCfg->authentication['oauth_enabled']}
<div class="button">
<a style="text-decoration: none; color:#ffffff;" href="{$gui->oauth}">
<img src="{$tlCfg->theme_dir}images/{$tlCfg->logo_oauth}" style="height: 42px; vertical-align:middle;">
<span style="padding: 10px;">{$labels.oauth_login}</span></a>
</div>
{/if}

{foreach from=$gui->oauth item=oauth_item}
<div class="button">
<a style="text-decoration: none; color:#ffffff;" href="{$oauth_item->link}">
<img src="{$tlCfg->theme_dir}images/{$oauth_item->icon}" style="height: 42px; vertical-align:middle;">
<span style="padding: 10px;">{$labels.oauth_login}{$oauth_item->name}</span></a>
</div>
{/foreach}
</form>

<p class="text--center">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ input {
color: #ffffff;
outline: 0;
font-weight: bold;
margin-bottom: 15px;
/* text-transform: uppercase; */
}
.button:focus, .button:hover {
Expand Down
72 changes: 8 additions & 64 deletions lib/functions/oauth_api.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,23 @@
* @since 1.9.17
*
*/

// Create correct link for oauth
function oauth_link($authCfg)
function oauth_link($oauthCfg)
{
$promt = 'none';
if ($tlCfg->authentication['oauth_force_single'])
if ($oauthCfg['oauth_force_single'])
$promt = 'consent';

$oauth_url = $authCfg['oauth_url'] . '/auth';
$oauth_url = $oauthCfg['oauth_url'];
$oauth_params = array(
'redirect_uri' => isset($_SERVER['HTTPS']) ? 'https://' : 'http://' . $_SERVER[HTTP_HOST]. '/login.php?oauth=true',
'redirect_uri' => isset($_SERVER['HTTPS']) ? 'https://' : 'http://' . $_SERVER[HTTP_HOST]. '/login.php?oauth='.$oauthCfg['oauth_name'],
'response_type' => 'code',
'prompt' => $promt,
'client_id' => $authCfg['oauth_client_id'],
'scope' => $authCfg['oauth_scope']
'client_id' => $oauthCfg['oauth_client_id'],
'scope' => $oauthCfg['oauth_scope']
);


$url = $oauth_url . '?' . urldecode(http_build_query($oauth_params));
return $url;
}
Expand All @@ -46,59 +45,4 @@ function create_oauth_user_db($login, $options)
$user->isActive = true;
$user->setPassword('oauth');
return ($user->writeToDB($db) == tl::OK);
}

//Get token
function oauth_get_token($authCfg, $code)
{

$result = new stdClass();
$result->status = array('status' => tl::OK, 'msg' => null);

//Params to get token
$oauthParams = array(
'code' => $code,
'grant_type' => $authCfg['oauth_grant_type'],
'client_id' => $authCfg['oauth_client_id'],
'redirect_uri' => isset($_SERVER['HTTPS']) ? 'https://' : 'http://' . $_SERVER[HTTP_HOST]. '/login.php?oauth=true',
'client_secret' => $authCfg['oauth_client_secret']
);
$url = $authCfg['oauth_url'] . '/token';

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, urldecode(http_build_query($oauthParams)));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
$result_curl = curl_exec($curl);
curl_close($curl);
$tokenInfo = json_decode($result_curl, true);

//If token is received start session
if (isset($tokenInfo['access_token'])){
$oauthParams['access_token'] = $tokenInfo['access_token'];
$userInfo = json_decode(file_get_contents($authCfg['oauth_profile'] . '?' . urldecode(http_build_query($oauthParams))), true);

if (isset($userInfo['id'])){
if (isset($authCfg['oauth_domain'])) {
$domain = substr(strrchr($userInfo['email'], "@"), 1);
if ($domain !== $authCfg['oauth_domain']){
$result->status['msg'] = 'User doesn\'t correspond to Oauth policy';
$result->status['status'] = tl::ERROR;
}
}
} else {
$result->status['msg'] = 'User ID is empty';
$result->status['status'] = tl::ERROR;
}

$result->userInfo = $userInfo;
} else {
$result->status['msg'] = 'An error occurred during getting token';
$result->status['status'] = tl::ERROR;
}

return $result;

}
}
83 changes: 83 additions & 0 deletions lib/functions/oauth_providers/github.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php
/**
* TestLink Open Source Project - http://testlink.sourceforge.net/
* This script is distributed under the GNU General Public License 2 or later.
*
* @filesource github.php
*
* Github OAUTH API (authentication)
*
* @internal revisions
* @since 1.9.17
*
*/

//Get token
function oauth_get_token($authCfg, $code)
{

$result = new stdClass();
$result->status = array('status' => tl::OK, 'msg' => null);

//Params to get token
$oauthParams = array(
'code' => $code,
'client_id' => $authCfg['oauth_client_id'],
'redirect_uri' => isset($_SERVER['HTTPS']) ? 'https://' : 'http://' . $_SERVER[HTTP_HOST]. '/login.php?oauth=github',
'client_secret' => $authCfg['oauth_client_secret']
);

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $authCfg['token_url']);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/json'));
curl_setopt($curl, CURLOPT_POSTFIELDS, urldecode(http_build_query($oauthParams)));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_COOKIESESSION, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
$result_curl = curl_exec($curl);
curl_close($curl);

$tokenInfo = json_decode($result_curl);

//If token is received start session
if (isset($tokenInfo->access_token)){
$oauthParams['access_token'] = $tokenInfo->access_token;

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $authCfg['oauth_profile'].'?'.urldecode(http_build_query($tokenInfo)));
curl_setopt($curl, CURLOPT_USERAGENT,'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:7.0.1) Gecko/20100101 Firefox/7.0.1');
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/xml','Accept: application/json'));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$result_curl = curl_exec($curl);
$userInfo = json_decode($result_curl, true);
curl_close($curl);

if (!isset($userInfo['login'])){
$result->status['msg'] = 'User ID is empty';
$result->status['status'] = tl::ERROR;
}

//Get email
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $authCfg['oauth_profile'].'/emails?'.urldecode(http_build_query($tokenInfo)));
curl_setopt($curl, CURLOPT_USERAGENT,'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:7.0.1) Gecko/20100101 Firefox/7.0.1');
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/xml','Accept: application/json'));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$result_curl = curl_exec($curl);
$emailInfo = json_decode($result_curl, true);
curl_close($curl);

$options = new stdClass();
$options->givenName = $userInfo['login'];
$options->familyName = $userInfo['id'];
$options->user = $emailInfo[0]['email'];
$options->auth = 'oauth';

$result->options = $options;
} else {
$result->status['msg'] = 'An error occurred during getting token';
$result->status['status'] = tl::ERROR;
}
return $result;
}
73 changes: 73 additions & 0 deletions lib/functions/oauth_providers/google.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php
/**
* TestLink Open Source Project - http://testlink.sourceforge.net/
* This script is distributed under the GNU General Public License 2 or later.
*
* @filesource google.php
*
* Google OAUTH API (authentication)
*
* @internal revisions
* @since 1.9.17
*
*/

//Get token
function oauth_get_token($authCfg, $code)
{

$result = new stdClass();
$result->status = array('status' => tl::OK, 'msg' => null);

//Params to get token
$oauthParams = array(
'code' => $code,
'grant_type' => $authCfg['oauth_grant_type'],
'client_id' => $authCfg['oauth_client_id'],
'redirect_uri' => isset($_SERVER['HTTPS']) ? 'https://' : 'http://' . $_SERVER[HTTP_HOST]. '/login.php?oauth=google',
'client_secret' => $authCfg['oauth_client_secret']
);

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $authCfg['token_url']);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, urldecode(http_build_query($oauthParams)));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
$result_curl = curl_exec($curl);
curl_close($curl);
$tokenInfo = json_decode($result_curl, true);

//If token is received start session
if (isset($tokenInfo['access_token'])){
$oauthParams['access_token'] = $tokenInfo['access_token'];
$userInfo = json_decode(file_get_contents($authCfg['oauth_profile'] . '?' . urldecode(http_build_query($oauthParams))), true);

if (isset($userInfo['id'])){
if (isset($authCfg['oauth_domain'])) {
$domain = substr(strrchr($userInfo['email'], "@"), 1);
if ($domain !== $authCfg['oauth_domain']){
$result->status['msg'] = 'User doesn\'t correspond to Oauth policy';
$result->status['status'] = tl::ERROR;
}
}
} else {
$result->status['msg'] = 'User ID is empty';
$result->status['status'] = tl::ERROR;
}

$options = new stdClass();
$options->givenName = $userInfo['given_name'];
$options->familyName = $userInfo['family_name'];
$options->user = $userInfo['email'];
$options->auth = 'oauth';

$result->options = $options;
} else {
$result->status['msg'] = 'An error occurred during getting token';
$result->status['status'] = tl::ERROR;
}

return $result;

}
2 changes: 1 addition & 1 deletion locale/en_GB/strings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ $TLS_passwd_lost = "Your password has been sent to the email account you specifi
$TLS_password_reseted = "New password has been sent via mail";
$TLS_session_expired = "Your session has expired! Please login again.";
$TLS_your_first_login = "Welcome to TestLink! You have guest access now. Please login ...";
$TLS_oauth_login = "Sign in with Google";
$TLS_oauth_login = "Sign in with ";

// ----- newest_tcversions.php -----
$TLS_no_linked_tcversions = "no linked Test Case versions";
Expand Down
2 changes: 1 addition & 1 deletion locale/en_US/strings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ $TLS_password_reseted = "New password has been sent via mail";
$TLS_please_login = "Please log in ...";
$TLS_session_expired = "Your session has expired! Please login again.";
$TLS_your_first_login = "Welcome to TestLink! You have guest access now. Please login ...";
$TLS_oauth_login = "Sign in with Google";
$TLS_oauth_login = "Sign in with ";

// ----- newest_tcversions.php -----
$TLS_no_linked_tcversions = "no linked Test Case versions";
Expand Down
Loading

0 comments on commit 8218662

Please sign in to comment.