oauth.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. <?php
  2. /* Copyright (C) 2015-2018 Frederic France <frederic.france@netlogic.fr>
  3. * Copyright (C) 2016 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
  4. * Copyright (C) 2022 Laurent Destailleur <eldy@users.sourceforge.net>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. /**
  20. * \file htdocs/admin/oauth.php
  21. * \ingroup oauth
  22. * \brief Setup page to configure oauth access api
  23. */
  24. // Load Dolibarr environment
  25. require '../main.inc.php';
  26. require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
  27. require_once DOL_DOCUMENT_ROOT.'/core/lib/oauth.lib.php';
  28. $supportedoauth2array = getSupportedOauth2Array();
  29. // Define $urlwithroot
  30. $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
  31. $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
  32. //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
  33. // Load translation files required by the page
  34. $langs->loadLangs(array('admin', 'oauth', 'modulebuilder'));
  35. // Security check
  36. if (!$user->admin) {
  37. accessforbidden();
  38. }
  39. $action = GETPOST('action', 'aZ09');
  40. $provider = GETPOST('provider', 'aZ09');
  41. $label = GETPOST('label', 'aZ09');
  42. $servicetoeditname = GETPOST('servicetoeditname', 'aZ09');
  43. $error = 0;
  44. /*
  45. * Actions
  46. */
  47. if ($action == 'add') { // $provider is OAUTH_XXX
  48. if ($provider && $provider != '-1') {
  49. $constname = strtoupper($provider).($label ? '-'.$label : '').'_ID';
  50. if (getDolGlobalString($constname)) {
  51. setEventMessages($langs->trans("AOAuthEntryForThisProviderAndLabelAlreadyHasAKey"), null, 'errors');
  52. $error++;
  53. } else {
  54. dolibarr_set_const($db, $constname, $langs->trans('ToComplete'), 'chaine', 0, '', $conf->entity);
  55. setEventMessages($langs->trans("OAuthProviderAdded"), null);
  56. }
  57. }
  58. }
  59. if ($action == 'update') {
  60. foreach ($conf->global as $key => $val) {
  61. if (!empty($val) && preg_match('/^OAUTH_.+_ID$/', $key)) {
  62. $constvalue = str_replace('_ID', '', $key);
  63. $newconstvalue = $constvalue;
  64. if (GETPOSTISSET($constvalue.'_NAME')) {
  65. $newconstvalue = preg_replace('/-.*$/', '', $constvalue).'-'.GETPOST($constvalue.'_NAME');
  66. }
  67. if (GETPOSTISSET($constvalue.'_ID')) {
  68. if (!dolibarr_set_const($db, $newconstvalue.'_ID', GETPOST($constvalue.'_ID'), 'chaine', 0, '', $conf->entity)) {
  69. $error++;
  70. }
  71. }
  72. // If we reset this provider, we also remove the secret
  73. if (GETPOSTISSET($constvalue.'_SECRET')) {
  74. if (!dolibarr_set_const($db, $newconstvalue.'_SECRET', GETPOST($constvalue.'_ID') ? GETPOST($constvalue.'_SECRET') : '', 'chaine', 0, '', $conf->entity)) {
  75. $error++;
  76. }
  77. }
  78. if (GETPOSTISSET($constvalue.'_URLAUTHORIZE')) {
  79. if (!dolibarr_set_const($db, $newconstvalue.'_URLAUTHORIZE', GETPOST($constvalue.'_URLAUTHORIZE'), 'chaine', 0, '', $conf->entity)) {
  80. $error++;
  81. }
  82. }
  83. if (GETPOSTISSET($constvalue.'_TENANT')) {
  84. if (!dolibarr_set_const($db, $constvalue.'_TENANT', GETPOST($constvalue.'_TENANT'), 'chaine', 0, '', $conf->entity)) {
  85. $error++;
  86. }
  87. }
  88. if (GETPOSTISSET($constvalue.'_SCOPE')) {
  89. if (is_array(GETPOST($constvalue.'_SCOPE'))) {
  90. $scopestring = implode(',', GETPOST($constvalue.'_SCOPE'));
  91. } else {
  92. $scopestring = GETPOST($constvalue.'_SCOPE');
  93. }
  94. if (!dolibarr_set_const($db, $newconstvalue.'_SCOPE', $scopestring, 'chaine', 0, '', $conf->entity)) {
  95. $error++;
  96. }
  97. } elseif ($newconstvalue !== $constvalue) {
  98. if (!dolibarr_set_const($db, $newconstvalue.'_SCOPE', '', 'chaine', 0, '', $conf->entity)) {
  99. $error++;
  100. }
  101. }
  102. // If name changed, we have to delete old const and proceed few other changes
  103. if ($constvalue !== $newconstvalue) {
  104. dolibarr_del_const($db, $constvalue.'_ID', $conf->entity);
  105. dolibarr_del_const($db, $constvalue.'_SECRET', $conf->entity);
  106. dolibarr_del_const($db, $constvalue.'_URLAUTHORIZE', $conf->entity);
  107. dolibarr_del_const($db, $constvalue.'_SCOPE', $conf->entity);
  108. // Update name of token
  109. $oldname = preg_replace('/^OAUTH_/', '', $constvalue);
  110. $oldprovider = ucfirst(strtolower(preg_replace('/-.*$/', '', $oldname)));
  111. $oldlabel = preg_replace('/^.*-/', '', $oldname);
  112. $newlabel = preg_replace('/^.*-/', '', $newconstvalue);
  113. $sql = "UPDATE ".MAIN_DB_PREFIX."oauth_token";
  114. $sql.= " SET service = '".$db->escape($oldprovider."-".$newlabel)."'";
  115. $sql.= " WHERE service = '".$db->escape($oldprovider."-".$oldlabel)."'";
  116. $resql = $db->query($sql);
  117. if (!$resql) {
  118. $error++;
  119. }
  120. // Update const where the token was used, might not be exhaustive
  121. if (getDolGlobalString('MAIN_MAIL_SMTPS_OAUTH_SERVICE') == $oldname) {
  122. if (!dolibarr_set_const($db, 'MAIN_MAIL_SMTPS_OAUTH_SERVICE', strtoupper($oldprovider).'-'.$newlabel, 'chaine', 0, '', $conf->entity)) {
  123. $error++;
  124. }
  125. }
  126. }
  127. }
  128. }
  129. if (!$error) {
  130. setEventMessages($langs->trans("SetupSaved"), null);
  131. } else {
  132. setEventMessages($langs->trans("Error"), null, 'errors');
  133. }
  134. }
  135. if ($action == 'confirm_delete') {
  136. $provider = GETPOST('provider', 'aZ09');
  137. $label = GETPOST('label');
  138. $globalkey = empty($provider) ? $label : $label.'-'.$provider;
  139. if (getDolGlobalString($globalkey.'_ID') && getDolGlobalString($globalkey.'_SECRET')) { // If ID and secret exist, we delete first the token
  140. $backtourl = DOL_URL_ROOT.'/admin/oauth.php?action=delete_entry&provider='.$provider.'&label='.$label.'&token='.newToken();
  141. $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
  142. $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT;
  143. $callbacktodel = $urlwithroot;
  144. if ($label == 'OAUTH_GOOGLE') {
  145. $callbacktodel .= '/core/modules/oauth/google_oauthcallback.php?action=delete&keyforprovider='.$provider.'&token='.newToken().'&backtourl='.urlencode($backtourl);
  146. } elseif ($label == 'OAUTH_GITHUB') {
  147. $callbacktodel .= '/core/modules/oauth/github_oauthcallback.php?action=delete&keyforprovider='.$provider.'&token='.newToken().'&backtourl='.urlencode($backtourl);
  148. } elseif ($label == 'OAUTH_STRIPE_LIVE') {
  149. $callbacktodel .= '/core/modules/oauth/stripelive_oauthcallback.php?action=delete&keyforprovider='.$provider.'&token='.newToken().'&backtourl='.urlencode($backtourl);
  150. } elseif ($label == 'OAUTH_STRIPE_TEST') {
  151. $callbacktodel .= '/core/modules/oauth/stripetest_oauthcallback.php?action=delete&keyforprovider='.$provider.'&token='.newToken().'&backtourl='.urlencode($backtourl);
  152. } elseif ($label == 'OAUTH_MICROSOFT') {
  153. $callbacktodel .= '/core/modules/oauth/microsoft_oauthcallback.php?action=delete&keyforprovider='.$provider.'&token='.newToken().'&backtourl='.urlencode($backtourl);
  154. } elseif ($label == 'OAUTH_OTHER') {
  155. $callbacktodel .= '/core/modules/oauth/generic_oauthcallback.php?action=delete&keyforprovider='.$provider.'&token='.newToken().'&backtourl='.urlencode($backtourl);
  156. }
  157. header("Location: ".$callbacktodel);
  158. exit;
  159. } else {
  160. $action = 'delete_entry';
  161. }
  162. }
  163. if ($action == 'delete_entry') {
  164. $provider = GETPOST('provider', 'aZ09');
  165. $label = GETPOST('label');
  166. $globalkey = empty($provider) ? $label : $label.'-'.$provider;
  167. if (!dolibarr_del_const($db, $globalkey.'_NAME', $conf->entity) || !dolibarr_del_const($db, $globalkey.'_ID', $conf->entity) || !dolibarr_del_const($db, $globalkey.'_SECRET', $conf->entity) || !dolibarr_del_const($db, $globalkey.'_URLAUTHORIZE', $conf->entity) || !dolibarr_del_const($db, $globalkey.'_SCOPE', $conf->entity)) {
  168. setEventMessages($langs->trans("ErrorInEntryDeletion"), null, 'errors');
  169. $error++;
  170. } else {
  171. setEventMessages($langs->trans("EntryDeleted"), null);
  172. }
  173. }
  174. /*
  175. * View
  176. */
  177. llxHeader();
  178. $form = new Form($db);
  179. // Confirmation of action process
  180. if ($action == 'delete') {
  181. $formquestion = array();
  182. $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?provider='.GETPOST('provider').'&label='.GETPOST('label'), $langs->trans('OAuthServiceConfirmDeleteTitle'), $langs->trans('OAuthServiceConfirmDeleteMessage'), 'confirm_delete', $formquestion, 0, 1, 220);
  183. print $formconfirm;
  184. }
  185. $linkback = '<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
  186. print load_fiche_titre($langs->trans('ConfigOAuth'), $linkback, 'title_setup');
  187. print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST">';
  188. print '<input type="hidden" name="token" value="'.newToken().'">';
  189. print '<input type="hidden" name="action" value="add">';
  190. $head = oauthadmin_prepare_head();
  191. print dol_get_fiche_head($head, 'services', '', -1, '');
  192. print '<span class="opacitymedium">'.$langs->trans("ListOfSupportedOauthProviders").'</span><br><br>';
  193. print '<select name="provider" id="provider" class="minwidth150">';
  194. print '<option name="-1" value="-1">'.$langs->trans("OAuthProvider").'</option>';
  195. $list = getAllOauth2Array();
  196. // TODO Make a loop directly on getSupportedOauth2Array() and remove getAllOauth2Array()
  197. foreach ($list as $key) {
  198. $supported = 0;
  199. $keyforsupportedoauth2array = $key[0];
  200. if (in_array($keyforsupportedoauth2array, array_keys($supportedoauth2array))) {
  201. $supported = 1;
  202. }
  203. if (!$supported) {
  204. continue; // show only supported
  205. }
  206. print '<option name="'.$keyforsupportedoauth2array.'" value="'.str_replace('_NAME', '', $keyforsupportedoauth2array).'">'.$supportedoauth2array[$keyforsupportedoauth2array]['name'].'</option>'."\n";
  207. }
  208. print '</select>';
  209. print ajax_combobox('provider');
  210. print ' <input type="text" name="label" value="" placeholder="'.$langs->trans("Label").'" pattern="^\S+$" title="'.$langs->trans("SpaceOrSpecialCharAreNotAllowed").'">';
  211. print ' <input type="submit" class="button small" name="add" value="'.$langs->trans("Add").'">';
  212. print '<br>';
  213. print '<br>';
  214. print dol_get_fiche_end();
  215. print '</form>';
  216. $listinsetup = [];
  217. // Define $listinsetup
  218. foreach ($conf->global as $key => $val) {
  219. if (!empty($val) && preg_match('/^OAUTH_.*_ID$/', $key)) {
  220. $provider = preg_replace('/_ID$/', '', $key);
  221. $listinsetup[] = array(
  222. $provider.'_NAME',
  223. $provider.'_ID',
  224. $provider.'_SECRET',
  225. $provider.'_URLAUTHORIZE', // For custom oauth links
  226. $provider.'_SCOPE' // For custom oauth links
  227. );
  228. }
  229. }
  230. if (count($listinsetup) > 0) {
  231. print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST">';
  232. print '<input type="hidden" name="token" value="'.newToken().'">';
  233. print '<input type="hidden" name="action" value="update">';
  234. print '<div class="div-table-responsive-no-min">';
  235. $i = 0;
  236. // $list is defined into oauth.lib.php to the list of supporter OAuth providers.
  237. foreach ($listinsetup as $key) {
  238. $supported = 0;
  239. $keyforsupportedoauth2array = $key[0]; // May be OAUTH_GOOGLE_NAME or OAUTH_GOOGLE_xxx_NAME
  240. $keyforsupportedoauth2array = preg_replace('/^OAUTH_/', '', $keyforsupportedoauth2array);
  241. $keyforsupportedoauth2array = preg_replace('/_NAME$/', '', $keyforsupportedoauth2array);
  242. if (preg_match('/^.*-/', $keyforsupportedoauth2array)) {
  243. $keybeforeprovider = preg_replace('/-.*$/', '', $keyforsupportedoauth2array);
  244. $keyforprovider = preg_replace('/^.*-/', '', $keyforsupportedoauth2array);
  245. } else {
  246. $keybeforeprovider = $keyforsupportedoauth2array;
  247. $keyforprovider = '';
  248. }
  249. $keyforsupportedoauth2array = preg_replace('/-.*$/', '', $keyforsupportedoauth2array);
  250. $keyforsupportedoauth2array = 'OAUTH_'.$keyforsupportedoauth2array.'_NAME';
  251. if (in_array($keyforsupportedoauth2array, array_keys($supportedoauth2array))) {
  252. $supported = 1;
  253. }
  254. if (!$supported) {
  255. continue; // show only supported
  256. }
  257. $i++;
  258. print '<table class="noborder centpercent">';
  259. // OAUTH service name
  260. $label = $langs->trans($keyforsupportedoauth2array);
  261. print '<tr class="liste_titre'.($i > 1 ? ' liste_titre_add' : '').'">';
  262. print '<td class="titlefieldcreate">';
  263. print img_picto('', $supportedoauth2array[$keyforsupportedoauth2array]['picto'], 'class="pictofixedwidth"');
  264. if ($label == $keyforsupportedoauth2array) {
  265. print $supportedoauth2array[$keyforsupportedoauth2array]['name'];
  266. } else {
  267. print $label;
  268. }
  269. if ($servicetoeditname == $key[0]) {
  270. print ' (<input style="width: 20%" type="text" name="'.$key[0].'" value="'.$keyforprovider.'" >)';
  271. } elseif ($keyforprovider) {
  272. print ' (<b>'.$keyforprovider.'</b>)';
  273. } else {
  274. print ' (<b>'.$langs->trans("NoName").'</b>)';
  275. }
  276. if (!($servicetoeditname == $key[0])) {
  277. print '<a class="editfielda reposition" href="'.$_SERVER["PHP_SELF"].'?token='.newToken().'&servicetoeditname='.urlencode($key[0]).'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).'</a>';
  278. }
  279. print '</td>';
  280. print '<td>';
  281. if (!empty($supportedoauth2array[$keyforsupportedoauth2array]['urlforcredentials'])) {
  282. print $langs->trans("OAUTH_URL_FOR_CREDENTIAL", $supportedoauth2array[$keyforsupportedoauth2array]['urlforcredentials']);
  283. }
  284. print '</td>';
  285. // Delete
  286. print '<td>';
  287. $label = preg_replace('/_NAME$/', '', $keyforsupportedoauth2array);
  288. print '<a href="'.$_SERVER["PHP_SELF"].'?action=delete&token='.newToken().'&provider='.urlencode($keyforprovider).'&label='.urlencode($label).'">';
  289. print img_picto('', 'delete');
  290. print '</a>';
  291. print '</form>';
  292. print '</td>';
  293. print '</tr>';
  294. if ($supported) {
  295. $redirect_uri = $urlwithroot.'/core/modules/oauth/'.$supportedoauth2array[$keyforsupportedoauth2array]['callbackfile'].'_oauthcallback.php';
  296. print '<tr class="oddeven value">';
  297. print '<td>'.$form->textwithpicto($langs->trans("RedirectURL"), $langs->trans("UseTheFollowingUrlAsRedirectURI")).'</td>';
  298. print '<td><input style="width: 80%" type="text" name="uri'.$keyforsupportedoauth2array.'" id="uri'.$keyforsupportedoauth2array.$keyforprovider.'" value="'.$redirect_uri.'" disabled>';
  299. print ajax_autoselect('uri'.$keyforsupportedoauth2array.$keyforprovider);
  300. print '</td>';
  301. print '<td></td>';
  302. print '</tr>';
  303. if ($keyforsupportedoauth2array == 'OAUTH_OTHER_NAME') {
  304. print '<tr class="oddeven value">';
  305. print '<td>'.$langs->trans("URLOfServiceForAuthorization").'</td>';
  306. print '<td><input style="width: 80%" type="text" name="'.$key[3].'" value="'.getDolGlobalString($key[3]).'" >';
  307. print '</td>';
  308. print '<td></td>';
  309. print '</tr>';
  310. }
  311. } else {
  312. print '<tr class="oddeven value">';
  313. print '<td>'.$langs->trans("UseTheFollowingUrlAsRedirectURI").'</td>';
  314. print '<td>'.$langs->trans("FeatureNotYetSupported").'</td>';
  315. print '</td>';
  316. print '<td></td>';
  317. print '</tr>';
  318. }
  319. // Api Id
  320. print '<tr class="oddeven value">';
  321. print '<td><label for="'.$key[1].'">'.$langs->trans("OAUTH_ID").'</label></td>';
  322. print '<td><input type="text" size="100" id="'.$key[1].'" name="'.$key[1].'" value="'.getDolGlobalString($key[1]).'">';
  323. print '</td>';
  324. print '<td></td>';
  325. print '</tr>';
  326. // Api Secret
  327. print '<tr class="oddeven value">';
  328. print '<td><label for="'.$key[2].'">'.$langs->trans("OAUTH_SECRET").'</label></td>';
  329. print '<td><input type="password" size="100" id="'.$key[2].'" name="'.$key[2].'" value="'.getDolGlobalString($key[2]).'">';
  330. print '</td>';
  331. print '<td></td>';
  332. print '</tr>';
  333. // Tenant
  334. if ($keybeforeprovider == 'MICROSOFT') {
  335. print '<tr class="oddeven value">';
  336. print '<td><label for="'.$key[2].'">'.$langs->trans("OAUTH_TENANT").'</label></td>';
  337. 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').'">';
  338. print '</td>';
  339. print '<td></td>';
  340. print '</tr>';
  341. }
  342. // TODO Move this into token generation ?
  343. if ($supported) {
  344. if ($keyforsupportedoauth2array == 'OAUTH_OTHER_NAME') {
  345. print '<tr class="oddeven value">';
  346. print '<td>'.$langs->trans("Scopes").'</td>';
  347. print '<td>';
  348. print '<input style="width: 80%" type"text" name="'.$key[4].'" value="'.getDolGlobalString($key[4]).'" >';
  349. print '</td>';
  350. print '<td></td>';
  351. print '</tr>';
  352. } else {
  353. $availablescopes = array_flip(explode(',', $supportedoauth2array[$keyforsupportedoauth2array]['availablescopes']));
  354. $currentscopes = explode(',', getDolGlobalString($key[4]));
  355. $scopestodispay = array();
  356. foreach ($availablescopes as $keyscope => $valscope) {
  357. if (in_array($keyscope, $currentscopes)) {
  358. $scopestodispay[$keyscope] = 1;
  359. } else {
  360. $scopestodispay[$keyscope] = 0;
  361. }
  362. }
  363. // Api Scope
  364. print '<tr class="oddeven value">';
  365. print '<td>'.$langs->trans("Scopes").'</td>';
  366. print '<td>';
  367. foreach ($scopestodispay as $scope => $val) {
  368. print '<input type="checkbox" id="'.$keyforprovider.$scope.'" name="'.$key[4].'[]" value="'.$scope.'"'.($val ? ' checked' : '').'>';
  369. print '<label style="margin-right: 10px" for="'.$keyforprovider.$scope.'">'.$scope.'</label>';
  370. }
  371. print '</td>';
  372. print '<td></td>';
  373. print '</tr>';
  374. }
  375. } else {
  376. print '<tr class="oddeven value">';
  377. print '<td>'.$langs->trans("UseTheFollowingUrlAsRedirectURI").'</td>';
  378. print '<td>'.$langs->trans("FeatureNotYetSupported").'</td>';
  379. print '</td>';
  380. print '<td></td>';
  381. print '</tr>';
  382. }
  383. print '</table>'."\n";
  384. print '<br>';
  385. }
  386. print '</div>';
  387. print $form->buttonsSaveCancel("Save", '');
  388. print '</form>';
  389. }
  390. // End of page
  391. llxFooter();
  392. $db->close();