oauth.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  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 is defined into oauth.lib.php
  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. foreach ($list as $key) {
  196. $supported = 0;
  197. $keyforsupportedoauth2array = $key[0];
  198. if (in_array($keyforsupportedoauth2array, array_keys($supportedoauth2array))) {
  199. $supported = 1;
  200. }
  201. if (!$supported) {
  202. continue; // show only supported
  203. }
  204. $i++;
  205. print '<option name="'.$keyforsupportedoauth2array.'" value="'.str_replace('_NAME', '', $keyforsupportedoauth2array).'">'.$supportedoauth2array[$keyforsupportedoauth2array]['name'].'</option>'."\n";
  206. }
  207. print '</select>';
  208. print ajax_combobox('provider');
  209. print ' <input type="text" name="label" value="" placeholder="'.$langs->trans("Label").'" pattern="^\S+$" title="'.$langs->trans("SpaceOrSpecialCharAreNotAllowed").'">';
  210. print ' <input type="submit" class="button small" name="add" value="'.$langs->trans("Add").'">';
  211. print '<br>';
  212. print '<br>';
  213. print dol_get_fiche_end();
  214. print '</form>';
  215. $listinsetup = [];
  216. // Define $listinsetup
  217. foreach ($conf->global as $key => $val) {
  218. if (!empty($val) && preg_match('/^OAUTH_.*_ID$/', $key)) {
  219. $provider = preg_replace('/_ID$/', '', $key);
  220. $listinsetup[] = array(
  221. $provider.'_NAME',
  222. $provider.'_ID',
  223. $provider.'_SECRET',
  224. $provider.'_URLAUTHORIZE', // For custom oauth links
  225. $provider.'_SCOPE' // For custom oauth links
  226. );
  227. }
  228. }
  229. if (count($listinsetup) > 0) {
  230. print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST">';
  231. print '<input type="hidden" name="token" value="'.newToken().'">';
  232. print '<input type="hidden" name="action" value="update">';
  233. print '<div class="div-table-responsive-no-min">';
  234. $i = 0;
  235. // $list is defined into oauth.lib.php to the list of supporter OAuth providers.
  236. foreach ($listinsetup as $key) {
  237. $supported = 0;
  238. $keyforsupportedoauth2array = $key[0]; // May be OAUTH_GOOGLE_NAME or OAUTH_GOOGLE_xxx_NAME
  239. $keyforsupportedoauth2array = preg_replace('/^OAUTH_/', '', $keyforsupportedoauth2array);
  240. $keyforsupportedoauth2array = preg_replace('/_NAME$/', '', $keyforsupportedoauth2array);
  241. if (preg_match('/^.*-/', $keyforsupportedoauth2array)) {
  242. $keybeforeprovider = preg_replace('/-.*$/', '', $keyforsupportedoauth2array);
  243. $keyforprovider = preg_replace('/^.*-/', '', $keyforsupportedoauth2array);
  244. } else {
  245. $keybeforeprovider = $keyforsupportedoauth2array;
  246. $keyforprovider = '';
  247. }
  248. $keyforsupportedoauth2array = preg_replace('/-.*$/', '', $keyforsupportedoauth2array);
  249. $keyforsupportedoauth2array = 'OAUTH_'.$keyforsupportedoauth2array.'_NAME';
  250. if (in_array($keyforsupportedoauth2array, array_keys($supportedoauth2array))) {
  251. $supported = 1;
  252. }
  253. if (!$supported) {
  254. continue; // show only supported
  255. }
  256. $i++;
  257. print '<table class="noborder centpercent">';
  258. // OAUTH service name
  259. $label = $langs->trans($keyforsupportedoauth2array);
  260. print '<tr class="liste_titre'.($i > 1 ? ' liste_titre_add' : '').'">';
  261. print '<td class="titlefieldcreate">';
  262. print img_picto('', $supportedoauth2array[$keyforsupportedoauth2array]['picto'], 'class="pictofixedwidth"');
  263. if ($label == $keyforsupportedoauth2array) {
  264. print $supportedoauth2array[$keyforsupportedoauth2array]['name'];
  265. } else {
  266. print $label;
  267. }
  268. if ($servicetoeditname == $key[0]) {
  269. print ' (<input style="width: 20%" type="text" name="'.$key[0].'" value="'.$keyforprovider.'" >)';
  270. } elseif ($keyforprovider) {
  271. print ' (<b>'.$keyforprovider.'</b>)';
  272. } else {
  273. print ' (<b>'.$langs->trans("NoName").'</b>)';
  274. }
  275. if (!($servicetoeditname == $key[0])) {
  276. print '<a class="editfielda reposition" href="'.$_SERVER["PHP_SELF"].'?token='.newToken().'&servicetoeditname='.urlencode($key[0]).'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).'</a>';
  277. }
  278. print '</td>';
  279. print '<td>';
  280. if (!empty($supportedoauth2array[$keyforsupportedoauth2array]['urlforcredentials'])) {
  281. print $langs->trans("OAUTH_URL_FOR_CREDENTIAL", $supportedoauth2array[$keyforsupportedoauth2array]['urlforcredentials']);
  282. }
  283. print '</td>';
  284. // Delete
  285. print '<td>';
  286. $label = preg_replace('/_NAME$/', '', $keyforsupportedoauth2array);
  287. print '<a href="'.$_SERVER["PHP_SELF"].'?action=delete&token='.newToken().'&provider='.urlencode($keyforprovider).'&label='.urlencode($label).'">';
  288. print img_picto('', 'delete');
  289. print '</a>';
  290. print '</form>';
  291. print '</td>';
  292. print '</tr>';
  293. if ($supported) {
  294. $redirect_uri = $urlwithroot.'/core/modules/oauth/'.$supportedoauth2array[$keyforsupportedoauth2array]['callbackfile'].'_oauthcallback.php';
  295. print '<tr class="oddeven value">';
  296. print '<td>'.$langs->trans("UseTheFollowingUrlAsRedirectURI").'</td>';
  297. print '<td><input style="width: 80%" type="text" name="uri'.$keyforsupportedoauth2array.'" id="uri'.$keyforsupportedoauth2array.$keyforprovider.'" value="'.$redirect_uri.'" disabled>';
  298. print ajax_autoselect('uri'.$keyforsupportedoauth2array.$keyforprovider);
  299. print '</td>';
  300. print '<td></td>';
  301. print '</tr>';
  302. if ($keyforsupportedoauth2array == 'OAUTH_OTHER_NAME') {
  303. print '<tr class="oddeven value">';
  304. print '<td>'.$langs->trans("URLOfServiceForAuthorization").'</td>';
  305. print '<td><input style="width: 80%" type="text" name="'.$key[3].'" value="'.getDolGlobalString($key[3]).'" >';
  306. print '</td>';
  307. print '<td></td>';
  308. print '</tr>';
  309. }
  310. } else {
  311. print '<tr class="oddeven value">';
  312. print '<td>'.$langs->trans("UseTheFollowingUrlAsRedirectURI").'</td>';
  313. print '<td>'.$langs->trans("FeatureNotYetSupported").'</td>';
  314. print '</td>';
  315. print '<td></td>';
  316. print '</tr>';
  317. }
  318. // Api Id
  319. print '<tr class="oddeven value">';
  320. print '<td><label for="'.$key[1].'">'.$langs->trans("OAUTH_ID").'</label></td>';
  321. print '<td><input type="text" size="100" id="'.$key[1].'" name="'.$key[1].'" value="'.getDolGlobalString($key[1]).'">';
  322. print '</td>';
  323. print '<td></td>';
  324. print '</tr>';
  325. // Api Secret
  326. print '<tr class="oddeven value">';
  327. print '<td><label for="'.$key[2].'">'.$langs->trans("OAUTH_SECRET").'</label></td>';
  328. print '<td><input type="password" size="100" id="'.$key[2].'" name="'.$key[2].'" value="'.getDolGlobalString($key[2]).'">';
  329. print '</td>';
  330. print '<td></td>';
  331. print '</tr>';
  332. // Tenant
  333. if ($keybeforeprovider == 'MICROSOFT') {
  334. print '<tr class="oddeven value">';
  335. print '<td><label for="'.$key[2].'">'.$langs->trans("OAUTH_TENANT").'</label></td>';
  336. 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').'">';
  337. print '</td>';
  338. print '<td></td>';
  339. print '</tr>';
  340. }
  341. // TODO Move this into token generation ?
  342. if ($supported) {
  343. if ($keyforsupportedoauth2array == 'OAUTH_OTHER_NAME') {
  344. print '<tr class="oddeven value">';
  345. print '<td>'.$langs->trans("Scopes").'</td>';
  346. print '<td>';
  347. print '<input style="width: 80%" type"text" name="'.$key[4].'" value="'.getDolGlobalString($key[4]).'" >';
  348. print '</td>';
  349. print '<td></td>';
  350. print '</tr>';
  351. } else {
  352. $availablescopes = array_flip(explode(',', $supportedoauth2array[$keyforsupportedoauth2array]['availablescopes']));
  353. $currentscopes = explode(',', getDolGlobalString($key[4]));
  354. $scopestodispay = array();
  355. foreach ($availablescopes as $keyscope => $valscope) {
  356. if (in_array($keyscope, $currentscopes)) {
  357. $scopestodispay[$keyscope] = 1;
  358. } else {
  359. $scopestodispay[$keyscope] = 0;
  360. }
  361. }
  362. // Api Scope
  363. print '<tr class="oddeven value">';
  364. print '<td>'.$langs->trans("Scopes").'</td>';
  365. print '<td>';
  366. foreach ($scopestodispay as $scope => $val) {
  367. print '<input type="checkbox" id="'.$keyforprovider.$scope.'" name="'.$key[4].'[]" value="'.$scope.'"'.($val ? ' checked' : '').'>';
  368. print '<label style="margin-right: 10px" for="'.$keyforprovider.$scope.'">'.$scope.'</label>';
  369. }
  370. print '</td>';
  371. print '<td></td>';
  372. print '</tr>';
  373. }
  374. } else {
  375. print '<tr class="oddeven value">';
  376. print '<td>'.$langs->trans("UseTheFollowingUrlAsRedirectURI").'</td>';
  377. print '<td>'.$langs->trans("FeatureNotYetSupported").'</td>';
  378. print '</td>';
  379. print '<td></td>';
  380. print '</tr>';
  381. }
  382. print '</table>'."\n";
  383. print '<br>';
  384. }
  385. print '</div>';
  386. print $form->buttonsSaveCancel("Save", '');
  387. print '</form>';
  388. }
  389. // End of page
  390. llxFooter();
  391. $db->close();