Maxi debug of OAuth module

This commit is contained in:
Laurent Destailleur
2023-02-07 20:12:53 +01:00
parent 2190dcc9f7
commit e71ad2e23b
16 changed files with 348 additions and 141 deletions

View File

@@ -84,6 +84,11 @@ if ($action == 'update') {
$error++;
}
}
if (GETPOSTISSET($constvalue.'_TENANT')) {
if (!dolibarr_set_const($db, $constvalue.'_TENANT', GETPOST($constvalue.'_TENANT'), 'chaine', 0, '', $conf->entity)) {
$error++;
}
}
if (GETPOSTISSET($constvalue.'_SCOPE')) {
if (is_array(GETPOST($constvalue.'_SCOPE'))) {
$scopestring = implode(',', GETPOST($constvalue.'_SCOPE'));
@@ -128,6 +133,8 @@ if ($action == 'confirm_delete') {
$callbacktodel .= '/core/modules/oauth/stripelive_oauthcallback.php?action=delete&keyforprovider='.$provider.'&token='.newToken().'&backtourl='.urlencode($backtourl);
} elseif ($label == 'OAUTH_STRIPE_TEST') {
$callbacktodel .= '/core/modules/oauth/stripetest_oauthcallback.php?action=delete&keyforprovider='.$provider.'&token='.newToken().'&backtourl='.urlencode($backtourl);
} elseif ($label == 'OAUTH_MICROSOFT') {
$callbacktodel .= '/core/modules/oauth/microsoft_oauthcallback.php?action=delete&keyforprovider='.$provider.'&token='.newToken().'&backtourl='.urlencode($backtourl);
} elseif ($label == 'OAUTH_OTHER') {
$callbacktodel .= '/core/modules/oauth/generic_oauthcallback.php?action=delete&keyforprovider='.$provider.'&token='.newToken().'&backtourl='.urlencode($backtourl);
}
@@ -242,8 +249,10 @@ if (count($listinsetup) > 0) {
$keyforsupportedoauth2array = preg_replace('/^OAUTH_/', '', $keyforsupportedoauth2array);
$keyforsupportedoauth2array = preg_replace('/_NAME$/', '', $keyforsupportedoauth2array);
if (preg_match('/^.*-/', $keyforsupportedoauth2array)) {
$keybeforeprovider = preg_replace('/-.*$/', '', $keyforsupportedoauth2array);
$keyforprovider = preg_replace('/^.*-/', '', $keyforsupportedoauth2array);
} else {
$keybeforeprovider = $keyforsupportedoauth2array;
$keyforprovider = '';
}
$keyforsupportedoauth2array = preg_replace('/-.*$/', '', $keyforsupportedoauth2array);
@@ -337,6 +346,16 @@ if (count($listinsetup) > 0) {
print '<td></td>';
print '</tr>';
// Tenant
if ($keybeforeprovider == 'MICROSOFT') {
print '<tr class="oddeven value">';
print '<td><label for="'.$key[2].'">'.$langs->trans("OAUTH_TENANT").'</label></td>';
print '<td><input type="text" size="100" id="OAUTH_'.$keybeforeprovider.($keyforprovider ? '-'.$keyforprovider : '').'_TENANT" name="OAUTH_'.$keybeforeprovider.($keyforprovider ? '-'.$keyforprovider : '').'_TENANT" value="'.getDolGlobalString('OAUTH_'.$keybeforeprovider.($keyforprovider ? '-'.$keyforprovider : '').'_TENANT').'">';
print '</td>';
print '<td></td>';
print '</tr>';
}
// TODO Move this into token generation ?
if ($supported) {
if ($keyforsupportedoauth2array == 'OAUTH_OTHER_NAME') {
@@ -386,7 +405,7 @@ if (count($listinsetup) > 0) {
print '</div>';
print $form->buttonsSaveCancel("Modify", '');
print $form->buttonsSaveCancel("Save", '');
print '</form>';
}

View File

@@ -162,8 +162,10 @@ if ($mode == 'setup' && $user->admin) {
$keyforsupportedoauth2array = preg_replace('/^OAUTH_/', '', $keyforsupportedoauth2array);
$keyforsupportedoauth2array = preg_replace('/_NAME$/', '', $keyforsupportedoauth2array);
if (preg_match('/^.*-/', $keyforsupportedoauth2array)) {
$keybeforeprovider = preg_replace('/-.*$/', '', $keyforsupportedoauth2array);
$keyforprovider = preg_replace('/^.*-/', '', $keyforsupportedoauth2array);
} else {
$keybeforeprovider = $keyforsupportedoauth2array;
$keyforprovider = '';
}
$keyforsupportedoauth2array = preg_replace('/-.*$/', '', $keyforsupportedoauth2array);
@@ -179,13 +181,12 @@ if ($mode == 'setup' && $user->admin) {
$state = $shortscope; // TODO USe a better state
// Define $urltorenew, $urltodelete, $urltocheckperms
// TODO Use array $supportedoauth2array
if ($keyforsupportedoauth2array == 'OAUTH_GITHUB_NAME') {
// List of keys that will be converted into scopes (from constants 'SCOPE_state_in_uppercase' in file of service).
// We pass this param list in to 'state' because we need it before and after the redirect.
// Note: github does not accept csrf key inside the state parameter (only known values)
$urltorenew = $urlwithroot.'/core/modules/oauth/github_oauthcallback.php?shortscope='.urlencode($shortscope).'&state='.$shortscope.'&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php');
$urltorenew = $urlwithroot.'/core/modules/oauth/github_oauthcallback.php?shortscope='.urlencode($shortscope).'&state='.urlencode($shortscope).'&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php');
$urltodelete = $urlwithroot.'/core/modules/oauth/github_oauthcallback.php?action=delete&token='.newToken().'&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php');
$urltocheckperms = 'https://github.com/settings/applications/';
} elseif ($keyforsupportedoauth2array == 'OAUTH_GOOGLE_NAME') {
@@ -195,17 +196,9 @@ if ($mode == 'setup' && $user->admin) {
$urltorenew = $urlwithroot.'/core/modules/oauth/google_oauthcallback.php?shortscope='.urlencode($shortscope).'&state='.urlencode($state).'-'.$oauthstateanticsrf.'&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php');
$urltodelete = $urlwithroot.'/core/modules/oauth/google_oauthcallback.php?action=delete&token='.newToken().'&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php');
$urltocheckperms = 'https://security.google.com/settings/security/permissions';
} elseif ($keyforsupportedoauth2array == 'OAUTH_STRIPE_TEST_NAME') {
$urltorenew = $urlwithroot.'/core/modules/oauth/stripetest_oauthcallback.php?shortscope='.urlencode($shortscope).'&state='.urlencode($state).'&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php');
$urltodelete = '';
$urltocheckperms = '';
} elseif ($keyforsupportedoauth2array == 'OAUTH_STRIPE_LIVE_NAME') {
$urltorenew = $urlwithroot.'/core/modules/oauth/stripelive_oauthcallback.php?shortscope='.urlencode($shortscope).'&state='.urlencode($state).'&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php');
$urltodelete = '';
$urltocheckperms = '';
} elseif ($keyforsupportedoauth2array = 'OAUTH_OTHER_NAME') {
$urltorenew = $urlwithroot.'/core/modules/oauth/generic_oauthcallback.php?shortscope='.urlencode($shortscope).'&state='.urlencode($state).'&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php');
$urltodelete = '';
} elseif (!empty($supportedoauth2array[$keyforsupportedoauth2array]['returnurl'])) {
$urltorenew = $urlwithroot.$supportedoauth2array[$keyforsupportedoauth2array]['returnurl'].'?shortscope='.urlencode($shortscope).'&state='.urlencode($state).'&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php');
$urltodelete = $urlwithroot.$supportedoauth2array[$keyforsupportedoauth2array]['returnurl'].'?action=delete&token='.newToken().'&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php');
$urltocheckperms = '';
} else {
$urltorenew = '';
@@ -220,17 +213,19 @@ if ($mode == 'setup' && $user->admin) {
$urltodelete .= '&keyforprovider='.urlencode($keyforprovider);
}
// Show value of token
$tokenobj = null;
// Token
require_once DOL_DOCUMENT_ROOT.'/includes/OAuth/bootstrap.php';
// Dolibarr storage
$storage = new DoliStorage($db, $conf);
$storage = new DoliStorage($db, $conf, $keyforprovider);
try {
// $OAUTH_SERVICENAME is for example 'Google-keyforprovider'
print $OAUTH_SERVICENAME;
$tokenobj = $storage->retrieveAccessToken($OAUTH_SERVICENAME);
} catch (Exception $e) {
// Return an error if token not found
//print $e->getMessage();
}
// Set other properties
@@ -321,7 +316,11 @@ if ($mode == 'setup' && $user->admin) {
// Links to delete/checks token
if (is_object($tokenobj)) {
//test on $storage->hasAccessToken($OAUTH_SERVICENAME) ?
if ($urltodelete) {
print '<a class="button smallpaddingimp" href="'.$urltodelete.'">'.$langs->trans('DeleteAccess').'</a><br>';
} else {
print '<span class="opacitymedium">'.$langs->trans('GoOnTokenProviderToDeleteToken').'</span><br>';
}
}
// Request remote token
if ($urltorenew) {

View File

@@ -4102,7 +4102,7 @@ function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $
'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
'stock', 'resize', 'service', 'stats', 'trip',
'security', 'setup', 'share-alt', 'sign-out', 'split', 'stripe', 'stripe-s', 'switch_off', 'switch_on', 'switch_on_red', 'tools', 'unlink', 'uparrow', 'user', 'user-tie', 'vcard', 'wrench',
'github', 'google', 'jabber', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top', 'commercial', 'companies',
'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
@@ -4123,7 +4123,7 @@ function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $
if (in_array($pictowithouttext, array('card', 'bell', 'clock', 'establishment', 'generic', 'minus-square', 'object_generic', 'pdf', 'plus-square', 'timespent', 'note', 'off', 'on', 'object_bookmark', 'bookmark', 'vcard'))) {
$fa = 'far';
}
if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
$fa = 'fab';
}

View File

@@ -25,15 +25,17 @@
// Supported OAUTH (a provider is supported when a file xxx_oauthcallback.php is available into htdocs/core/modules/oauth)
$supportedoauth2array = array(
'OAUTH_GOOGLE_NAME'=>array('callbackfile' => 'google', 'picto' => 'google', 'urlforapp' => 'OAUTH_GOOGLE_DESC', 'name'=>'Google', 'urlforcredentials'=>'https://console.developers.google.com/', 'availablescopes'=> 'userinfo_email,userinfo_profile,openid,email,profile,cloud_print,admin_directory_user,gmail_full'),
'OAUTH_GOOGLE_NAME'=>array('callbackfile' => 'google', 'picto' => 'google', 'urlforapp' => 'OAUTH_GOOGLE_DESC', 'name'=>'Google', 'urlforcredentials'=>'https://console.developers.google.com/', 'availablescopes'=> 'userinfo_email,userinfo_profile,openid,email,profile,cloud_print,admin_directory_user,gmail_full', 'returnurl'=>'/core/modules/oauth/google_oauthcallback.php'),
);
if (isModEnabled('stripe')) {
$supportedoauth2array['OAUTH_STRIPE_TEST_NAME'] = array('callbackfile' => 'stripetest', 'picto' => 'stripe', 'urlforapp' => '', 'name'=>'StripeTest', 'urlforcredentials'=>'', 'availablescopes'=>'read_write');
$supportedoauth2array['OAUTH_STRIPE_LIVE_NAME'] = array('callbackfile' => 'stripelive', 'picto' => 'stripe', 'urlforapp' => '', 'name'=>'StripeLive', 'urlforcredentials'=>'', 'availablescopes'=>'read_write');
$supportedoauth2array['OAUTH_STRIPE_TEST_NAME'] = array('callbackfile' => 'stripetest', 'picto' => 'stripe', 'urlforapp' => '', 'name'=>'StripeTest', 'urlforcredentials'=>'', 'availablescopes'=>'read_write', 'returnurl'=>'/core/modules/oauth/stripetest_oauthcallback.php');
$supportedoauth2array['OAUTH_STRIPE_LIVE_NAME'] = array('callbackfile' => 'stripelive', 'picto' => 'stripe', 'urlforapp' => '', 'name'=>'StripeLive', 'urlforcredentials'=>'', 'availablescopes'=>'read_write', 'returnurl'=>'/core/modules/oauth/stripelive_oauthcallback.php');
}
$supportedoauth2array['OAUTH_GITHUB_NAME'] = array('callbackfile' => 'github', 'picto' => 'github', 'urlforapp' => 'OAUTH_GITHUB_DESC', 'name'=>'GitHub', 'urlforcredentials'=>'https://github.com/settings/developers', 'availablescopes'=>'user,public_repo');
$supportedoauth2array['OAUTH_GITHUB_NAME'] = array('callbackfile' => 'github', 'picto' => 'github', 'urlforapp' => 'OAUTH_GITHUB_DESC', 'name'=>'GitHub', 'urlforcredentials'=>'https://github.com/settings/developers', 'availablescopes'=>'user,public_repo', 'returnurl'=>'/core/modules/oauth/github_oauthcallback.php');
if (getDolGlobalInt('MAIN_FEATURES_LEVEL') >= 2) {
$supportedoauth2array['OAUTH_OTHER_NAME'] = array('callbackfile' => 'generic', 'picto' => 'generic', 'urlforapp' => 'OAUTH_OTHER_DESC', 'name'=>'Other', 'urlforcredentials'=>'', 'availablescopes'=>'Standard');
$supportedoauth2array['OAUTH_OTHER_NAME'] = array('callbackfile' => 'generic', 'picto' => 'generic', 'urlforapp' => 'OAUTH_OTHER_DESC', 'name'=>'Other', 'urlforcredentials'=>'', 'availablescopes'=>'Standard', 'returnurl'=>'/core/modules/oauth/generic_oauthcallback.php');
// See https://learn.microsoft.com/fr-fr/azure/active-directory/develop/quickstart-register-app#register-an-application
$supportedoauth2array['OAUTH_MICROSOFT_NAME'] = array('callbackfile' => 'microsoft', 'picto' => 'microsoft', 'urlforapp' => 'OAUTH_MICROSOFT_DESC', 'name'=>'Microsoft', 'urlforcredentials'=>'https://portal.azure.com/', 'availablescopes'=>'openid,offline_access,profile,email,IMAP.AccessAsUser.All', 'returnurl'=>'/core/modules/oauth/microsoft_oauthcallback.php');
}

View File

@@ -66,7 +66,7 @@ $httpClient = new \OAuth\Common\Http\Client\CurlClient();
$serviceFactory->setHttpClient($httpClient);
// Dolibarr storage
$storage = new DoliStorage($db, $conf);
$storage = new DoliStorage($db, $conf, $keyforprovider);
// Setup the credentials for the requests
$keyforparamid = 'OAUTH_'.$genericstring.($keyforprovider ? '-'.$keyforprovider : '').'_ID';
@@ -77,9 +77,11 @@ $credentials = new Credentials(
$currentUri->getAbsoluteUri()
);
$state = GETPOST('state');
$requestedpermissionsarray = array();
if (GETPOST('state')) {
$requestedpermissionsarray = explode(',', GETPOST('state')); // Example: 'user'. 'state' parameter is standard to retrieve some parameters back
if ($state) {
$requestedpermissionsarray = explode(',', $state); // Example: 'user'. 'state' parameter is standard to retrieve some parameters back
}
if ($action != 'delete' && empty($requestedpermissionsarray)) {
print 'Error, parameter state is not defined';
@@ -88,7 +90,8 @@ if ($action != 'delete' && empty($requestedpermissionsarray)) {
//var_dump($requestedpermissionsarray);exit;
// Instantiate the Api service using the credentials, http client and storage mechanism for the token
$apiService = $serviceFactory->createService($genericstring, $credentials, $storage, $requestedpermissionsarray);
// ucfirst(strtolower($genericstring)) must be the name of a class into OAuth/OAuth2/Services/Xxxx
$apiService = $serviceFactory->createService(ucfirst(strtolower($genericstring)), $credentials, $storage, $requestedpermissionsarray);
/*
var_dump($genericstring.($keyforprovider ? '-'.$keyforprovider : ''));
@@ -128,35 +131,25 @@ if ($action == 'delete') {
exit();
}
if (GETPOST('code')) { // We are coming from oauth provider page
if (GETPOST('code') || GETPOST('error')) { // We are coming from oauth provider page
// We should have
//$_GET=array('code' => string 'aaaaaaaaaaaaaa' (length=20), 'state' => string 'user,public_repo' (length=16))
dol_syslog("We are coming from the oauth provider page");
//llxHeader('',$langs->trans("OAuthSetup"));
//$linkback='<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
//print load_fiche_titre($langs->trans("OAuthSetup"),$linkback,'title_setup');
//print dol_get_fiche_head();
// retrieve the CSRF state parameter
$state = GETPOSTISSET('state') ? GETPOST('state') : null;
//print '<table>';
dol_syslog("We are coming from the oauth provider page code=".dol_trunc(GETPOST('code'), 5)." error=".GETPOST('error'));
// This was a callback request from service, get the token
try {
//var_dump($_GET['code']);
//var_dump($state);
//var_dump($apiService); // OAuth\OAuth2\Service\GitHub
//var_dump($apiService); // OAuth\OAuth2\Service\Xxx
if (GETPOST('error')) {
setEventMessages(GETPOST('error').' '.GETPOST('error_description'), null, 'errors');
} else {
//$token = $apiService->requestAccessToken(GETPOST('code'), $state);
$token = $apiService->requestAccessToken(GETPOST('code'));
// Github is a service that does not need state to be stored.
// Into constructor of GitHub, the call
// parent::__construct($credentials, $httpClient, $storage, $scopes, $baseApiUri)
// has not the ending parameter to true like the Google class constructor.
setEventMessages($langs->trans('NewTokenStored'), null, 'mesgs'); // Stored into object managed by class DoliStorage so into table oauth_token
}
$backtourl = $_SESSION["backtourlsavedbeforeoauthjump"];
unset($_SESSION["backtourlsavedbeforeoauthjump"]);
@@ -166,15 +159,17 @@ if (GETPOST('code')) { // We are coming from oauth provider page
} catch (Exception $e) {
print $e->getMessage();
}
} else { // If entry on page with no parameter, we arrive here
} else {
// If we enter this page without 'code' parameter, we arrive here. This is the case when we want to get the redirect
// to the OAuth provider login page.
$_SESSION["backtourlsavedbeforeoauthjump"] = $backtourl;
$_SESSION["oauthkeyforproviderbeforeoauthjump"] = $keyforprovider;
$_SESSION['oauthstateanticsrf'] = $state;
// This may create record into oauth_state before the header redirect.
// Creation of record with state in this tables depend on the Provider used (see its constructor).
if (GETPOST('state')) {
$url = $apiService->getAuthorizationUri(array('state' => GETPOST('state')));
if ($state) {
$url = $apiService->getAuthorizationUri(array('state' => $state));
} else {
$url = $apiService->getAuthorizationUri(); // Parameter state will be randomly generated
}

View File

@@ -65,7 +65,7 @@ $httpClient = new \OAuth\Common\Http\Client\CurlClient();
$serviceFactory->setHttpClient($httpClient);
// Dolibarr storage
$storage = new DoliStorage($db, $conf);
$storage = new DoliStorage($db, $conf, $keyforprovider);
// Setup the credentials for the requests
$keyforparamid = 'OAUTH_GITHUB'.($keyforprovider ? '-'.$keyforprovider : '').'_ID';
@@ -115,30 +115,21 @@ if ($action == 'delete') {
exit();
}
if (!empty($_GET['code'])) { // We are coming from oauth provider page
if (GETPOST('code')) { // We are coming from oauth provider page
// We should have
//$_GET=array('code' => string 'aaaaaaaaaaaaaa' (length=20), 'state' => string 'user,public_repo' (length=16))
dol_syslog("We are coming from the oauth provider page");
//llxHeader('',$langs->trans("OAuthSetup"));
//$linkback='<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
//print load_fiche_titre($langs->trans("OAuthSetup"),$linkback,'title_setup');
//print dol_get_fiche_head();
// retrieve the CSRF state parameter
$state = isset($_GET['state']) ? $_GET['state'] : null;
//print '<table>';
dol_syslog("We are coming from the oauth provider page code=".dol_trunc(GETPOST('code'), 5));
// This was a callback request from service, get the token
try {
//var_dump($_GET['code']);
//var_dump($state);
//var_dump($apiService); // OAuth\OAuth2\Service\GitHub
//$token = $apiService->requestAccessToken($_GET['code'], $state);
$token = $apiService->requestAccessToken($_GET['code']);
// Github is a service that does not need state to be stored.
//$token = $apiService->requestAccessToken(GETPOST('code'), $state);
$token = $apiService->requestAccessToken(GETPOST('code'));
// Github is a service that does not need state to be stored as second paramater of requestAccessToken
// Into constructor of GitHub, the call
// parent::__construct($credentials, $httpClient, $storage, $scopes, $baseApiUri)
// has not the ending parameter to true like the Google class constructor.

View File

@@ -137,7 +137,7 @@ if ($action == 'delete') {
}
if (GETPOST('code')) { // We are coming from oauth provider page.
dol_syslog("We are coming from the oauth provider page keyforprovider=".$keyforprovider);
dol_syslog("We are coming from the oauth provider page keyforprovider=".$keyforprovider." code=".dol_trunc(GETPOST('code'), 5));
// We must validate that the $state is the same than the one into $_SESSION['oauthstateanticsrf'], return error if not.
if (isset($_SESSION['oauthstateanticsrf']) && $state != $_SESSION['oauthstateanticsrf']) {
@@ -146,7 +146,6 @@ if (GETPOST('code')) { // We are coming from oauth provider page.
} else {
// This was a callback request from service, get the token
try {
//var_dump($_GET['code']);
//var_dump($state);
//var_dump($apiService); // OAuth\OAuth2\Service\Google
@@ -193,7 +192,7 @@ if (GETPOST('code')) { // We are coming from oauth provider page.
}
}
} else {
// If we enter this page without 'code' parameter, we arrive here. this is the case when we want to get the redirect
// If we enter this page without 'code' parameter, we arrive here. This is the case when we want to get the redirect
// to the OAuth provider login page.
$_SESSION["backtourlsavedbeforeoauthjump"] = $backtourl;
$_SESSION["oauthkeyforproviderbeforeoauthjump"] = $keyforprovider;

View File

@@ -0,0 +1,211 @@
<?php
/* Copyright (C) 2022 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2015 Frederic France <frederic.france@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* \file htdocs/core/modules/oauth/microsoft_oauthcallback.php
* \ingroup oauth
* \brief Page to get oauth callback
*/
// Load Dolibarr environment
require '../../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/includes/OAuth/bootstrap.php';
use OAuth\Common\Storage\DoliStorage;
use OAuth\Common\Consumer\Credentials;
use OAuth\OAuth2\Service\GitHub;
// Define $urlwithroot
$urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
$urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
//$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
$action = GETPOST('action', 'aZ09');
$backtourl = GETPOST('backtourl', 'alpha');
$keyforprovider = GETPOST('keyforprovider', 'aZ09');
if (empty($keyforprovider) && !empty($_SESSION["oauthkeyforproviderbeforeoauthjump"]) && (GETPOST('code') || $action == 'delete')) {
$keyforprovider = $_SESSION["oauthkeyforproviderbeforeoauthjump"];
}
$genericstring = 'MICROSOFT';
/**
* Create a new instance of the URI class with the current URI, stripping the query string
*/
$uriFactory = new \OAuth\Common\Http\Uri\UriFactory();
//$currentUri = $uriFactory->createFromSuperGlobalArray($_SERVER);
//$currentUri->setQuery('');
$currentUri = $uriFactory->createFromAbsolute($urlwithroot.'/core/modules/oauth/microsoft_oauthcallback.php');
/**
* Load the credential for the service
*/
/** @var $serviceFactory \OAuth\ServiceFactory An OAuth service factory. */
$serviceFactory = new \OAuth\ServiceFactory();
$httpClient = new \OAuth\Common\Http\Client\CurlClient();
// TODO Set options for proxy and timeout
// $params=array('CURLXXX'=>value, ...)
//$httpClient->setCurlParameters($params);
$serviceFactory->setHttpClient($httpClient);
// Dolibarr storage
$storage = new DoliStorage($db, $conf, $keyforprovider);
// Setup the credentials for the requests
$keyforparamid = 'OAUTH_'.$genericstring.($keyforprovider ? '-'.$keyforprovider : '').'_ID';
$keyforparamsecret = 'OAUTH_'.$genericstring.($keyforprovider ? '-'.$keyforprovider : '').'_SECRET';
$keyforparamtenant = 'OAUTH_'.$genericstring.($keyforprovider ? '-'.$keyforprovider : '').'_TENANT';
$credentials = new Credentials(
getDolGlobalString($keyforparamid),
getDolGlobalString($keyforparamsecret),
$currentUri->getAbsoluteUri()
);
$state = GETPOST('state');
$requestedpermissionsarray = array();
if ($state) {
$requestedpermissionsarray = explode(',', $state); // Example: 'user'. 'state' parameter is standard to retrieve some parameters back
}
if ($action != 'delete' && empty($requestedpermissionsarray)) {
print 'Error, parameter state is not defined';
exit;
}
//var_dump($requestedpermissionsarray);exit;
// Instantiate the Api service using the credentials, http client and storage mechanism for the token
// ucfirst(strtolower($genericstring)) must be the name of a class into OAuth/OAuth2/Services/Xxxx
// $requestedpermissionsarray contains list of scopes.
// Conversion into URL is done by Reflection on constant with name SCOPE_scope_in_uppercase
try {
$apiService = $serviceFactory->createService(ucfirst(strtolower($genericstring)), $credentials, $storage, $requestedpermissionsarray);
} catch (Exception $e) {
print $e->getMessage();
exit;
}
/*
var_dump($genericstring.($keyforprovider ? '-'.$keyforprovider : ''));
var_dump($credentials);
var_dump($storage);
var_dump($requestedpermissionsarray);
*/
if (empty($apiService)) {
print 'Error, failed to create serviceFactory';
exit;
}
// access type needed to have oauth provider refreshing token
//$apiService->setAccessType('offline');
$langs->load("oauth");
if (!getDolGlobalString($keyforparamid)) {
accessforbidden('Setup of service is not complete. Customer ID is missing');
}
if (!getDolGlobalString($keyforparamsecret)) {
accessforbidden('Setup of service is not complete. Secret key is missing');
}
/*
* Actions
*/
if ($action == 'delete') {
$storage->clearToken($genericstring);
setEventMessages($langs->trans('TokenDeleted'), null, 'mesgs');
header('Location: '.$backtourl);
exit();
}
//dol_syslog("GET=".join(',', $_GET));
if (GETPOST('code') || GETPOST('error')) { // We are coming from oauth provider page
// We should have
//$_GET=array('code' => string 'aaaaaaaaaaaaaa' (length=20), 'state' => string 'user,public_repo' (length=16))
dol_syslog("We are coming from the oauth provider page code=".dol_trunc(GETPOST('code'), 5)." error=".GETPOST('error'));
// This was a callback request from service, get the token
try {
//var_dump($state);
//var_dump($apiService); // OAuth\OAuth2\Service\Microsoft
if (GETPOST('error')) {
setEventMessages(GETPOST('error').' '.GETPOST('error_description'), null, 'errors');
} else {
$apiService->tenant = getDolGlobalString($keyforparamtenant);
//$token = $apiService->requestAccessToken(GETPOST('code'), $state);
$token = $apiService->requestAccessToken(GETPOST('code'));
// Microsoft is a service that does not need state to be stored as second paramater of requestAccessToken
setEventMessages($langs->trans('NewTokenStored'), null, 'mesgs'); // Stored into object managed by class DoliStorage so into table oauth_token
}
$backtourl = $_SESSION["backtourlsavedbeforeoauthjump"];
unset($_SESSION["backtourlsavedbeforeoauthjump"]);
header('Location: '.$backtourl);
exit();
} catch (Exception $e) {
print $e->getMessage();
}
} else {
// If we enter this page without 'code' parameter, we arrive here. This is the case when we want to get the redirect
// to the OAuth provider login page.
$_SESSION["backtourlsavedbeforeoauthjump"] = $backtourl;
$_SESSION["oauthkeyforproviderbeforeoauthjump"] = $keyforprovider;
$_SESSION['oauthstateanticsrf'] = $state;
//if (!preg_match('/^forlogin/', $state)) {
// $apiService->setApprouvalPrompt('auto');
//}
$apiService->tenant = getDolGlobalString($keyforparamtenant);
// This may create record into oauth_state before the header redirect.
// Creation of record with state in this tables depend on the Provider used (see its constructor).
if ($state) {
$url = $apiService->getAuthorizationUri(array('state' => $state));
} else {
$url = $apiService->getAuthorizationUri(); // Parameter state will be randomly generated
}
// Show url to get authorization
//var_dump((string) $url);exit;
dol_syslog("Redirect to url=".$url);
// we go on oauth provider authorization page
header('Location: '.$url);
exit();
}
/*
* View
*/
// No view at all, just actions
$db->close();

View File

@@ -65,7 +65,7 @@ $httpClient = new \OAuth\Common\Http\Client\CurlClient();
$serviceFactory->setHttpClient($httpClient);
// Dolibarr storage
$storage = new DoliStorage($db, $conf);
$storage = new DoliStorage($db, $conf, $keyforprovider);
// Setup the credentials for the requests
$keyforparamid = 'OAUTH_STRIPE_LIVE'.($keyforprovider ? '-'.$keyforprovider : '').'_ID';
@@ -121,33 +121,20 @@ if ($action == 'delete') {
exit();
}
if (!empty($_GET['code'])) { // We are coming from oauth provider page
if (GETPOST('code')) { // We are coming from oauth provider page
// We should have
//$_GET=array('code' => string 'aaaaaaaaaaaaaa' (length=20), 'state' => string 'user,public_repo' (length=16))
dol_syslog("We are coming from the oauth provider page");
//llxHeader('',$langs->trans("OAuthSetup"));
//$linkback='<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
//print load_fiche_titre($langs->trans("OAuthSetup"),$linkback,'title_setup');
//print dol_get_fiche_head();
// retrieve the CSRF state parameter
$state = isset($_GET['state']) ? $_GET['state'] : null;
//print '<table>';
dol_syslog("We are coming from the oauth provider page code=".dol_trunc(GETPOST('code'), 5));
// This was a callback request from service, get the token
try {
//var_dump($_GET['code']);
//var_dump($state);
//var_dump($apiService); // OAuth\OAuth2\Service\GitHub
//var_dump($apiService); // OAuth\OAuth2\Service\Stripe
//$token = $apiService->requestAccessToken($_GET['code'], $state);
$token = $apiService->requestAccessToken($_GET['code']);
// Github is a service that does not need state to be stored.
// Into constructor of GitHub, the call
// parent::__construct($credentials, $httpClient, $storage, $scopes, $baseApiUri)
// has not the ending parameter to true like the Google class constructor.
//$token = $apiService->requestAccessToken(GETPOST('code'), $state);
$token = $apiService->requestAccessToken(GETPOST('code'));
// Stripe is a service that does not need state to be stored as second paramater of requestAccessToken
setEventMessages($langs->trans('NewTokenStored'), null, 'mesgs'); // Stored into object managed by class DoliStorage so into table oauth_token

View File

@@ -65,7 +65,7 @@ $httpClient = new \OAuth\Common\Http\Client\CurlClient();
$serviceFactory->setHttpClient($httpClient);
// Dolibarr storage
$storage = new DoliStorage($db, $conf);
$storage = new DoliStorage($db, $conf, $keyforprovider);
// Setup the credentials for the requests
$keyforparamid = 'OAUTH_STRIPE_TEST'.($keyforprovider ? '-'.$keyforprovider : '').'_ID';
@@ -121,33 +121,20 @@ if ($action == 'delete') {
exit();
}
if (!empty($_GET['code'])) { // We are coming from oauth provider page
if (GETPOST('code')) { // We are coming from oauth provider page
// We should have
//$_GET=array('code' => string 'aaaaaaaaaaaaaa' (length=20), 'state' => string 'user,public_repo' (length=16))
dol_syslog("We are coming from the oauth provider page");
//llxHeader('',$langs->trans("OAuthSetup"));
//$linkback='<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
//print load_fiche_titre($langs->trans("OAuthSetup"),$linkback,'title_setup');
//print dol_get_fiche_head();
// retrieve the CSRF state parameter
$state = isset($_GET['state']) ? $_GET['state'] : null;
//print '<table>';
dol_syslog("We are coming from the oauth provider page code=".dol_trunc(GETPOST('code'), 5));
// This was a callback request from service, get the token
try {
//var_dump($_GET['code']);
//var_dump($state);
//var_dump($apiService); // OAuth\OAuth2\Service\GitHub
//var_dump($apiService); // OAuth\OAuth2\Service\Stripe
//$token = $apiService->requestAccessToken($_GET['code'], $state);
$token = $apiService->requestAccessToken($_GET['code']);
// Github is a service that does not need state to be stored.
// Into constructor of GitHub, the call
// parent::__construct($credentials, $httpClient, $storage, $scopes, $baseApiUri)
// has not the ending parameter to true like the Google class constructor.
//$token = $apiService->requestAccessToken(GETPOST('code'), $state);
$token = $apiService->requestAccessToken(GETPOST('code'));
// Stripe is a service that does not need state to be stored as second paramater of requestAccessToken
setEventMessages($langs->trans('NewTokenStored'), null, 'mesgs'); // Stored into object managed by class DoliStorage so into table oauth_token

View File

@@ -126,6 +126,9 @@ class DoliStorage implements TokenStorageInterface
$sql.= " SET token = '".$this->db->escape($serializedToken)."'";
$sql.= " WHERE rowid = ".((int) $obj['rowid']);
$resql = $this->db->query($sql);
if (!$resql) {
dol_print_error($this->db);
}
} else {
// save
$sql = "INSERT INTO ".MAIN_DB_PREFIX."oauth_token (service, token, entity, datec)";
@@ -133,6 +136,9 @@ class DoliStorage implements TokenStorageInterface
$sql .= " '".$this->db->idate(dol_now())."'";
$sql .= ")";
$resql = $this->db->query($sql);
if (!$resql) {
dol_print_error($this->db);
}
}
//print $sql;

View File

@@ -12,30 +12,35 @@ use OAuth\Common\Http\Uri\UriInterface;
class Microsoft extends AbstractService
{
const SCOPE_BASIC = 'wl.basic';
const SCOPE_OFFLINE = 'wl.offline_access';
const SCOPE_SIGNIN = 'wl.signin';
const SCOPE_BIRTHDAY = 'wl.birthday';
const SCOPE_CALENDARS = 'wl.calendars';
const SCOPE_CALENDARS_UPDATE = 'wl.calendars_update';
const SCOPE_CONTACTS_BIRTHDAY = 'wl.contacts_birthday';
const SCOPE_CONTACTS_CREATE = 'wl.contacts_create';
const SCOPE_CONTACTS_CALENDARS = 'wl.contacts_calendars';
const SCOPE_CONTACTS_PHOTOS = 'wl.contacts_photos';
const SCOPE_CONTACTS_SKYDRIVE = 'wl.contacts_skydrive';
const SCOPE_EMAILS = 'wl.emails';
const SCOPE_EVENTS_CREATE = 'wl.events_create';
const SCOPE_MESSENGER = 'wl.messenger';
const SCOPE_PHONE_NUMBERS = 'wl.phone_numbers';
const SCOPE_PHOTOS = 'wl.photos';
const SCOPE_POSTAL_ADDRESSES = 'wl.postal_addresses';
const SCOPE_SHARE = 'wl.share';
const SCOPE_SKYDRIVE = 'wl.skydrive';
const SCOPE_SKYDRIVE_UPDATE = 'wl.skydrive_update';
const SCOPE_WORK_PROFILE = 'wl.work_profile';
const SCOPE_APPLICATIONS = 'wl.applications';
const SCOPE_APPLICATIONS_CREATE = 'wl.applications_create';
const SCOPE_IMAP = 'wl.imap';
const SCOPE_BASIC = 'basic';
const SCOPE_OFFLINE_ACCESS = 'offline_access';
const SCOPE_SIGNIN = 'signin';
const SCOPE_BIRTHDAY = 'birthday';
const SCOPE_CALENDARS = 'calendars';
const SCOPE_CALENDARS_UPDATE = 'calendars_update';
const SCOPE_CONTACTS_BIRTHDAY = 'contacts_birthday';
const SCOPE_CONTACTS_CREATE = 'contacts_create';
const SCOPE_CONTACTS_CALENDARS = 'contacts_calendars';
const SCOPE_CONTACTS_PHOTOS = 'contacts_photos';
const SCOPE_CONTACTS_SKYDRIVE = 'contacts_skydrive';
const SCOPE_EMAIL = 'email';
const SCOPE_EVENTS_CREATE = 'events_create';
const SCOPE_MESSENGER = 'messenger';
const SCOPE_OPENID = 'openid';
const SCOPE_PHONE_NUMBERS = 'phone_numbers';
const SCOPE_PHOTOS = 'photos';
const SCOPE_POSTAL_ADDRESSES = 'postal_addresses';
const SCOPE_PROFILE = 'profile';
const SCOPE_SHARE = 'share';
const SCOPE_SKYDRIVE = 'skydrive';
const SCOPE_SKYDRIVE_UPDATE = 'skydrive_update';
const SCOPE_WORK_PROFILE = 'work_profile';
const SCOPE_APPLICATIONS = 'applications';
const SCOPE_APPLICATIONS_CREATE = 'applications_create';
const SCOPE_IMAP = 'imap';
const SOCPE_IMAP_AccessAsUser_All='IMAP.AccessAsUser.All';
public string $tenant;
/**
* MS uses some magical not officialy supported scope to get even moar info like full emailaddresses.
@@ -48,7 +53,8 @@ class Microsoft extends AbstractService
*
* Considering this scope is not officially supported: use with care
*/
const SCOPE_CONTACTS_EMAILS = 'wl.contacts_emails';
const SCOPE_CONTACTS_EMAILS = 'contacts_emails';
public function __construct(
CredentialsInterface $credentials,
@@ -69,7 +75,9 @@ class Microsoft extends AbstractService
*/
public function getAuthorizationEndpoint()
{
return new Uri('https://login.live.com/oauth20_authorize.srf');
//return new Uri('https://login.live.com/oauth20_authorize.srf');
//return new Uri('https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize');
return new Uri('https://login.microsoftonline.com/'.$this->tenant.'/oauth2/v2.0/authorize');
}
/**
@@ -77,7 +85,9 @@ class Microsoft extends AbstractService
*/
public function getAccessTokenEndpoint()
{
return new Uri('https://login.live.com/oauth20_token.srf');
//return new Uri('https://login.live.com/oauth20_token.srf');
//return new Uri('https://login.microsoftonline.com/organizations/oauth2/v2.0/token');
return new Uri('https://login.microsoftonline.com/'.$this->tenant.'/oauth2/v2.0/token');
}
/**

View File

@@ -2348,4 +2348,5 @@ AllowExternalDownload=Allow external download (without login, using a shared lin
DeadlineDayVATSubmission=Deadline day for vat submission on the next month
MaxNumberOfAttachementOnForms=Max number of joinded files in a form
IfDefinedUseAValueBeetween=If defined, use a value between %s and %s
MAIN_IMAP_USE_PHPIMAP=Use the PHP-IMAP library for IMAP instead of native PHP IMAP. This also allow the use of OAuth2 connection for IMAP.
EMailsInGoingDesc=Incoming emails are managed by the module %s. You must enable and configure it if you need to support ingoing emails.
MAIN_IMAP_USE_PHPIMAP=Use the PHP-IMAP library for IMAP instead of native PHP IMAP. This also allow the use of OAuth2 connection for IMAP (module OAuth must also be activated).

View File

@@ -179,4 +179,3 @@ RecordCreatedByEmailCollector=Record created by the Email Collector %s from emai
DefaultBlacklistMailingStatus=Default value for field '%s' when creating a new contact
DefaultStatusEmptyMandatory=Empty but mandatory
WarningLimitSendByDay=WARNING: The setup or contract of your instance limits your number of emails per day to <b>%s</b>. Trying to send more may result in having your instance slow down or suspended. Please contact your support if you need a higher quota.
EMailsInGoingDesc=Incoming emails are managed by the module %s. You must enable and configure it if you need to support ingoing emails.

View File

@@ -33,6 +33,7 @@ OAUTH_STRIPE_TEST_NAME=OAuth Stripe Test
OAUTH_STRIPE_LIVE_NAME=OAuth Stripe Live
OAUTH_ID=OAuth ID
OAUTH_SECRET=OAuth secret
OAUTH_TENANT=OAuth tenant
OAuthProviderAdded=OAuth provider added
AOAuthEntryForThisProviderAndLabelAlreadyHasAKey=An OAuth entry for this provider and label already exists
URLOfServiceForAuthorization=URL provided by OAuth service for authentication