index.js 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186
  1. import { ErrorV3 } from '@growi/core/dist/models';
  2. import xss from 'xss';
  3. import { SupportedAction } from '~/interfaces/activity';
  4. import { PageDeleteConfigValue } from '~/interfaces/page-delete-config';
  5. import { generateAddActivityMiddleware } from '~/server/middlewares/add-activity';
  6. import { apiV3FormValidator } from '~/server/middlewares/apiv3-form-validator';
  7. import ShareLink from '~/server/models/share-link';
  8. import { configManager } from '~/server/service/config-manager';
  9. import loggerFactory from '~/utils/logger';
  10. import { validateDeleteConfigs, prepareDeleteConfigValuesForCalc } from '~/utils/page-delete-config';
  11. import { checkSetupStrategiesHasAdmin } from './checkSetupStrategiesHasAdmin';
  12. const logger = loggerFactory('growi:routes:apiv3:security-setting');
  13. const express = require('express');
  14. const router = express.Router();
  15. const { body } = require('express-validator');
  16. const validator = {
  17. generalSetting: [
  18. body('sessionMaxAge').optional({ checkFalsy: true }).trim().isInt(),
  19. body('restrictGuestMode').if(value => value != null).isString().isIn([
  20. 'Deny', 'Readonly',
  21. ]),
  22. body('pageCompleteDeletionAuthority').if(value => value != null).isString().isIn(Object.values(PageDeleteConfigValue)),
  23. body('hideRestrictedByOwner').if(value => value != null).isBoolean(),
  24. body('hideRestrictedByGroup').if(value => value != null).isBoolean(),
  25. body('isUsersHomepageDeletionEnabled').if(value => value != null).isBoolean(),
  26. body('isForceDeleteUserHomepageOnUserDeletion').if(value => value != null).isBoolean(),
  27. ],
  28. shareLinkSetting: [
  29. body('disableLinkSharing').if(value => value != null).isBoolean(),
  30. ],
  31. authenticationSetting: [
  32. body('isEnabled').if(value => value != null).isBoolean(),
  33. body('authId').isString().isIn([
  34. 'local', 'ldap', 'saml', 'oidc', 'google', 'github',
  35. ]),
  36. ],
  37. localSetting: [
  38. body('registrationMode').isString().isIn([
  39. 'Open', 'Restricted', 'Closed',
  40. ]),
  41. body('registrationWhitelist').if(value => value != null).isArray().customSanitizer((value, { req }) => {
  42. return value.filter(email => email !== '');
  43. }),
  44. ],
  45. ldapAuth: [
  46. body('serverUrl').if(value => value != null).isString(),
  47. body('isUserBind').if(value => value != null).isBoolean(),
  48. body('ldapBindDN').if(value => value != null).isString(),
  49. body('ldapBindDNPassword').if(value => value != null).isString(),
  50. body('ldapSearchFilter').if(value => value != null).isString(),
  51. body('ldapAttrMapUsername').if(value => value != null).isString(),
  52. body('isSameUsernameTreatedAsIdenticalUser').if(value => value != null).isBoolean(),
  53. body('ldapAttrMapMail').if(value => value != null).isString(),
  54. body('ldapAttrMapName').if(value => value != null).isString(),
  55. body('ldapGroupSearchBase').if(value => value != null).isString(),
  56. body('ldapGroupSearchFilter').if(value => value != null).isString(),
  57. body('ldapGroupDnProperty').if(value => value != null).isString(),
  58. ],
  59. samlAuth: [
  60. body('entryPoint').if(value => value != null).isString(),
  61. body('issuer').if(value => value != null).isString(),
  62. body('cert').if(value => value != null).isString(),
  63. body('attrMapId').if(value => value != null).isString(),
  64. body('attrMapUsername').if(value => value != null).isString(),
  65. body('attrMapMail').if(value => value != null).isString(),
  66. body('attrMapFirstName').if(value => value != null).isString(),
  67. body('attrMapLastName').if(value => value != null).isString(),
  68. body('isSameUsernameTreatedAsIdenticalUser').if(value => value != null).isBoolean(),
  69. body('isSameEmailTreatedAsIdenticalUser').if(value => value != null).isBoolean(),
  70. body('ABLCRule').if(value => value != null).isString(),
  71. ],
  72. oidcAuth: [
  73. body('oidcProviderName').if(value => value != null).isString(),
  74. body('oidcIssuerHost').if(value => value != null).isString(),
  75. body('oidcAuthorizationEndpoint').if(value => value != null).isString(),
  76. body('oidcTokenEndpoint').if(value => value != null).isString(),
  77. body('oidcRevocationEndpoint').if(value => value != null).isString(),
  78. body('oidcIntrospectionEndpoint').if(value => value != null).isString(),
  79. body('oidcUserInfoEndpoint').if(value => value != null).isString(),
  80. body('oidcEndSessionEndpoint').if(value => value != null).isString(),
  81. body('oidcRegistrationEndpoint').if(value => value != null).isString(),
  82. body('oidcJWKSUri').if(value => value != null).isString(),
  83. body('oidcClientId').if(value => value != null).isString(),
  84. body('oidcClientSecret').if(value => value != null).isString(),
  85. body('oidcAttrMapId').if(value => value != null).isString(),
  86. body('oidcAttrMapUserName').if(value => value != null).isString(),
  87. body('oidcAttrMapEmail').if(value => value != null).isString(),
  88. body('isSameUsernameTreatedAsIdenticalUser').if(value => value != null).isBoolean(),
  89. body('isSameEmailTreatedAsIdenticalUser').if(value => value != null).isBoolean(),
  90. ],
  91. googleOAuth: [
  92. body('googleClientId').if(value => value != null).isString(),
  93. body('googleClientSecret').if(value => value != null).isString(),
  94. body('isSameUsernameTreatedAsIdenticalUser').if(value => value != null).isBoolean(),
  95. ],
  96. githubOAuth: [
  97. body('githubClientId').if(value => value != null).isString(),
  98. body('githubClientSecret').if(value => value != null).isString(),
  99. body('isSameUsernameTreatedAsIdenticalUser').if(value => value != null).isBoolean(),
  100. ],
  101. };
  102. /**
  103. * @swagger
  104. * tags:
  105. * name: SecuritySetting
  106. */
  107. /**
  108. * @swagger
  109. *
  110. * components:
  111. * schemas:
  112. * GeneralSetting:
  113. * type: object
  114. * properties:
  115. * restrictGuestMode:
  116. * type: string
  117. * description: type of restrictGuestMode
  118. * pageCompleteDeletionAuthority:
  119. * type: string
  120. * description: type of pageDeletionAuthority
  121. * hideRestrictedByOwner:
  122. * type: boolean
  123. * description: enable hide by owner
  124. * hideRestrictedByGroup:
  125. * type: boolean
  126. * description: enable hide by group
  127. * ShareLinkSetting:
  128. * type: object
  129. * properties:
  130. * disableLinkSharing:
  131. * type: boolean
  132. * description: disable link sharing
  133. * LocalSetting:
  134. * type: object
  135. * properties:
  136. * isLocalEnabled:
  137. * type: boolean
  138. * description: local setting mode
  139. * registrationMode:
  140. * type: string
  141. * description: type of registrationMode
  142. * registrationWhitelist:
  143. * type: array
  144. * description: array of regsitrationList
  145. * items:
  146. * type: string
  147. * description: registration whitelist
  148. * LdapAuthSetting:
  149. * type: object
  150. * properties:
  151. * serverUrl:
  152. * type: string
  153. * description: server url for ldap
  154. * isUserBind:
  155. * type: boolean
  156. * description: enable user bind
  157. * ldapBindDN:
  158. * type: string
  159. * description: the query used to bind with the directory service
  160. * ldapBindDNPassword:
  161. * type: string
  162. * description: the password that is entered in the login page will be used to bind
  163. * ldapSearchFilter:
  164. * type: string
  165. * description: the query used to locate the authenticated user
  166. * ldapAttrMapUsername:
  167. * type: string
  168. * description: specification of mappings for username when creating new users
  169. * isSameUsernameTreatedAsIdenticalUser:
  170. * type: boolean
  171. * description: local account automatically linked the user name matched
  172. * ldapAttrMapMail:
  173. * type: string
  174. * description: specification of mappings for mail address when creating new users
  175. * ldapAttrMapName:
  176. * type: string
  177. * description: Specification of mappings for full name address when creating new users
  178. * ldapGroupSearchBase:
  179. * type: string
  180. * description: the base DN from which to search for groups.
  181. * ldapGroupSearchFilter:
  182. * type: string
  183. * description: the query used to filter for groups
  184. * ldapGroupDnProperty:
  185. * type: string
  186. * description: The property of user object to use in dn interpolation of Group Search Filter
  187. * SamlAuthSetting:
  188. * type: object
  189. * properties:
  190. * samlEntryPoint:
  191. * type: string
  192. * description: entry point for saml
  193. * samlIssuer:
  194. * type: string
  195. * description: issuer for saml
  196. * samlCert:
  197. * type: string
  198. * description: certificate for saml
  199. * samlAttrMapId:
  200. * type: string
  201. * description: attribute mapping id for saml
  202. * samlAttrMapUserName:
  203. * type: string
  204. * description: attribute mapping user name for saml
  205. * samlAttrMapMail:
  206. * type: string
  207. * description: attribute mapping mail for saml
  208. * samlAttrMapFirstName:
  209. * type: string
  210. * description: attribute mapping first name for saml
  211. * samlAttrMapLastName:
  212. * type: string
  213. * description: attribute mapping last name for saml
  214. * isSameUsernameTreatedAsIdenticalUser:
  215. * type: boolean
  216. * description: local account automatically linked the user name matched
  217. * isSameEmailTreatedAsIdenticalUser:
  218. * type: boolean
  219. * description: local account automatically linked the email matched
  220. * samlABLCRule:
  221. * type: string
  222. * description: ABLCRule for saml
  223. * OidcAuthSetting:
  224. * type: object
  225. * properties:
  226. * oidcProviderName:
  227. * type: string
  228. * description: provider name for oidc
  229. * oidcIssuerHost:
  230. * type: string
  231. * description: issuer host for oidc
  232. * oidcAuthorizationEndpoint:
  233. * type: string
  234. * description: authorization endpoint for oidc
  235. * oidcTokenEndpoint:
  236. * type: string
  237. * description: token endpoint for oidc
  238. * oidcRevocationEndpoint:
  239. * type: string
  240. * description: revocation endpoint for oidc
  241. * oidcIntrospectionEndpoint:
  242. * type: string
  243. * description: introspection endpoint for oidc
  244. * oidcUserInfoEndpoint:
  245. * type: string
  246. * description: userinfo endpoint for oidc
  247. * oidcEndSessionEndpoint:
  248. * type: string
  249. * description: end session endpoint for oidc
  250. * oidcRegistrationEndpoint:
  251. * type: string
  252. * description: registration endpoint for oidc
  253. * oidcJWKSUri:
  254. * type: string
  255. * description: JSON Web Key Set URI for oidc
  256. * oidcClientId:
  257. * type: string
  258. * description: client id for oidc
  259. * oidcClientSecret:
  260. * type: string
  261. * description: client secret for oidc
  262. * oidcAttrMapId:
  263. * type: string
  264. * description: attr map id for oidc
  265. * oidcAttrMapUserName:
  266. * type: string
  267. * description: attr map username for oidc
  268. * oidcAttrMapName:
  269. * type: string
  270. * description: attr map name for oidc
  271. * oidcAttrMapMail:
  272. * type: string
  273. * description: attr map mail for oidc
  274. * isSameUsernameTreatedAsIdenticalUser:
  275. * type: boolean
  276. * description: local account automatically linked the user name matched
  277. * isSameEmailTreatedAsIdenticalUser:
  278. * type: boolean
  279. * description: local account automatically linked the email matched
  280. * GitHubOAuthSetting:
  281. * type: object
  282. * properties:
  283. * githubClientId:
  284. * type: string
  285. * description: key of comsumer
  286. * githubClientSecret:
  287. * type: string
  288. * description: password of comsumer
  289. * isSameUsernameTreatedAsIdenticalUser:
  290. * type: boolean
  291. * description: local account automatically linked the email matched
  292. * GoogleOAuthSetting:
  293. * type: object
  294. * properties:
  295. * googleClientId:
  296. * type: string
  297. * description: key of comsumer
  298. * googleClientSecret:
  299. * type: string
  300. * description: password of comsumer
  301. * isSameUsernameTreatedAsIdenticalUser:
  302. * type: boolean
  303. * description: local account automatically linked the email matched
  304. */
  305. module.exports = (crowi) => {
  306. const loginRequiredStrictly = require('~/server/middlewares/login-required')(crowi);
  307. const adminRequired = require('~/server/middlewares/admin-required')(crowi);
  308. const addActivity = generateAddActivityMiddleware(crowi);
  309. const activityEvent = crowi.event('activity');
  310. async function updateAndReloadStrategySettings(authId, params) {
  311. const { passportService } = crowi;
  312. // update config without publishing S2sMessage
  313. await configManager.updateConfigsInTheSameNamespace('crowi', params, true);
  314. await passportService.setupStrategyById(authId);
  315. passportService.publishUpdatedMessage(authId);
  316. }
  317. /**
  318. * @swagger
  319. *
  320. * /_api/v3/security-setting/:
  321. * get:
  322. * tags: [SecuritySetting, apiv3]
  323. * description: Get security paramators
  324. * responses:
  325. * 200:
  326. * description: params of security
  327. * content:
  328. * application/json:
  329. * schema:
  330. * properties:
  331. * securityParams:
  332. * type: object
  333. * description: security params
  334. */
  335. router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => {
  336. const securityParams = {
  337. generalSetting: {
  338. restrictGuestMode: crowi.aclService.getGuestModeValue(),
  339. pageDeletionAuthority: await configManager.getConfig('crowi', 'security:pageDeletionAuthority'),
  340. pageCompleteDeletionAuthority: await configManager.getConfig('crowi', 'security:pageCompleteDeletionAuthority'),
  341. pageRecursiveDeletionAuthority: await configManager.getConfig('crowi', 'security:pageRecursiveDeletionAuthority'),
  342. pageRecursiveCompleteDeletionAuthority: await configManager.getConfig('crowi', 'security:pageRecursiveCompleteDeletionAuthority'),
  343. isAllGroupMembershipRequiredForPageCompleteDeletion:
  344. await configManager.getConfig('crowi', 'security:isAllGroupMembershipRequiredForPageCompleteDeletion'),
  345. hideRestrictedByOwner: await configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByOwner'),
  346. hideRestrictedByGroup: await configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByGroup'),
  347. isUsersHomepageDeletionEnabled: await configManager.getConfig('crowi', 'security:user-homepage-deletion:isEnabled'),
  348. isForceDeleteUserHomepageOnUserDeletion:
  349. await configManager.getConfig('crowi', 'security:user-homepage-deletion:isForceDeleteUserHomepageOnUserDeletion'),
  350. isRomUserAllowedToComment: await configManager.getConfig('crowi', 'security:isRomUserAllowedToComment'),
  351. wikiMode: await configManager.getConfig('crowi', 'security:wikiMode'),
  352. sessionMaxAge: await configManager.getConfig('crowi', 'security:sessionMaxAge'),
  353. },
  354. shareLinkSetting: {
  355. disableLinkSharing: await configManager.getConfig('crowi', 'security:disableLinkSharing'),
  356. },
  357. localSetting: {
  358. useOnlyEnvVarsForSomeOptions: await configManager.getConfig('crowi', 'security:passport-local:useOnlyEnvVarsForSomeOptions'),
  359. registrationMode: await configManager.getConfig('crowi', 'security:registrationMode'),
  360. registrationWhitelist: await configManager.getConfig('crowi', 'security:registrationWhitelist'),
  361. isPasswordResetEnabled: await configManager.getConfig('crowi', 'security:passport-local:isPasswordResetEnabled'),
  362. isEmailAuthenticationEnabled: await configManager.getConfig('crowi', 'security:passport-local:isEmailAuthenticationEnabled'),
  363. },
  364. generalAuth: {
  365. isLocalEnabled: await configManager.getConfig('crowi', 'security:passport-local:isEnabled'),
  366. isLdapEnabled: await configManager.getConfig('crowi', 'security:passport-ldap:isEnabled'),
  367. isSamlEnabled: await configManager.getConfig('crowi', 'security:passport-saml:isEnabled'),
  368. isOidcEnabled: await configManager.getConfig('crowi', 'security:passport-oidc:isEnabled'),
  369. isGoogleEnabled: await configManager.getConfig('crowi', 'security:passport-google:isEnabled'),
  370. isGitHubEnabled: await configManager.getConfig('crowi', 'security:passport-github:isEnabled'),
  371. },
  372. ldapAuth: {
  373. serverUrl: await configManager.getConfig('crowi', 'security:passport-ldap:serverUrl'),
  374. isUserBind: await configManager.getConfig('crowi', 'security:passport-ldap:isUserBind'),
  375. ldapBindDN: await configManager.getConfig('crowi', 'security:passport-ldap:bindDN'),
  376. ldapBindDNPassword: await configManager.getConfig('crowi', 'security:passport-ldap:bindDNPassword'),
  377. ldapSearchFilter: await configManager.getConfig('crowi', 'security:passport-ldap:searchFilter'),
  378. ldapAttrMapUsername: await configManager.getConfig('crowi', 'security:passport-ldap:attrMapUsername'),
  379. isSameUsernameTreatedAsIdenticalUser: await configManager.getConfig('crowi', 'security:passport-ldap:isSameUsernameTreatedAsIdenticalUser'),
  380. ldapAttrMapMail: await configManager.getConfig('crowi', 'security:passport-ldap:attrMapMail'),
  381. ldapAttrMapName: await configManager.getConfig('crowi', 'security:passport-ldap:attrMapName'),
  382. ldapGroupSearchBase: await configManager.getConfig('crowi', 'security:passport-ldap:groupSearchBase'),
  383. ldapGroupSearchFilter: await configManager.getConfig('crowi', 'security:passport-ldap:groupSearchFilter'),
  384. ldapGroupDnProperty: await configManager.getConfig('crowi', 'security:passport-ldap:groupDnProperty'),
  385. },
  386. samlAuth: {
  387. missingMandatoryConfigKeys: await crowi.passportService.getSamlMissingMandatoryConfigKeys(),
  388. useOnlyEnvVarsForSomeOptions: await configManager.getConfigFromEnvVars('crowi', 'security:passport-saml:useOnlyEnvVarsForSomeOptions'),
  389. samlEntryPoint: await configManager.getConfigFromDB('crowi', 'security:passport-saml:entryPoint'),
  390. samlEnvVarEntryPoint: await configManager.getConfigFromEnvVars('crowi', 'security:passport-saml:entryPoint'),
  391. samlIssuer: await configManager.getConfigFromDB('crowi', 'security:passport-saml:issuer'),
  392. samlEnvVarIssuer: await configManager.getConfigFromEnvVars('crowi', 'security:passport-saml:issuer'),
  393. samlCert: await configManager.getConfigFromDB('crowi', 'security:passport-saml:cert'),
  394. samlEnvVarCert: await configManager.getConfigFromEnvVars('crowi', 'security:passport-saml:cert'),
  395. samlAttrMapId: await configManager.getConfigFromDB('crowi', 'security:passport-saml:attrMapId'),
  396. samlEnvVarAttrMapId: await configManager.getConfigFromEnvVars('crowi', 'security:passport-saml:attrMapId'),
  397. samlAttrMapUsername: await configManager.getConfigFromDB('crowi', 'security:passport-saml:attrMapUsername'),
  398. samlEnvVarAttrMapUsername: await configManager.getConfigFromEnvVars('crowi', 'security:passport-saml:attrMapUsername'),
  399. samlAttrMapMail: await configManager.getConfigFromDB('crowi', 'security:passport-saml:attrMapMail'),
  400. samlEnvVarAttrMapMail: await configManager.getConfigFromEnvVars('crowi', 'security:passport-saml:attrMapMail'),
  401. samlAttrMapFirstName: await configManager.getConfigFromDB('crowi', 'security:passport-saml:attrMapFirstName'),
  402. samlEnvVarAttrMapFirstName: await configManager.getConfigFromEnvVars('crowi', 'security:passport-saml:attrMapFirstName'),
  403. samlAttrMapLastName: await configManager.getConfigFromDB('crowi', 'security:passport-saml:attrMapLastName'),
  404. samlEnvVarAttrMapLastName: await configManager.getConfigFromEnvVars('crowi', 'security:passport-saml:attrMapLastName'),
  405. isSameUsernameTreatedAsIdenticalUser: await configManager.getConfig('crowi', 'security:passport-saml:isSameUsernameTreatedAsIdenticalUser'),
  406. isSameEmailTreatedAsIdenticalUser: await configManager.getConfig('crowi', 'security:passport-saml:isSameEmailTreatedAsIdenticalUser'),
  407. samlABLCRule: await configManager.getConfigFromDB('crowi', 'security:passport-saml:ABLCRule'),
  408. samlEnvVarABLCRule: await configManager.getConfigFromEnvVars('crowi', 'security:passport-saml:ABLCRule'),
  409. },
  410. oidcAuth: {
  411. oidcProviderName: await configManager.getConfig('crowi', 'security:passport-oidc:providerName'),
  412. oidcIssuerHost: await configManager.getConfig('crowi', 'security:passport-oidc:issuerHost'),
  413. oidcAuthorizationEndpoint: await configManager.getConfig('crowi', 'security:passport-oidc:authorizationEndpoint'),
  414. oidcTokenEndpoint: await configManager.getConfig('crowi', 'security:passport-oidc:tokenEndpoint'),
  415. oidcRevocationEndpoint: await configManager.getConfig('crowi', 'security:passport-oidc:revocationEndpoint'),
  416. oidcIntrospectionEndpoint: await configManager.getConfig('crowi', 'security:passport-oidc:introspectionEndpoint'),
  417. oidcUserInfoEndpoint: await configManager.getConfig('crowi', 'security:passport-oidc:userInfoEndpoint'),
  418. oidcEndSessionEndpoint: await configManager.getConfig('crowi', 'security:passport-oidc:endSessionEndpoint'),
  419. oidcRegistrationEndpoint: await configManager.getConfig('crowi', 'security:passport-oidc:registrationEndpoint'),
  420. oidcJWKSUri: await configManager.getConfig('crowi', 'security:passport-oidc:jwksUri'),
  421. oidcClientId: await configManager.getConfig('crowi', 'security:passport-oidc:clientId'),
  422. oidcClientSecret: await configManager.getConfig('crowi', 'security:passport-oidc:clientSecret'),
  423. oidcAttrMapId: await configManager.getConfig('crowi', 'security:passport-oidc:attrMapId'),
  424. oidcAttrMapUserName: await configManager.getConfig('crowi', 'security:passport-oidc:attrMapUserName'),
  425. oidcAttrMapName: await configManager.getConfig('crowi', 'security:passport-oidc:attrMapName'),
  426. oidcAttrMapEmail: await configManager.getConfig('crowi', 'security:passport-oidc:attrMapMail'),
  427. isSameUsernameTreatedAsIdenticalUser: await configManager.getConfig('crowi', 'security:passport-oidc:isSameUsernameTreatedAsIdenticalUser'),
  428. isSameEmailTreatedAsIdenticalUser: await configManager.getConfig('crowi', 'security:passport-oidc:isSameEmailTreatedAsIdenticalUser'),
  429. },
  430. googleOAuth: {
  431. googleClientId: await configManager.getConfig('crowi', 'security:passport-google:clientId'),
  432. googleClientSecret: await configManager.getConfig('crowi', 'security:passport-google:clientSecret'),
  433. isSameEmailTreatedAsIdenticalUser: await configManager.getConfig('crowi', 'security:passport-google:isSameEmailTreatedAsIdenticalUser'),
  434. },
  435. githubOAuth: {
  436. githubClientId: await configManager.getConfig('crowi', 'security:passport-github:clientId'),
  437. githubClientSecret: await configManager.getConfig('crowi', 'security:passport-github:clientSecret'),
  438. isSameUsernameTreatedAsIdenticalUser: await configManager.getConfig('crowi', 'security:passport-github:isSameUsernameTreatedAsIdenticalUser'),
  439. },
  440. };
  441. return res.apiv3({ securityParams });
  442. });
  443. /**
  444. * @swagger
  445. *
  446. * /_api/v3/security-setting/authentication/enabled:
  447. * put:
  448. * tags: [SecuritySetting, apiv3]
  449. * description: Update authentication isEnabled
  450. * requestBody:
  451. * required: true
  452. * content:
  453. * application/json:
  454. * schema:
  455. * type: object
  456. * properties:
  457. * isEnabled:
  458. * type: boolean
  459. * target:
  460. * type: string
  461. * responses:
  462. * 200:
  463. * description: Succeeded to enable authentication
  464. * content:
  465. * application/json:
  466. * schema:
  467. * type: object
  468. * description: updated param
  469. */
  470. // eslint-disable-next-line max-len
  471. router.put('/authentication/enabled', loginRequiredStrictly, adminRequired, addActivity, validator.authenticationSetting, apiV3FormValidator, async(req, res) => {
  472. const { isEnabled, authId } = req.body;
  473. let setupStrategies = await crowi.passportService.getSetupStrategies();
  474. const parameters = {};
  475. // Reflect request param
  476. setupStrategies = setupStrategies.filter(strategy => strategy !== authId);
  477. if (setupStrategies.length === 0) {
  478. return res.apiv3Err(new ErrorV3('Can not turn everything off'), 405);
  479. }
  480. if (!isEnabled) {
  481. const isSetupStrategiesHasAdmin = await checkSetupStrategiesHasAdmin(setupStrategies);
  482. // Return an error when disabling an strategy when there are no setup strategies with admin-enabled login
  483. if (!isSetupStrategiesHasAdmin) {
  484. return res.apiv3Err(new ErrorV3('Must have admin enabled authentication method'), 405);
  485. }
  486. }
  487. const enableParams = { [`security:passport-${authId}:isEnabled`]: isEnabled };
  488. try {
  489. await updateAndReloadStrategySettings(authId, enableParams);
  490. const responseParams = {
  491. [`security:passport-${authId}:isEnabled`]: await configManager.getConfig('crowi', `security:passport-${authId}:isEnabled`),
  492. };
  493. switch (authId) {
  494. case 'local':
  495. if (isEnabled) {
  496. parameters.action = SupportedAction.ACTION_ADMIN_AUTH_ID_PASS_ENABLED;
  497. break;
  498. }
  499. parameters.action = SupportedAction.ACTION_ADMIN_AUTH_ID_PASS_DISABLED;
  500. break;
  501. case 'ldap':
  502. if (isEnabled) {
  503. parameters.action = SupportedAction.ACTION_ADMIN_AUTH_LDAP_ENABLED;
  504. break;
  505. }
  506. parameters.action = SupportedAction.ACTION_ADMIN_AUTH_LDAP_DISABLED;
  507. break;
  508. case 'saml':
  509. if (isEnabled) {
  510. parameters.action = SupportedAction.ACTION_ADMIN_AUTH_SAML_ENABLED;
  511. break;
  512. }
  513. parameters.action = SupportedAction.ACTION_ADMIN_AUTH_SAML_DISABLED;
  514. break;
  515. case 'oidc':
  516. if (isEnabled) {
  517. parameters.action = SupportedAction.ACTION_ADMIN_AUTH_OIDC_ENABLED;
  518. break;
  519. }
  520. parameters.action = SupportedAction.ACTION_ADMIN_AUTH_OIDC_DISABLED;
  521. break;
  522. case 'google':
  523. if (isEnabled) {
  524. parameters.action = SupportedAction.ACTION_ADMIN_AUTH_GOOGLE_ENABLED;
  525. break;
  526. }
  527. parameters.action = SupportedAction.ACTION_ADMIN_AUTH_GOOGLE_DISABLED;
  528. break;
  529. case 'github':
  530. if (isEnabled) {
  531. parameters.action = SupportedAction.ACTION_ADMIN_AUTH_GITHUB_ENABLED;
  532. break;
  533. }
  534. parameters.action = SupportedAction.ACTION_ADMIN_AUTH_GITHUB_DISABLED;
  535. break;
  536. }
  537. activityEvent.emit('update', res.locals.activity._id, parameters);
  538. return res.apiv3({ responseParams });
  539. }
  540. catch (err) {
  541. const msg = 'Error occurred in updating enable setting';
  542. logger.error('Error', err);
  543. return res.apiv3Err(new ErrorV3(msg, 'update-enable-setting failed'));
  544. }
  545. });
  546. /**
  547. * @swagger
  548. *
  549. * /_api/v3/security-setting/authentication:
  550. * get:
  551. * tags: [SecuritySetting, apiv3]
  552. * description: Get setup strategies for passport
  553. * responses:
  554. * 200:
  555. * description: params of setup strategies
  556. * content:
  557. * application/json:
  558. * schema:
  559. * properties:
  560. * setupStrategies:
  561. * type: array
  562. * description: setup strategies list
  563. * items:
  564. * type: string
  565. * description: setup strategie
  566. * example: ["local"]
  567. */
  568. router.get('/authentication/', loginRequiredStrictly, adminRequired, async(req, res) => {
  569. const setupStrategies = await crowi.passportService.getSetupStrategies();
  570. return res.apiv3({ setupStrategies });
  571. });
  572. /**
  573. * @swagger
  574. *
  575. * /_api/v3/security-setting/general-setting:
  576. * put:
  577. * tags: [SecuritySetting, apiv3]
  578. * description: Update GeneralSetting
  579. * requestBody:
  580. * required: true
  581. * content:
  582. * application/json:
  583. * schema:
  584. * $ref: '#/components/schemas/GeneralSetting'
  585. * responses:
  586. * 200:
  587. * description: Succeeded to update general Setting
  588. * content:
  589. * application/json:
  590. * schema:
  591. * $ref: '#/components/schemas/GeneralSetting'
  592. */
  593. router.put('/general-setting', loginRequiredStrictly, adminRequired, addActivity, validator.generalSetting, apiV3FormValidator, async(req, res) => {
  594. const updateData = {
  595. 'security:sessionMaxAge': parseInt(req.body.sessionMaxAge),
  596. 'security:restrictGuestMode': req.body.restrictGuestMode,
  597. 'security:pageDeletionAuthority': req.body.pageDeletionAuthority,
  598. 'security:pageRecursiveDeletionAuthority': req.body.pageRecursiveDeletionAuthority,
  599. 'security:pageCompleteDeletionAuthority': req.body.pageCompleteDeletionAuthority,
  600. 'security:pageRecursiveCompleteDeletionAuthority': req.body.pageRecursiveCompleteDeletionAuthority,
  601. 'security:isAllGroupMembershipRequiredForPageCompleteDeletion': req.body.isAllGroupMembershipRequiredForPageCompleteDeletion,
  602. 'security:list-policy:hideRestrictedByOwner': req.body.hideRestrictedByOwner,
  603. 'security:list-policy:hideRestrictedByGroup': req.body.hideRestrictedByGroup,
  604. 'security:user-homepage-deletion:isEnabled': req.body.isUsersHomepageDeletionEnabled,
  605. // Validate user-homepage-deletion config
  606. 'security:user-homepage-deletion:isForceDeleteUserHomepageOnUserDeletion': req.body.isUsersHomepageDeletionEnabled
  607. ? req.body.isForceDeleteUserHomepageOnUserDeletion
  608. : false,
  609. 'security:isRomUserAllowedToComment': req.body.isRomUserAllowedToComment,
  610. };
  611. // Validate delete config
  612. const [singleAuthority1, recursiveAuthority1] = prepareDeleteConfigValuesForCalc(req.body.pageDeletionAuthority, req.body.pageRecursiveDeletionAuthority);
  613. // eslint-disable-next-line max-len
  614. const [singleAuthority2, recursiveAuthority2] = prepareDeleteConfigValuesForCalc(req.body.pageCompleteDeletionAuthority, req.body.pageRecursiveCompleteDeletionAuthority);
  615. const isDeleteConfigNormalized = validateDeleteConfigs(singleAuthority1, recursiveAuthority1)
  616. && validateDeleteConfigs(singleAuthority2, recursiveAuthority2);
  617. if (!isDeleteConfigNormalized) {
  618. return res.apiv3Err(new ErrorV3('Delete config values are not correct.', 'delete_config_not_normalized'));
  619. }
  620. const wikiMode = await configManager.getConfig('crowi', 'security:wikiMode');
  621. if (wikiMode === 'private' || wikiMode === 'public') {
  622. logger.debug('security:restrictGuestMode will not be changed because wiki mode is forced to set');
  623. delete updateData['security:restrictGuestMode'];
  624. }
  625. try {
  626. await configManager.updateConfigsInTheSameNamespace('crowi', updateData);
  627. const securitySettingParams = {
  628. sessionMaxAge: await configManager.getConfig('crowi', 'security:sessionMaxAge'),
  629. restrictGuestMode: await configManager.getConfig('crowi', 'security:restrictGuestMode'),
  630. pageDeletionAuthority: await configManager.getConfig('crowi', 'security:pageDeletionAuthority'),
  631. pageCompleteDeletionAuthority: await configManager.getConfig('crowi', 'security:pageCompleteDeletionAuthority'),
  632. pageRecursiveDeletionAuthority: await configManager.getConfig('crowi', 'security:pageRecursiveDeletionAuthority'),
  633. pageRecursiveCompleteDeletionAuthority: await configManager.getConfig('crowi', 'security:pageRecursiveCompleteDeletionAuthority'),
  634. isAllGroupMembershipRequiredForPageCompleteDeletion:
  635. await configManager.getConfig('crowi', 'security:isAllGroupMembershipRequiredForPageCompleteDeletion'),
  636. hideRestrictedByOwner: await configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByOwner'),
  637. hideRestrictedByGroup: await configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByGroup'),
  638. isUsersHomepageDeletionEnabled: await configManager.getConfig('crowi', 'security:user-homepage-deletion:isEnabled'),
  639. isForceDeleteUserHomepageOnUserDeletion:
  640. await configManager.getConfig('crowi', 'security:user-homepage-deletion:isForceDeleteUserHomepageOnUserDeletion'),
  641. isRomUserAllowedToComment: await configManager.getConfig('crowi', 'security:isRomUserAllowedToComment'),
  642. };
  643. const parameters = { action: SupportedAction.ACTION_ADMIN_SECURITY_SETTINGS_UPDATE };
  644. activityEvent.emit('update', res.locals.activity._id, parameters);
  645. return res.apiv3({ securitySettingParams });
  646. }
  647. catch (err) {
  648. const msg = 'Error occurred in updating security setting';
  649. logger.error('Error', err);
  650. return res.apiv3Err(new ErrorV3(msg, 'update-secuirty-setting failed'));
  651. }
  652. });
  653. /**
  654. * @swagger
  655. *
  656. * /_api/v3/security-setting/share-link-setting:
  657. * put:
  658. * tags: [SecuritySetting, apiv3]
  659. * description: Update ShareLink Setting
  660. * requestBody:
  661. * required: true
  662. * content:
  663. * application/json:
  664. * schema:
  665. * $ref: '#/components/schemas/ShareLinkSetting'
  666. * responses:
  667. * 200:
  668. * description: Succeeded to update ShareLink Setting
  669. * content:
  670. * application/json:
  671. * schema:
  672. * $ref: '#/components/schemas/ShareLinkSetting'
  673. */
  674. router.put('/share-link-setting', loginRequiredStrictly, adminRequired, addActivity, validator.generalSetting, apiV3FormValidator, async(req, res) => {
  675. const updateData = {
  676. 'security:disableLinkSharing': req.body.disableLinkSharing,
  677. };
  678. try {
  679. await configManager.updateConfigsInTheSameNamespace('crowi', updateData);
  680. const securitySettingParams = {
  681. disableLinkSharing: configManager.getConfig('crowi', 'security:disableLinkSharing'),
  682. };
  683. // eslint-disable-next-line max-len
  684. const parameters = { action: updateData['security:disableLinkSharing'] ? SupportedAction.ACTION_ADMIN_REJECT_SHARE_LINK : SupportedAction.ACTION_ADMIN_PERMIT_SHARE_LINK };
  685. activityEvent.emit('update', res.locals.activity._id, parameters);
  686. return res.apiv3({ securitySettingParams });
  687. }
  688. catch (err) {
  689. const msg = 'Error occurred in updating security setting';
  690. logger.error('Error', err);
  691. return res.apiv3Err(new ErrorV3(msg, 'update-secuirty-setting failed'));
  692. }
  693. });
  694. /**
  695. * @swagger
  696. *
  697. * /_api/v3/security-setting/all-share-links:
  698. * get:
  699. * tags: [ShareLinkSettings, apiv3]
  700. * description: Get All ShareLinks at Share Link Setting
  701. * responses:
  702. * 200:
  703. * description: all share links
  704. * content:
  705. * application/json:
  706. * schema:
  707. * properties:
  708. * securityParams:
  709. * type: object
  710. * description: suceed to get all share links
  711. */
  712. router.get('/all-share-links/', loginRequiredStrictly, adminRequired, async(req, res) => {
  713. const page = parseInt(req.query.page) || 1;
  714. const limit = 10;
  715. const linkQuery = {};
  716. try {
  717. const paginateResult = await ShareLink.paginate(
  718. linkQuery,
  719. {
  720. page,
  721. limit,
  722. populate: {
  723. path: 'relatedPage',
  724. select: 'path',
  725. },
  726. },
  727. );
  728. return res.apiv3({ paginateResult });
  729. }
  730. catch (err) {
  731. const msg = 'Error occured in get share link';
  732. logger.error('Error', err);
  733. return res.apiv3Err(new ErrorV3(msg, 'get-all-share-links-failed'));
  734. }
  735. });
  736. /**
  737. * @swagger
  738. *
  739. * /_api/v3/security-setting/all-share-links:
  740. * delete:
  741. * tags: [ShareLinkSettings, apiv3]
  742. * description: Delete All ShareLinks at Share Link Setting
  743. * responses:
  744. * 200:
  745. * description: succeed to delete all share links
  746. */
  747. router.delete('/all-share-links/', loginRequiredStrictly, adminRequired, async(req, res) => {
  748. try {
  749. const removedAct = await ShareLink.remove({});
  750. const removeTotal = await removedAct.n;
  751. return res.apiv3({ removeTotal });
  752. }
  753. catch (err) {
  754. const msg = 'Error occured in delete all share links';
  755. logger.error('Error', err);
  756. return res.apiv3Err(new ErrorV3(msg, 'failed-to-delete-all-share-links'));
  757. }
  758. });
  759. /**
  760. * @swagger
  761. *
  762. * /_api/v3/security-setting/local-setting:
  763. * put:
  764. * tags: [LocalSetting, apiv3]
  765. * description: Update LocalSetting
  766. * requestBody:
  767. * required: true
  768. * content:
  769. * application/json:
  770. * schema:
  771. * $ref: '#/components/schemas/LocalSetting'
  772. * responses:
  773. * 200:
  774. * description: Succeeded to update local Setting
  775. * content:
  776. * application/json:
  777. * schema:
  778. * $ref: '#/components/schemas/LocalSetting'
  779. */
  780. router.put('/local-setting', loginRequiredStrictly, adminRequired, addActivity, validator.localSetting, apiV3FormValidator, async(req, res) => {
  781. try {
  782. const sanitizedRegistrationWhitelist = req.body.registrationWhitelist
  783. .map(line => xss(line, { stripIgnoreTag: true }));
  784. const requestParams = {
  785. 'security:registrationMode': req.body.registrationMode,
  786. 'security:registrationWhitelist': sanitizedRegistrationWhitelist,
  787. 'security:passport-local:isPasswordResetEnabled': req.body.isPasswordResetEnabled,
  788. 'security:passport-local:isEmailAuthenticationEnabled': req.body.isEmailAuthenticationEnabled,
  789. };
  790. await updateAndReloadStrategySettings('local', requestParams);
  791. const localSettingParams = {
  792. registrationMode: await configManager.getConfig('crowi', 'security:registrationMode'),
  793. registrationWhitelist: await configManager.getConfig('crowi', 'security:registrationWhitelist'),
  794. isPasswordResetEnabled: await configManager.getConfig('crowi', 'security:passport-local:isPasswordResetEnabled'),
  795. isEmailAuthenticationEnabled: await configManager.getConfig('crowi', 'security:passport-local:isEmailAuthenticationEnabled'),
  796. };
  797. const parameters = { action: SupportedAction.ACTION_ADMIN_AUTH_ID_PASS_UPDATE };
  798. activityEvent.emit('update', res.locals.activity._id, parameters);
  799. return res.apiv3({ localSettingParams });
  800. }
  801. catch (err) {
  802. const msg = 'Error occurred in updating local setting';
  803. logger.error('Error', err);
  804. return res.apiv3Err(new ErrorV3(msg, 'update-local-setting failed'));
  805. }
  806. });
  807. /**
  808. * @swagger
  809. *
  810. * /_api/v3/security-setting/ldap:
  811. * put:
  812. * tags: [SecuritySetting, apiv3]
  813. * description: Update LDAP setting
  814. * requestBody:
  815. * required: true
  816. * content:
  817. * application/json:
  818. * schema:
  819. * $ref: '#/components/schemas/LdapAuthSetting'
  820. * responses:
  821. * 200:
  822. * description: Succeeded to update LDAP setting
  823. * content:
  824. * application/json:
  825. * schema:
  826. * $ref: '#/components/schemas/LdapAuthSetting'
  827. */
  828. router.put('/ldap', loginRequiredStrictly, adminRequired, addActivity, validator.ldapAuth, apiV3FormValidator, async(req, res) => {
  829. const requestParams = {
  830. 'security:passport-ldap:serverUrl': req.body.serverUrl,
  831. 'security:passport-ldap:isUserBind': req.body.isUserBind,
  832. 'security:passport-ldap:bindDN': req.body.ldapBindDN,
  833. 'security:passport-ldap:bindDNPassword': req.body.ldapBindDNPassword,
  834. 'security:passport-ldap:searchFilter': req.body.ldapSearchFilter,
  835. 'security:passport-ldap:attrMapUsername': req.body.ldapAttrMapUsername,
  836. 'security:passport-ldap:isSameUsernameTreatedAsIdenticalUser': req.body.isSameUsernameTreatedAsIdenticalUser,
  837. 'security:passport-ldap:attrMapMail': req.body.ldapAttrMapMail,
  838. 'security:passport-ldap:attrMapName': req.body.ldapAttrMapName,
  839. 'security:passport-ldap:groupSearchBase': req.body.ldapGroupSearchBase,
  840. 'security:passport-ldap:groupSearchFilter': req.body.ldapGroupSearchFilter,
  841. 'security:passport-ldap:groupDnProperty': req.body.ldapGroupDnProperty,
  842. };
  843. try {
  844. await updateAndReloadStrategySettings('ldap', requestParams);
  845. const securitySettingParams = {
  846. serverUrl: await configManager.getConfig('crowi', 'security:passport-ldap:serverUrl'),
  847. isUserBind: await configManager.getConfig('crowi', 'security:passport-ldap:isUserBind'),
  848. ldapBindDN: await configManager.getConfig('crowi', 'security:passport-ldap:bindDN'),
  849. ldapBindDNPassword: await configManager.getConfig('crowi', 'security:passport-ldap:bindDNPassword'),
  850. ldapSearchFilter: await configManager.getConfig('crowi', 'security:passport-ldap:searchFilter'),
  851. ldapAttrMapUsername: await configManager.getConfig('crowi', 'security:passport-ldap:attrMapUsername'),
  852. isSameUsernameTreatedAsIdenticalUser: await configManager.getConfig('crowi', 'security:passport-ldap:isSameUsernameTreatedAsIdenticalUser'),
  853. ldapAttrMapMail: await configManager.getConfig('crowi', 'security:passport-ldap:attrMapMail'),
  854. ldapAttrMapName: await configManager.getConfig('crowi', 'security:passport-ldap:attrMapName'),
  855. ldapGroupSearchBase: await configManager.getConfig('crowi', 'security:passport-ldap:groupSearchBase'),
  856. ldapGroupSearchFilter: await configManager.getConfig('crowi', 'security:passport-ldap:groupSearchFilter'),
  857. ldapGroupDnProperty: await configManager.getConfig('crowi', 'security:passport-ldap:groupDnProperty'),
  858. };
  859. const parameters = { action: SupportedAction.ACTION_ADMIN_AUTH_LDAP_UPDATE };
  860. activityEvent.emit('update', res.locals.activity._id, parameters);
  861. return res.apiv3({ securitySettingParams });
  862. }
  863. catch (err) {
  864. const msg = 'Error occurred in updating SAML setting';
  865. logger.error('Error', err);
  866. return res.apiv3Err(new ErrorV3(msg, 'update-SAML-failed'));
  867. }
  868. });
  869. /**
  870. * @swagger
  871. *
  872. * /_api/v3/security-setting/saml:
  873. * put:
  874. * tags: [SecuritySetting, apiv3]
  875. * description: Update SAML setting
  876. * requestBody:
  877. * required: true
  878. * content:
  879. * application/json:
  880. * schema:
  881. * $ref: '#/components/schemas/SamlAuthSetting'
  882. * responses:
  883. * 200:
  884. * description: Succeeded to update SAML setting
  885. * content:
  886. * application/json:
  887. * schema:
  888. * $ref: '#/components/schemas/SamlAuthSetting'
  889. */
  890. router.put('/saml', loginRequiredStrictly, adminRequired, addActivity, validator.samlAuth, apiV3FormValidator, async(req, res) => {
  891. // For the value of each mandatory items,
  892. // check whether it from the environment variables is empty and form value to update it is empty
  893. // validate the syntax of a attribute - based login control rule
  894. const invalidValues = [];
  895. for (const configKey of crowi.passportService.mandatoryConfigKeysForSaml) {
  896. const key = configKey.replace('security:passport-saml:', '');
  897. const formValue = req.body[key];
  898. if (configManager.getConfigFromEnvVars('crowi', configKey) === null && formValue == null) {
  899. const formItemName = req.t(`security_setting.form_item_name.${key}`);
  900. invalidValues.push(req.t('form_validation.required', formItemName));
  901. }
  902. }
  903. if (invalidValues.length !== 0) {
  904. return res.apiv3Err(req.t('form_validation.error_message'), 400, invalidValues);
  905. }
  906. const rule = req.body.ABLCRule;
  907. // Empty string disables attribute-based login control.
  908. // So, when rule is empty string, validation is passed.
  909. if (rule != null) {
  910. try {
  911. crowi.passportService.parseABLCRule(rule);
  912. }
  913. catch (err) {
  914. return res.apiv3Err(req.t('form_validation.invalid_syntax', req.t('security_settings.form_item_name.ABLCRule')), 400);
  915. }
  916. }
  917. const requestParams = {
  918. 'security:passport-saml:entryPoint': req.body.entryPoint,
  919. 'security:passport-saml:issuer': req.body.issuer,
  920. 'security:passport-saml:cert': req.body.cert,
  921. 'security:passport-saml:attrMapId': req.body.attrMapId,
  922. 'security:passport-saml:attrMapUsername': req.body.attrMapUsername,
  923. 'security:passport-saml:attrMapMail': req.body.attrMapMail,
  924. 'security:passport-saml:attrMapFirstName': req.body.attrMapFirstName,
  925. 'security:passport-saml:attrMapLastName': req.body.attrMapLastName,
  926. 'security:passport-saml:isSameUsernameTreatedAsIdenticalUser': req.body.isSameUsernameTreatedAsIdenticalUser,
  927. 'security:passport-saml:isSameEmailTreatedAsIdenticalUser': req.body.isSameEmailTreatedAsIdenticalUser,
  928. 'security:passport-saml:ABLCRule': req.body.ABLCRule,
  929. };
  930. try {
  931. await updateAndReloadStrategySettings('saml', requestParams);
  932. const securitySettingParams = {
  933. missingMandatoryConfigKeys: await crowi.passportService.getSamlMissingMandatoryConfigKeys(),
  934. samlEntryPoint: await configManager.getConfigFromDB('crowi', 'security:passport-saml:entryPoint'),
  935. samlIssuer: await configManager.getConfigFromDB('crowi', 'security:passport-saml:issuer'),
  936. samlCert: await configManager.getConfigFromDB('crowi', 'security:passport-saml:cert'),
  937. samlAttrMapId: await configManager.getConfigFromDB('crowi', 'security:passport-saml:attrMapId'),
  938. samlAttrMapUsername: await configManager.getConfigFromDB('crowi', 'security:passport-saml:attrMapUsername'),
  939. samlAttrMapMail: await configManager.getConfigFromDB('crowi', 'security:passport-saml:attrMapMail'),
  940. samlAttrMapFirstName: await configManager.getConfigFromDB('crowi', 'security:passport-saml:attrMapFirstName'),
  941. samlAttrMapLastName: await configManager.getConfigFromDB('crowi', 'security:passport-saml:attrMapLastName'),
  942. isSameUsernameTreatedAsIdenticalUser: await configManager.getConfig('crowi', 'security:passport-saml:isSameUsernameTreatedAsIdenticalUser'),
  943. isSameEmailTreatedAsIdenticalUser: await configManager.getConfig('crowi', 'security:passport-saml:isSameEmailTreatedAsIdenticalUser'),
  944. samlABLCRule: await configManager.getConfig('crowi', 'security:passport-saml:ABLCRule'),
  945. };
  946. const parameters = { action: SupportedAction.ACTION_ADMIN_AUTH_SAML_UPDATE };
  947. activityEvent.emit('update', res.locals.activity._id, parameters);
  948. return res.apiv3({ securitySettingParams });
  949. }
  950. catch (err) {
  951. const msg = 'Error occurred in updating SAML setting';
  952. logger.error('Error', err);
  953. return res.apiv3Err(new ErrorV3(msg, 'update-SAML-failed'));
  954. }
  955. });
  956. /**
  957. * @swagger
  958. *
  959. * /_api/v3/security-setting/oidc:
  960. * put:
  961. * tags: [SecuritySetting, apiv3]
  962. * description: Update OpenID Connect setting
  963. * requestBody:
  964. * required: true
  965. * content:
  966. * application/json:
  967. * schema:
  968. * $ref: '#/components/schemas/OidcAuthSetting'
  969. * responses:
  970. * 200:
  971. * description: Succeeded to update OpenID Connect setting
  972. * content:
  973. * application/json:
  974. * schema:
  975. * $ref: '#/components/schemas/OidcAuthSetting'
  976. */
  977. router.put('/oidc', loginRequiredStrictly, adminRequired, addActivity, validator.oidcAuth, apiV3FormValidator, async(req, res) => {
  978. const requestParams = {
  979. 'security:passport-oidc:providerName': req.body.oidcProviderName,
  980. 'security:passport-oidc:issuerHost': req.body.oidcIssuerHost,
  981. 'security:passport-oidc:authorizationEndpoint': req.body.oidcAuthorizationEndpoint,
  982. 'security:passport-oidc:tokenEndpoint': req.body.oidcTokenEndpoint,
  983. 'security:passport-oidc:revocationEndpoint': req.body.oidcRevocationEndpoint,
  984. 'security:passport-oidc:introspectionEndpoint': req.body.oidcIntrospectionEndpoint,
  985. 'security:passport-oidc:userInfoEndpoint': req.body.oidcUserInfoEndpoint,
  986. 'security:passport-oidc:endSessionEndpoint': req.body.oidcEndSessionEndpoint,
  987. 'security:passport-oidc:registrationEndpoint': req.body.oidcRegistrationEndpoint,
  988. 'security:passport-oidc:jwksUri': req.body.oidcJWKSUri,
  989. 'security:passport-oidc:clientId': req.body.oidcClientId,
  990. 'security:passport-oidc:clientSecret': req.body.oidcClientSecret,
  991. 'security:passport-oidc:attrMapId': req.body.oidcAttrMapId,
  992. 'security:passport-oidc:attrMapUserName': req.body.oidcAttrMapUserName,
  993. 'security:passport-oidc:attrMapName': req.body.oidcAttrMapName,
  994. 'security:passport-oidc:attrMapMail': req.body.oidcAttrMapEmail,
  995. 'security:passport-oidc:isSameUsernameTreatedAsIdenticalUser': req.body.isSameUsernameTreatedAsIdenticalUser,
  996. 'security:passport-oidc:isSameEmailTreatedAsIdenticalUser': req.body.isSameEmailTreatedAsIdenticalUser,
  997. };
  998. try {
  999. await updateAndReloadStrategySettings('oidc', requestParams);
  1000. const securitySettingParams = {
  1001. oidcProviderName: await configManager.getConfig('crowi', 'security:passport-oidc:providerName'),
  1002. oidcIssuerHost: await configManager.getConfig('crowi', 'security:passport-oidc:issuerHost'),
  1003. oidcAuthorizationEndpoint: await configManager.getConfig('crowi', 'security:passport-oidc:authorizationEndpoint'),
  1004. oidcTokenEndpoint: await configManager.getConfig('crowi', 'security:passport-oidc:tokenEndpoint'),
  1005. oidcRevocationEndpoint: await configManager.getConfig('crowi', 'security:passport-oidc:revocationEndpoint'),
  1006. oidcIntrospectionEndpoint: await configManager.getConfig('crowi', 'security:passport-oidc:introspectionEndpoint'),
  1007. oidcUserInfoEndpoint: await configManager.getConfig('crowi', 'security:passport-oidc:userInfoEndpoint'),
  1008. oidcEndSessionEndpoint: await configManager.getConfig('crowi', 'security:passport-oidc:endSessionEndpoint'),
  1009. oidcRegistrationEndpoint: await configManager.getConfig('crowi', 'security:passport-oidc:registrationEndpoint'),
  1010. oidcJWKSUri: await configManager.getConfig('crowi', 'security:passport-oidc:jwksUri'),
  1011. oidcClientId: await configManager.getConfig('crowi', 'security:passport-oidc:clientId'),
  1012. oidcClientSecret: await configManager.getConfig('crowi', 'security:passport-oidc:clientSecret'),
  1013. oidcAttrMapId: await configManager.getConfig('crowi', 'security:passport-oidc:attrMapId'),
  1014. oidcAttrMapUserName: await configManager.getConfig('crowi', 'security:passport-oidc:attrMapUserName'),
  1015. oidcAttrMapName: await configManager.getConfig('crowi', 'security:passport-oidc:attrMapName'),
  1016. oidcAttrMapEmail: await configManager.getConfig('crowi', 'security:passport-oidc:attrMapMail'),
  1017. isSameUsernameTreatedAsIdenticalUser: await configManager.getConfig('crowi', 'security:passport-oidc:isSameUsernameTreatedAsIdenticalUser'),
  1018. isSameEmailTreatedAsIdenticalUser: await configManager.getConfig('crowi', 'security:passport-oidc:isSameEmailTreatedAsIdenticalUser'),
  1019. };
  1020. const parameters = { action: SupportedAction.ACTION_ADMIN_AUTH_OIDC_UPDATE };
  1021. activityEvent.emit('update', res.locals.activity._id, parameters);
  1022. return res.apiv3({ securitySettingParams });
  1023. }
  1024. catch (err) {
  1025. const msg = 'Error occurred in updating OpenIDConnect';
  1026. logger.error('Error', err);
  1027. return res.apiv3Err(new ErrorV3(msg, 'update-OpenIDConnect-failed'));
  1028. }
  1029. });
  1030. /**
  1031. * @swagger
  1032. *
  1033. * /_api/v3/security-setting/google-oauth:
  1034. * put:
  1035. * tags: [SecuritySetting, apiv3]
  1036. * description: Update google OAuth
  1037. * requestBody:
  1038. * required: true
  1039. * content:
  1040. * application/json:
  1041. * schema:
  1042. * $ref: '#/components/schemas/GoogleOAuthSetting'
  1043. * responses:
  1044. * 200:
  1045. * description: Succeeded to google OAuth
  1046. * content:
  1047. * application/json:
  1048. * schema:
  1049. * $ref: '#/components/schemas/GoogleOAuthSetting'
  1050. */
  1051. router.put('/google-oauth', loginRequiredStrictly, adminRequired, addActivity, validator.googleOAuth, apiV3FormValidator, async(req, res) => {
  1052. const requestParams = {
  1053. 'security:passport-google:clientId': req.body.googleClientId,
  1054. 'security:passport-google:clientSecret': req.body.googleClientSecret,
  1055. 'security:passport-google:isSameEmailTreatedAsIdenticalUser': req.body.isSameEmailTreatedAsIdenticalUser,
  1056. };
  1057. try {
  1058. await updateAndReloadStrategySettings('google', requestParams);
  1059. const securitySettingParams = {
  1060. googleClientId: await configManager.getConfig('crowi', 'security:passport-google:clientId'),
  1061. googleClientSecret: await configManager.getConfig('crowi', 'security:passport-google:clientSecret'),
  1062. isSameEmailTreatedAsIdenticalUser: await configManager.getConfig('crowi', 'security:passport-google:isSameEmailTreatedAsIdenticalUser'),
  1063. };
  1064. const parameters = { action: SupportedAction.ACTION_ADMIN_AUTH_GOOGLE_UPDATE };
  1065. activityEvent.emit('update', res.locals.activity._id, parameters);
  1066. return res.apiv3({ securitySettingParams });
  1067. }
  1068. catch (err) {
  1069. const msg = 'Error occurred in updating googleOAuth';
  1070. logger.error('Error', err);
  1071. return res.apiv3Err(new ErrorV3(msg, 'update-googleOAuth-failed'));
  1072. }
  1073. });
  1074. /**
  1075. * @swagger
  1076. *
  1077. * /_api/v3/security-setting/github-oauth:
  1078. * put:
  1079. * tags: [SecuritySetting, apiv3]
  1080. * description: Update github OAuth
  1081. * requestBody:
  1082. * required: true
  1083. * content:
  1084. * application/json:
  1085. * schema:
  1086. * $ref: '#/components/schemas/GitHubOAuthSetting'
  1087. * responses:
  1088. * 200:
  1089. * description: Succeeded to github OAuth
  1090. * content:
  1091. * application/json:
  1092. * schema:
  1093. * $ref: '#/components/schemas/GitHubOAuthSetting'
  1094. */
  1095. router.put('/github-oauth', loginRequiredStrictly, adminRequired, addActivity, validator.githubOAuth, apiV3FormValidator, async(req, res) => {
  1096. const requestParams = {
  1097. 'security:passport-github:clientId': req.body.githubClientId,
  1098. 'security:passport-github:clientSecret': req.body.githubClientSecret,
  1099. 'security:passport-github:isSameUsernameTreatedAsIdenticalUser': req.body.isSameUsernameTreatedAsIdenticalUser,
  1100. };
  1101. try {
  1102. await updateAndReloadStrategySettings('github', requestParams);
  1103. const securitySettingParams = {
  1104. githubClientId: await configManager.getConfig('crowi', 'security:passport-github:clientId'),
  1105. githubClientSecret: await configManager.getConfig('crowi', 'security:passport-github:clientSecret'),
  1106. isSameUsernameTreatedAsIdenticalUser: await configManager.getConfig('crowi', 'security:passport-github:isSameUsernameTreatedAsIdenticalUser'),
  1107. };
  1108. const parameters = { action: SupportedAction.ACTION_ADMIN_AUTH_GITHUB_UPDATE };
  1109. activityEvent.emit('update', res.locals.activity._id, parameters);
  1110. return res.apiv3({ securitySettingParams });
  1111. }
  1112. catch (err) {
  1113. // reset strategy
  1114. await crowi.passportService.resetGitHubStrategy();
  1115. const msg = 'Error occurred in updating githubOAuth';
  1116. logger.error('Error', err);
  1117. return res.apiv3Err(new ErrorV3(msg, 'update-githubOAuth-failed'));
  1118. }
  1119. });
  1120. return router;
  1121. };