';
+ }
+
// TODO Move this into token generation ?
if ($supported) {
if ($keyforsupportedoauth2array == 'OAUTH_OTHER_NAME') {
@@ -386,7 +405,7 @@ if (count($listinsetup) > 0) {
print '';
- print $form->buttonsSaveCancel("Modify", '');
+ print $form->buttonsSaveCancel("Save", '');
print '';
}
diff --git a/htdocs/admin/oauthlogintokens.php b/htdocs/admin/oauthlogintokens.php
index 9a0532880cd..d6fcc9e0f7c 100644
--- a/htdocs/admin/oauthlogintokens.php
+++ b/htdocs/admin/oauthlogintokens.php
@@ -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) ?
- print ''.$langs->trans('DeleteAccess').' ';
+ if ($urltodelete) {
+ print ''.$langs->trans('DeleteAccess').' ';
+ } else {
+ print ''.$langs->trans('GoOnTokenProviderToDeleteToken').' ';
+ }
}
// Request remote token
if ($urltorenew) {
diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php
index d47b71d39cf..e94b9d5e4c0 100644
--- a/htdocs/core/lib/functions.lib.php
+++ b/htdocs/core/lib/functions.lib.php
@@ -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';
}
diff --git a/htdocs/core/lib/oauth.lib.php b/htdocs/core/lib/oauth.lib.php
index 83359ef1c65..4f504196b47 100644
--- a/htdocs/core/lib/oauth.lib.php
+++ b/htdocs/core/lib/oauth.lib.php
@@ -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');
}
diff --git a/htdocs/core/modules/oauth/generic_oauthcallback.php b/htdocs/core/modules/oauth/generic_oauthcallback.php
index 34422111d5d..a394c7f4986 100644
--- a/htdocs/core/modules/oauth/generic_oauthcallback.php
+++ b/htdocs/core/modules/oauth/generic_oauthcallback.php
@@ -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=''.$langs->trans("BackToModuleList").'';
- //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 '
';
+ 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
- //$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.
+ if (GETPOST('error')) {
+ setEventMessages(GETPOST('error').' '.GETPOST('error_description'), null, 'errors');
+ } else {
+ //$token = $apiService->requestAccessToken(GETPOST('code'), $state);
+ $token = $apiService->requestAccessToken(GETPOST('code'));
- setEventMessages($langs->trans('NewTokenStored'), null, 'mesgs'); // Stored into object managed by class DoliStorage so into table oauth_token
+ 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
}
diff --git a/htdocs/core/modules/oauth/github_oauthcallback.php b/htdocs/core/modules/oauth/github_oauthcallback.php
index 24140718880..7656a1cda37 100644
--- a/htdocs/core/modules/oauth/github_oauthcallback.php
+++ b/htdocs/core/modules/oauth/github_oauthcallback.php
@@ -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=''.$langs->trans("BackToModuleList").'';
- //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 '
';
+ 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.
diff --git a/htdocs/core/modules/oauth/google_oauthcallback.php b/htdocs/core/modules/oauth/google_oauthcallback.php
index ed0caa1a4ff..c26187e4475 100644
--- a/htdocs/core/modules/oauth/google_oauthcallback.php
+++ b/htdocs/core/modules/oauth/google_oauthcallback.php
@@ -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;
diff --git a/htdocs/core/modules/oauth/microsoft_oauthcallback.php b/htdocs/core/modules/oauth/microsoft_oauthcallback.php
new file mode 100644
index 00000000000..ed47eec06e1
--- /dev/null
+++ b/htdocs/core/modules/oauth/microsoft_oauthcallback.php
@@ -0,0 +1,211 @@
+
+ * Copyright (C) 2015 Frederic France
+ *
+ * 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 .
+ */
+
+/**
+ * \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();
diff --git a/htdocs/core/modules/oauth/stripelive_oauthcallback.php b/htdocs/core/modules/oauth/stripelive_oauthcallback.php
index ef35b6573cc..bc16b44461a 100644
--- a/htdocs/core/modules/oauth/stripelive_oauthcallback.php
+++ b/htdocs/core/modules/oauth/stripelive_oauthcallback.php
@@ -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=''.$langs->trans("BackToModuleList").'';
- //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 '
';
+ 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
diff --git a/htdocs/core/modules/oauth/stripetest_oauthcallback.php b/htdocs/core/modules/oauth/stripetest_oauthcallback.php
index a5d481dbef5..12d133da14c 100644
--- a/htdocs/core/modules/oauth/stripetest_oauthcallback.php
+++ b/htdocs/core/modules/oauth/stripetest_oauthcallback.php
@@ -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=''.$langs->trans("BackToModuleList").'';
- //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 '
';
+ 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
diff --git a/htdocs/includes/OAuth/Common/Storage/DoliStorage.php b/htdocs/includes/OAuth/Common/Storage/DoliStorage.php
index 3e09e53fbe6..cf280262e99 100644
--- a/htdocs/includes/OAuth/Common/Storage/DoliStorage.php
+++ b/htdocs/includes/OAuth/Common/Storage/DoliStorage.php
@@ -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;
diff --git a/htdocs/includes/OAuth/OAuth2/Service/AbstractService.php b/htdocs/includes/OAuth/OAuth2/Service/AbstractService.php
index 996506afbec..0de0219306a 100644
--- a/htdocs/includes/OAuth/OAuth2/Service/AbstractService.php
+++ b/htdocs/includes/OAuth/OAuth2/Service/AbstractService.php
@@ -56,8 +56,8 @@ abstract class AbstractService extends BaseAbstractService implements ServiceInt
$this->stateParameterInAuthUrl = $stateParameterInAutUrl;
foreach ($scopes as $scope) {
- if (!$this->isValidScope($scope)) {
- throw new InvalidScopeException('Scope ' . $scope . ' is not valid for service ' . get_class($this));
+ if (!$this->isValidScope($scope)) {
+ throw new InvalidScopeException('Scope ' . $scope . ' is not valid for service ' . get_class($this));
}
}
diff --git a/htdocs/includes/OAuth/OAuth2/Service/Microsoft.php b/htdocs/includes/OAuth/OAuth2/Service/Microsoft.php
index c815b22bd44..b1b6a042c01 100644
--- a/htdocs/includes/OAuth/OAuth2/Service/Microsoft.php
+++ b/htdocs/includes/OAuth/OAuth2/Service/Microsoft.php
@@ -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');
}
/**
diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang
index 66210ef9d52..29551ac0874 100644
--- a/htdocs/langs/en_US/admin.lang
+++ b/htdocs/langs/en_US/admin.lang
@@ -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.
\ No newline at end of file
+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).
diff --git a/htdocs/langs/en_US/mails.lang b/htdocs/langs/en_US/mails.lang
index a112de8ceb2..f83637c9c40 100644
--- a/htdocs/langs/en_US/mails.lang
+++ b/htdocs/langs/en_US/mails.lang
@@ -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 %s. 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.
diff --git a/htdocs/langs/en_US/oauth.lang b/htdocs/langs/en_US/oauth.lang
index 01bb08e38bd..e773c470b30 100644
--- a/htdocs/langs/en_US/oauth.lang
+++ b/htdocs/langs/en_US/oauth.lang
@@ -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