diff --git a/htdocs/admin/emailcollector_card.php b/htdocs/admin/emailcollector_card.php index e5bf3142259..c25d0240995 100644 --- a/htdocs/admin/emailcollector_card.php +++ b/htdocs/admin/emailcollector_card.php @@ -413,7 +413,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea } else { $keyforprovider = ''; } - $keyforsupportedoauth2array = preg_replace('/-.*$/', '', $keyforsupportedoauth2array); + $keyforsupportedoauth2array = preg_replace('/-.*$/', '', strtoupper($keyforsupportedoauth2array)); $keyforsupportedoauth2array = 'OAUTH_'.$keyforsupportedoauth2array.'_NAME'; if (!empty($supportedoauth2array)) { diff --git a/htdocs/admin/oauthlogintokens.php b/htdocs/admin/oauthlogintokens.php index 08f98cebad5..8471f14da08 100644 --- a/htdocs/admin/oauthlogintokens.php +++ b/htdocs/admin/oauthlogintokens.php @@ -30,6 +30,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/oauth.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; use OAuth\Common\Storage\DoliStorage; +use OAuth\Common\Consumer\Credentials; $supportedoauth2array = getSupportedOauth2Array(); @@ -114,6 +115,101 @@ if ($action == 'setvalue' && $user->admin) { $action = ''; } +// Test a refresh of a token using the refresh token +if ($action == 'refreshtoken' && $user->admin) { + $keyforprovider = GETPOST('keyforprovider'); + $OAUTH_SERVICENAME = GETPOST('service'); + + // Show value of token + $tokenobj = null; + // Load OAUth libraries + require_once DOL_DOCUMENT_ROOT.'/includes/OAuth/bootstrap.php'; + + $keyforsupportedoauth2array = $OAUTH_SERVICENAME; + if (preg_match('/^.*-/', $keyforsupportedoauth2array)) { + $keyforprovider = preg_replace('/^.*-/', '', $keyforsupportedoauth2array); + } else { + $keyforprovider = ''; + } + $keyforsupportedoauth2array = preg_replace('/-.*$/', '', strtoupper($keyforsupportedoauth2array)); + $keyforsupportedoauth2array = 'OAUTH_'.$keyforsupportedoauth2array.'_NAME'; + + $keyforparamtenant = 'OAUTH_'.strtoupper(empty($supportedoauth2array[$keyforsupportedoauth2array]['callbackfile']) ? 'Unknown' : $supportedoauth2array[$keyforsupportedoauth2array]['callbackfile']).($keyforprovider ? '-'.$keyforprovider : '').'_TENANT'; + + // Dolibarr storage + $storage = new DoliStorage($db, $conf, $keyforprovider, getDolGlobalString($keyforparamtenant)); + try { + // $OAUTH_SERVICENAME is for example 'Google-keyforprovider' + print ''."\n"; + + dol_syslog("oauthlogintokens.php: Read token for service ".$OAUTH_SERVICENAME); + $tokenobj = $storage->retrieveAccessToken($OAUTH_SERVICENAME); + + $expire = ($tokenobj->getEndOfLife() !== -9002 && $tokenobj->getEndOfLife() !== -9001 && time() > ($tokenobj->getEndOfLife() - 30)); + // We have to save the refresh token in a memory variable because Google give it only once + $refreshtoken = $tokenobj->getRefreshToken(); + print ''; + + //print $tokenobj->getExtraParams()['id_token'].'
'; + //print $tokenobj->getAccessToken().'
'; + //print $tokenobj->getRefreshToken().'
'; + + //var_dump($expire); + + // We do the refresh even if not expired, this is the goal of action. + $oauthname = explode('-', $OAUTH_SERVICENAME); + $keyforoauthservice = strtoupper($oauthname[0]).(empty($oauthname[1]) ? '' : '-'.$oauthname[1]); + $credentials = new Credentials( + getDolGlobalString('OAUTH_'.$keyforoauthservice.'_ID'), + getDolGlobalString('OAUTH_'.$keyforoauthservice.'_SECRET'), + getDolGlobalString('OAUTH_'.$keyforoauthservice.'_URLCALLBACK') + ); + + $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); + + $scopes = array(); + if (preg_match('/^Microsoft/', $OAUTH_SERVICENAME)) { + //$extraparams = $tokenobj->getExtraParams(); + $tmp = explode('-', $OAUTH_SERVICENAME); + $scopes = explode(',', getDolGlobalString('OAUTH_'.strtoupper($tmp[0]).(empty($tmp[1]) ? '' : '-'.$tmp[1]).'_SCOPE')); + } + + // ex service is Google-Emails we need only the first part Google + $apiService = $serviceFactory->createService($oauthname[0], $credentials, $storage, $scopes); + + if ($apiService instanceof OAuth\OAuth2\Service\AbstractService || $apiService instanceof OAuth\OAuth1\Service\AbstractService) { + // ServiceInterface does not provide refreshAccessToekn, AbstractService does + dol_syslog("oauthlogintokens.php: call refreshAccessToken to get the new access token"); + $tokenobj = $apiService->refreshAccessToken($tokenobj); // This call refresh and store the new token (but does not include the refresh token) + + dol_syslog("oauthlogintokens.php: call setRefreshToken"); + $tokenobj->setRefreshToken($refreshtoken); // Restore the refresh token + + dol_syslog("oauthlogintokens.php: call storeAccessToken to save the new access token + the old refresh token"); + $storage->storeAccessToken($OAUTH_SERVICENAME, $tokenobj); // This save the new token including the refresh token + + if ($expire) { + setEventMessages($langs->trans("OldTokenWasExpiredItHasBeenRefresh"), null, 'mesgs'); + } else { + setEventMessages($langs->trans("OldTokenWasNotExpiredButItHasBeenRefresh"), null, 'mesgs'); + } + } else { + dol_print_error($db, 'apiService is not a correct OAUTH2 Abstract service'); + } + + dol_syslog("oauthlogintokens.php: Read token again for service ".$OAUTH_SERVICENAME); + $tokenobj = $storage->retrieveAccessToken($OAUTH_SERVICENAME); + } catch (Exception $e) { + // Return an error if token not found + print $e->getMessage(); + } +} + /* * View @@ -176,7 +272,7 @@ if ($mode == 'setup' && $user->admin) { $keybeforeprovider = $keyforsupportedoauth2array; $keyforprovider = ''; } - $keyforsupportedoauth2array = preg_replace('/-.*$/', '', $keyforsupportedoauth2array); + $keyforsupportedoauth2array = preg_replace('/-.*$/', '', strtoupper($keyforsupportedoauth2array)); $keyforsupportedoauth2array = 'OAUTH_'.$keyforsupportedoauth2array.'_NAME'; $nameofservice = ucfirst(strtolower(empty($supportedoauth2array[$keyforsupportedoauth2array]['callbackfile']) ? 'Unknown' : $supportedoauth2array[$keyforsupportedoauth2array]['callbackfile'])); @@ -191,6 +287,8 @@ if ($mode == 'setup' && $user->admin) { } $state = $shortscope; // TODO USe a better state + $urltorefresh = $_SERVER["PHP_SELF"].'?action=refreshtoken&token='.newToken(); + // Define $urltorenew, $urltodelete, $urltocheckperms if ($keyforsupportedoauth2array == 'OAUTH_GITHUB_NAME') { // List of keys that will be converted into scopes (from constants 'SCOPE_state_in_uppercase' in file of service). @@ -220,6 +318,9 @@ if ($mode == 'setup' && $user->admin) { if ($urltorenew) { $urltorenew .= '&keyforprovider='.urlencode($keyforprovider); } + if ($urltorefresh) { + $urltorefresh .= '&keyforprovider='.urlencode($keyforprovider).'&service='.urlencode($OAUTH_SERVICENAME); + } if ($urltodelete) { $urltodelete .= '&keyforprovider='.urlencode($keyforprovider); } @@ -234,6 +335,7 @@ if ($mode == 'setup' && $user->admin) { // $OAUTH_SERVICENAME is for example 'Google-keyforprovider' print ''."\n"; $tokenobj = $storage->retrieveAccessToken($OAUTH_SERVICENAME); + print ''; //print $storage->token.'
'; //print $tokenobj->getExtraParams()['id_token'].'
'; //print $tokenobj->getAccessToken().'
'; @@ -348,6 +450,11 @@ if ($mode == 'setup' && $user->admin) { print $form->textwithpicto('', $langs->trans('RequestAccess')); print '
'; } + // Request remote token + if ($urltorefresh && $refreshtoken) { + print ''.$langs->trans('RefreshToken').''; + } + // Check remote access if ($urltocheckperms) { print '
'.$langs->trans("ToCheckDeleteTokenOnProvider", $OAUTH_SERVICENAME).': '.$urltocheckperms.''; diff --git a/htdocs/emailcollector/class/emailcollector.class.php b/htdocs/emailcollector/class/emailcollector.class.php index cfa1c65e93c..bab0e76a46f 100644 --- a/htdocs/emailcollector/class/emailcollector.class.php +++ b/htdocs/emailcollector/class/emailcollector.class.php @@ -1144,7 +1144,7 @@ class EmailCollector extends CommonObject } else { $keyforprovider = ''; } - $keyforsupportedoauth2array = preg_replace('/-.*$/', '', $keyforsupportedoauth2array); + $keyforsupportedoauth2array = preg_replace('/-.*$/', '', strtoupper($keyforsupportedoauth2array)); $keyforsupportedoauth2array = 'OAUTH_'.$keyforsupportedoauth2array.'_NAME'; if (!empty($supportedoauth2array)) { @@ -1185,11 +1185,22 @@ class EmailCollector extends CommonObject $serviceFactory = new \OAuth\ServiceFactory(); $oauthname = explode('-', $OAUTH_SERVICENAME); // ex service is Google-Emails we need only the first part Google - $apiService = $serviceFactory->createService($oauthname[0], $credentials, $storage, array()); + + $scopes = array(); + if (preg_match('/^Microsoft/', $OAUTH_SERVICENAME)) { + //$extraparams = $tokenobj->getExtraParams(); + $tmp = explode('-', $OAUTH_SERVICENAME); + $scopes = explode(',', getDolGlobalString('OAUTH_'.strtoupper($tmp[0]).(empty($tmp[1]) ? '' : '-'.$tmp[1]).'_SCOPE')); + } + + $apiService = $serviceFactory->createService($oauthname[0], $credentials, $storage, $scopes); + '@phan-var-force OAuth\OAuth2\Service\AbstractService|OAuth\OAuth1\Service\AbstractService $apiService'; // createService is only ServiceInterface - // We have to save the token because Google give it only once + $refreshtoken = $tokenobj->getRefreshToken(); $tokenobj = $apiService->refreshAccessToken($tokenobj); + + // We have to save the token because answer give it only once $tokenobj->setRefreshToken($refreshtoken); $storage->storeAccessToken($OAUTH_SERVICENAME, $tokenobj); } diff --git a/htdocs/includes/OAuth/OAuth2/Service/AbstractService.php b/htdocs/includes/OAuth/OAuth2/Service/AbstractService.php index b6f89118d83..bc28762c688 100644 --- a/htdocs/includes/OAuth/OAuth2/Service/AbstractService.php +++ b/htdocs/includes/OAuth/OAuth2/Service/AbstractService.php @@ -218,12 +218,31 @@ abstract class AbstractService extends BaseAbstractService implements ServiceInt 'refresh_token' => $refreshToken, ); + // @CHANGE LDR + //global $conf; $conf->global->OAUTH_ADD_SCOPE_AND_TENANT_IN_REFRESH_MESSAGE = 1; + if (getDolGlobalString('OAUTH_ADD_SCOPE_AND_TENANT_IN_REFRESH_MESSAGE')) { + if (!empty($this->scopes)) { + $parameters['scope'] = implode($this->getScopesDelimiter(), $this->scopes); + } + if ($this->storage->getTenant()) { + $parameters['tenant'] = $this->storage->getTenant(); + } + } + //var_dump($parameters); + $responseBody = $this->httpClient->retrieveResponse( $this->getAccessTokenEndpoint(), $parameters, $this->getExtraOAuthHeaders() ); - //print $responseBody;exit; // We must have a result "{"token_type":"Bearer","scope... + + + // @CHANGE LDR + $data = json_decode((string) $responseBody, true); + if (is_array($data) && !empty($data['error_description'])) { + print 'Oauth2 AbstractService error received: '.$data['error_description']; + } + //print $responseBody;exit; // We must have a result string "{"token_type":"Bearer","error_description":...,"scope... $token = $this->parseAccessTokenResponse($responseBody); $this->storage->storeAccessToken($this->service(), $token);