authentication.mjs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. import _ from 'lodash-es'
  2. import { generateError, generateSuccess } from '../../helpers/graph.mjs'
  3. import jwt from 'jsonwebtoken'
  4. import ms from 'ms'
  5. import { DateTime } from 'luxon'
  6. import base64 from '@hexagon/base64'
  7. import {
  8. generateRegistrationOptions,
  9. verifyRegistrationResponse,
  10. generateAuthenticationOptions,
  11. verifyAuthenticationResponse
  12. } from '@simplewebauthn/server'
  13. export default {
  14. Query: {
  15. /**
  16. * List of API Keys
  17. */
  18. async apiKeys (obj, args, context) {
  19. const keys = await WIKI.db.apiKeys.query().orderBy(['isRevoked', 'name'])
  20. return keys.map(k => ({
  21. id: k.id,
  22. name: k.name,
  23. keyShort: '...' + k.key.substring(k.key.length - 20),
  24. isRevoked: k.isRevoked,
  25. expiration: k.expiration,
  26. createdAt: k.createdAt,
  27. updatedAt: k.updatedAt
  28. }))
  29. },
  30. /**
  31. * Current API State
  32. */
  33. apiState () {
  34. return WIKI.config.api.isEnabled
  35. },
  36. /**
  37. * Fetch authentication strategies
  38. */
  39. async authStrategies () {
  40. return WIKI.data.authentication.map(stg => ({
  41. ...stg,
  42. isAvailable: stg.isAvailable === true
  43. }))
  44. },
  45. /**
  46. * Fetch active authentication strategies
  47. */
  48. async authActiveStrategies (obj, args, context) {
  49. const strategies = await WIKI.db.authentication.getStrategies({ enabledOnly: args.enabledOnly })
  50. return strategies.map(a => {
  51. const str = _.find(WIKI.data.authentication, ['key', a.module]) || {}
  52. return {
  53. ...a,
  54. config: _.transform(str.props, (r, v, k) => {
  55. r[k] = v.sensitive ? '********' : a.config[k]
  56. }, {})
  57. }
  58. })
  59. },
  60. /**
  61. * Fetch site authentication strategies
  62. */
  63. async authSiteStrategies (obj, args, context, info) {
  64. const site = await WIKI.db.sites.query().findById(args.siteId)
  65. const activeStrategies = await WIKI.db.authentication.getStrategies({ enabledOnly: true })
  66. const siteStrategies = _.sortBy(activeStrategies.map(str => {
  67. const siteAuth = _.find(site.config.authStrategies, ['id', str.id]) || {}
  68. return {
  69. id: str.id,
  70. activeStrategy: str,
  71. order: siteAuth.order ?? 0,
  72. isVisible: siteAuth.isVisible ?? false
  73. }
  74. }), ['order'])
  75. return args.visibleOnly ? siteStrategies.filter(s => s.isVisible) : siteStrategies
  76. }
  77. },
  78. Mutation: {
  79. /**
  80. * Create New API Key
  81. */
  82. async createApiKey (obj, args, context) {
  83. try {
  84. const key = await WIKI.db.apiKeys.createNewKey(args)
  85. await WIKI.auth.reloadApiKeys()
  86. WIKI.events.outbound.emit('reloadApiKeys')
  87. return {
  88. key,
  89. operation: generateSuccess('API Key created successfully')
  90. }
  91. } catch (err) {
  92. WIKI.logger.warn(err)
  93. return generateError(err)
  94. }
  95. },
  96. /**
  97. * Perform Login
  98. */
  99. async login (obj, args, context) {
  100. try {
  101. const authResult = await WIKI.db.users.login(args, context)
  102. return {
  103. ...authResult,
  104. operation: generateSuccess('Login success')
  105. }
  106. } catch (err) {
  107. // LDAP Debug Flag
  108. if (args.strategy === 'ldap' && WIKI.config.flags.ldapdebug) {
  109. WIKI.logger.warn('LDAP LOGIN ERROR (c1): ', err)
  110. }
  111. WIKI.logger.debug(err)
  112. return generateError(err)
  113. }
  114. },
  115. /**
  116. * Perform 2FA Login
  117. */
  118. async loginTFA (obj, args, context) {
  119. try {
  120. const authResult = await WIKI.db.users.loginTFA(args, context)
  121. return {
  122. ...authResult,
  123. operation: generateSuccess('TFA success')
  124. }
  125. } catch (err) {
  126. WIKI.logger.debug(err)
  127. return generateError(err)
  128. }
  129. },
  130. /**
  131. * Setup TFA
  132. */
  133. async setupTFA (obj, args, context) {
  134. try {
  135. const userId = context.req.user?.id
  136. if (!userId) {
  137. throw new Error('ERR_USER_NOT_AUTHENTICATED')
  138. }
  139. const usr = await WIKI.db.users.query().findById(userId)
  140. if (!usr) {
  141. throw new Error('ERR_INVALID_USER')
  142. }
  143. const str = WIKI.auth.strategies[args.strategyId]
  144. if (!str) {
  145. throw new Error('ERR_INVALID_STRATEGY')
  146. }
  147. if (!usr.auth[args.strategyId]) {
  148. throw new Error('ERR_INVALID_STRATEGY')
  149. }
  150. if (usr.auth[args.strategyId].tfaIsActive) {
  151. throw new Error('ERR_TFA_ALREADY_ACTIVE')
  152. }
  153. const tfaQRImage = await usr.generateTFA(args.strategyId, args.siteId)
  154. const tfaToken = await WIKI.db.userKeys.generateToken({
  155. kind: 'tfaSetup',
  156. userId: usr.id,
  157. meta: {
  158. strategyId: args.strategyId
  159. }
  160. })
  161. return {
  162. operation: generateSuccess('TFA setup started'),
  163. continuationToken: tfaToken,
  164. tfaQRImage
  165. }
  166. } catch (err) {
  167. return generateError(err)
  168. }
  169. },
  170. /**
  171. * Deactivate 2FA
  172. */
  173. async deactivateTFA (obj, args, context) {
  174. try {
  175. const userId = context.req.user?.id
  176. if (!userId) {
  177. throw new Error('ERR_USER_NOT_AUTHENTICATED')
  178. }
  179. const usr = await WIKI.db.users.query().findById(userId)
  180. if (!usr) {
  181. throw new Error('ERR_INVALID_USER')
  182. }
  183. const str = WIKI.auth.strategies[args.strategyId]
  184. if (!str) {
  185. throw new Error('ERR_INVALID_STRATEGY')
  186. }
  187. if (!usr.auth[args.strategyId]) {
  188. throw new Error('ERR_INVALID_STRATEGY')
  189. }
  190. if (!usr.auth[args.strategyId].tfaIsActive) {
  191. throw new Error('ERR_TFA_NOT_ACTIVE')
  192. }
  193. usr.auth[args.strategyId].tfaIsActive = false
  194. usr.auth[args.strategyId].tfaSecret = null
  195. await usr.$query().patch({
  196. auth: usr.auth
  197. })
  198. return {
  199. operation: generateSuccess('TFA deactivated successfully.')
  200. }
  201. } catch (err) {
  202. return generateError(err)
  203. }
  204. },
  205. /**
  206. * Setup Passkey
  207. */
  208. async setupPasskey (obj, args, context) {
  209. try {
  210. const userId = context.req.user?.id
  211. if (!userId) {
  212. throw new Error('ERR_USER_NOT_AUTHENTICATED')
  213. }
  214. const usr = await WIKI.db.users.query().findById(userId)
  215. if (!usr) {
  216. throw new Error('ERR_INVALID_USER')
  217. }
  218. const site = WIKI.sites[args.siteId]
  219. if (!site) {
  220. throw new Error('ERR_INVALID_SITE')
  221. } else if (site.hostname === '*') {
  222. WIKI.logger.warn('Cannot use passkeys with a wildcard site hostname. Enter a valid hostname under the Administration Area > General.')
  223. throw new Error('ERR_PK_HOSTNAME_MISSING')
  224. }
  225. const options = await generateRegistrationOptions({
  226. rpName: site.config.title,
  227. rpId: site.hostname,
  228. userID: usr.id,
  229. userName: usr.email,
  230. userDisplayName: usr.name,
  231. attestationType: 'none',
  232. authenticatorSelection: {
  233. residentKey: 'required',
  234. userVerification: 'preferred'
  235. },
  236. excludeCredentials: usr.passkeys.authenticators?.map(authenticator => ({
  237. id: new Uint8Array(authenticator.credentialID),
  238. type: 'public-key',
  239. transports: authenticator.transports
  240. })) ?? []
  241. })
  242. usr.passkeys.reg = {
  243. challenge: options.challenge,
  244. rpId: site.hostname,
  245. siteId: site.id
  246. }
  247. await usr.$query().patch({
  248. passkeys: usr.passkeys
  249. })
  250. return {
  251. operation: generateSuccess('Passkey registration options generated successfully.'),
  252. registrationOptions: options
  253. }
  254. } catch (err) {
  255. return generateError(err)
  256. }
  257. },
  258. /**
  259. * Finalize Passkey Registration
  260. */
  261. async finalizePasskey (obj, args, context) {
  262. try {
  263. const userId = context.req.user?.id
  264. if (!userId) {
  265. throw new Error('ERR_USER_NOT_AUTHENTICATED')
  266. }
  267. const usr = await WIKI.db.users.query().findById(userId)
  268. if (!usr) {
  269. throw new Error('ERR_INVALID_USER')
  270. } else if (!usr.passkeys?.reg) {
  271. throw new Error('ERR_PASSKEY_NOT_SETUP')
  272. }
  273. if (!args.name || args.name.trim().length < 1 || args.name.length > 255) {
  274. throw new Error('ERR_PK_NAME_MISSING_OR_INVALID')
  275. }
  276. const verification = await verifyRegistrationResponse({
  277. response: args.registrationResponse,
  278. expectedChallenge: usr.passkeys.reg.challenge,
  279. expectedOrigin: `https://${usr.passkeys.reg.rpId}`,
  280. expectedRPID: usr.passkeys.reg.rpId,
  281. requireUserVerification: true
  282. })
  283. if (!verification.verified) {
  284. throw new Error('ERR_PK_VERIFICATION_FAILED')
  285. }
  286. if (!usr.passkeys.authenticators) {
  287. usr.passkeys.authenticators = []
  288. }
  289. usr.passkeys.authenticators.push({
  290. ...verification.registrationInfo,
  291. id: base64.fromArrayBuffer(verification.registrationInfo.credentialID, true),
  292. createdAt: new Date(),
  293. name: args.name,
  294. siteId: usr.passkeys.reg.siteId,
  295. transports: args.registrationResponse.response.transports
  296. })
  297. delete usr.passkeys.reg
  298. await usr.$query().patch({
  299. passkeys: JSON.stringify(usr.passkeys, (k, v) => {
  300. if (v instanceof Uint8Array) {
  301. return Array.apply([], v)
  302. }
  303. return v
  304. })
  305. })
  306. return {
  307. operation: generateSuccess('Passkey registered successfully.')
  308. }
  309. } catch (err) {
  310. return generateError(err)
  311. }
  312. },
  313. /**
  314. * Deactivate a passkey
  315. */
  316. async deactivatePasskey (obj, args, context) {
  317. try {
  318. const userId = context.req.user?.id
  319. if (!userId) {
  320. throw new Error('ERR_USER_NOT_AUTHENTICATED')
  321. }
  322. const usr = await WIKI.db.users.query().findById(userId)
  323. if (!usr) {
  324. throw new Error('ERR_INVALID_USER')
  325. } else if (!usr.passkeys?.authenticators) {
  326. throw new Error('ERR_PASSKEY_NOT_SETUP')
  327. }
  328. usr.passkeys.authenticators = usr.passkeys.authenticators.filter(a => a.id !== args.id)
  329. await usr.$query().patch({
  330. passkeys: usr.passkeys
  331. })
  332. return {
  333. operation: generateSuccess('Passkey deactivated successfully.')
  334. }
  335. } catch (err) {
  336. return generateError(err)
  337. }
  338. },
  339. /**
  340. * Login via passkey - Generate challenge
  341. */
  342. async authenticatePasskeyGenerate (obj, args, context) {
  343. try {
  344. const site = WIKI.sites[args.siteId]
  345. if (!site) {
  346. throw new Error('ERR_INVALID_SITE')
  347. } else if (site.hostname === '*') {
  348. WIKI.logger.warn('Cannot use passkeys with a wildcard site hostname. Enter a valid hostname under the Administration Area > General.')
  349. throw new Error('ERR_PK_HOSTNAME_MISSING')
  350. }
  351. const usr = await WIKI.db.users.query().findOne({ email: args.email })
  352. if (!usr || !usr.passkeys?.authenticators) {
  353. // Fake success response to prevent email leaking
  354. WIKI.logger.debug(`Cannot generate passkey challenge for ${args.email}... (non-existing or missing passkeys setup)`)
  355. return {
  356. operation: generateSuccess('Passkey challenge generated.'),
  357. authOptions: await generateAuthenticationOptions({
  358. allowCredentials: [{
  359. id: new Uint8Array(Array(30).map(v => _.random(0, 254))),
  360. type: 'public-key',
  361. transports: ['internal']
  362. }],
  363. userVerification: 'preferred',
  364. rpId: site.hostname
  365. })
  366. }
  367. }
  368. const options = await generateAuthenticationOptions({
  369. allowCredentials: usr.passkeys.authenticators.map(authenticator => ({
  370. id: new Uint8Array(authenticator.credentialID),
  371. type: 'public-key',
  372. transports: authenticator.transports
  373. })),
  374. userVerification: 'preferred',
  375. rpId: site.hostname
  376. })
  377. usr.passkeys.login = {
  378. challenge: options.challenge,
  379. rpId: site.hostname,
  380. siteId: site.id
  381. }
  382. await usr.$query().patch({
  383. passkeys: usr.passkeys
  384. })
  385. return {
  386. operation: generateSuccess('Passkey challenge generated.'),
  387. authOptions: options
  388. }
  389. } catch (err) {
  390. return generateError(err)
  391. }
  392. },
  393. /**
  394. * Login via passkey - Verify challenge
  395. */
  396. async authenticatePasskeyVerify (obj, args, context) {
  397. try {
  398. if (!args.authResponse?.response?.userHandle) {
  399. throw new Error('ERR_INVALID_PASSKEY_RESPONSE')
  400. }
  401. const usr = await WIKI.db.users.query().findById(args.authResponse.response.userHandle)
  402. if (!usr) {
  403. WIKI.logger.debug(`Passkey Login Failure: Cannot find user ${args.authResponse.response.userHandle}`)
  404. throw new Error('ERR_LOGIN_FAILED')
  405. } else if (!usr.passkeys?.login) {
  406. WIKI.logger.debug(`Passkey Login Failure: Missing login auth generation step for user ${args.authResponse.response.userHandle}`)
  407. throw new Error('ERR_LOGIN_FAILED')
  408. } else if (!usr.passkeys.authenticators?.some(a => a.id === args.authResponse.id)) {
  409. WIKI.logger.debug(`Passkey Login Failure: Authenticator provided is not registered for user ${args.authResponse.response.userHandle}`)
  410. throw new Error('ERR_LOGIN_FAILED')
  411. }
  412. const verification = await verifyAuthenticationResponse({
  413. response: args.authResponse,
  414. expectedChallenge: usr.passkeys.login.challenge,
  415. expectedOrigin: `https://${usr.passkeys.login.rpId}`,
  416. expectedRPID: usr.passkeys.login.rpId,
  417. requireUserVerification: true,
  418. authenticator: _.find(usr.passkeys.authenticators, ['id', args.authResponse.id])
  419. })
  420. if (!verification.verified) {
  421. WIKI.logger.debug(`Passkey Login Failure: Challenge verification failed for user ${args.authResponse.response.userHandle}`)
  422. throw new Error('ERR_LOGIN_FAILED')
  423. }
  424. delete usr.passkeys.login
  425. await usr.$query().patch({
  426. passkeys: usr.passkeys
  427. })
  428. const jwtToken = await WIKI.db.users.refreshToken(usr)
  429. return {
  430. operation: generateSuccess('Passkey challenge accepted.'),
  431. nextAction: 'redirect',
  432. jwt: jwtToken.token,
  433. redirect: '/'
  434. }
  435. } catch (err) {
  436. return generateError(err)
  437. }
  438. },
  439. /**
  440. * Perform Password Change
  441. */
  442. async changePassword (obj, args, context) {
  443. try {
  444. if (args.continuationToken) {
  445. const authResult = await WIKI.db.users.loginChangePassword(args, context)
  446. return {
  447. ...authResult,
  448. operation: generateSuccess('Password set successfully')
  449. }
  450. } else {
  451. await WIKI.db.users.changePassword(args, context)
  452. return {
  453. operation: generateSuccess('Password changed successfully')
  454. }
  455. }
  456. } catch (err) {
  457. WIKI.logger.debug(err)
  458. return generateError(err)
  459. }
  460. },
  461. /**
  462. * Perform Forget Password
  463. */
  464. async forgotPassword (obj, args, context) {
  465. try {
  466. await WIKI.db.users.loginForgotPassword(args, context)
  467. return {
  468. operation: generateSuccess('Password reset request processed.')
  469. }
  470. } catch (err) {
  471. return generateError(err)
  472. }
  473. },
  474. /**
  475. * Register a new account
  476. */
  477. async register (obj, args, context) {
  478. try {
  479. const usr = await WIKI.db.users.createNewUser({ ...args, userInitiated: true })
  480. const authResult = await WIKI.db.users.afterLoginChecks(usr, WIKI.data.systemIds.localAuthId, context)
  481. return {
  482. ...authResult,
  483. operation: generateSuccess('Registration success')
  484. }
  485. } catch (err) {
  486. return generateError(err)
  487. }
  488. },
  489. /**
  490. * Refresh Token
  491. */
  492. async refreshToken (obj, args, context) {
  493. try {
  494. let decoded = {}
  495. if (!args.token) {
  496. throw new Error('ERR_MISSING_TOKEN')
  497. }
  498. try {
  499. decoded = jwt.verify(args.token, WIKI.config.auth.certs.public, {
  500. audience: WIKI.config.auth.audience,
  501. issuer: 'urn:wiki.js',
  502. algorithms: ['RS256'],
  503. ignoreExpiration: true
  504. })
  505. } catch (err) {
  506. throw new Error('ERR_INVALID_TOKEN')
  507. }
  508. if (DateTime.utc().minus(ms(WIKI.config.auth.tokenRenewal)) > DateTime.fromSeconds(decoded.exp)) {
  509. throw new Error('ERR_EXPIRED_TOKEN')
  510. }
  511. const newToken = await WIKI.db.users.refreshToken(decoded.id)
  512. return {
  513. jwt: newToken.token,
  514. operation: generateSuccess('Token refreshed successfully')
  515. }
  516. } catch (err) {
  517. return generateError(err)
  518. }
  519. },
  520. /**
  521. * Set API state
  522. */
  523. async setApiState (obj, args, context) {
  524. try {
  525. if (!WIKI.auth.checkAccess(context.req.user, ['manage:system'])) {
  526. throw new Error('ERR_FORBIDDEN')
  527. }
  528. WIKI.config.api.isEnabled = args.enabled
  529. await WIKI.configSvc.saveToDb(['api'])
  530. return {
  531. operation: generateSuccess('API State changed successfully')
  532. }
  533. } catch (err) {
  534. return generateError(err)
  535. }
  536. },
  537. /**
  538. * Revoke an API key
  539. */
  540. async revokeApiKey (obj, args, context) {
  541. try {
  542. if (!WIKI.auth.checkAccess(context.req.user, ['manage:system'])) {
  543. throw new Error('ERR_FORBIDDEN')
  544. }
  545. await WIKI.db.apiKeys.query().findById(args.id).patch({
  546. isRevoked: true
  547. })
  548. await WIKI.auth.reloadApiKeys()
  549. WIKI.events.outbound.emit('reloadApiKeys')
  550. return {
  551. operation: generateSuccess('API Key revoked successfully')
  552. }
  553. } catch (err) {
  554. return generateError(err)
  555. }
  556. },
  557. /**
  558. * Update Authentication Strategies
  559. */
  560. async updateAuthStrategies (obj, args, context) {
  561. try {
  562. if (!WIKI.auth.checkAccess(context.req.user, ['manage:system'])) {
  563. throw new Error('ERR_FORBIDDEN')
  564. }
  565. const previousStrategies = await WIKI.db.authentication.getStrategies()
  566. for (const str of args.strategies) {
  567. const newStr = {
  568. displayName: str.displayName,
  569. isEnabled: str.isEnabled,
  570. config: _.reduce(str.config, (result, value, key) => {
  571. _.set(result, `${value.key}`, _.get(JSON.parse(value.value), 'v', null))
  572. return result
  573. }, {}),
  574. selfRegistration: str.selfRegistration,
  575. domainWhitelist: { v: str.domainWhitelist },
  576. autoEnrollGroups: { v: str.autoEnrollGroups }
  577. }
  578. if (_.some(previousStrategies, ['key', str.key])) {
  579. await WIKI.db.authentication.query().patch({
  580. key: str.key,
  581. strategyKey: str.strategyKey,
  582. ...newStr
  583. }).where('key', str.key)
  584. } else {
  585. await WIKI.db.authentication.query().insert({
  586. key: str.key,
  587. strategyKey: str.strategyKey,
  588. ...newStr
  589. })
  590. }
  591. }
  592. for (const str of _.differenceBy(previousStrategies, args.strategies, 'key')) {
  593. const hasUsers = await WIKI.db.users.query().count('* as total').where({ providerKey: str.key }).first()
  594. if (_.toSafeInteger(hasUsers.total) > 0) {
  595. throw new Error(`Cannot delete ${str.displayName} as 1 or more users are still using it.`)
  596. } else {
  597. await WIKI.db.authentication.query().delete().where('key', str.key)
  598. }
  599. }
  600. await WIKI.auth.activateStrategies()
  601. WIKI.events.outbound.emit('reloadAuthStrategies')
  602. return {
  603. responseResult: generateSuccess('Strategies updated successfully')
  604. }
  605. } catch (err) {
  606. return generateError(err)
  607. }
  608. },
  609. /**
  610. * Generate New Authentication Public / Private Key Certificates
  611. */
  612. async regenerateCertificates (obj, args, context) {
  613. try {
  614. if (!WIKI.auth.checkAccess(context.req.user, ['manage:system'])) {
  615. throw new Error('ERR_FORBIDDEN')
  616. }
  617. await WIKI.auth.regenerateCertificates()
  618. return {
  619. responseResult: generateSuccess('Certificates have been regenerated successfully.')
  620. }
  621. } catch (err) {
  622. return generateError(err)
  623. }
  624. },
  625. /**
  626. * Reset Guest User
  627. */
  628. async resetGuestUser (obj, args, context) {
  629. try {
  630. if (!WIKI.auth.checkAccess(context.req.user, ['manage:system'])) {
  631. throw new Error('ERR_FORBIDDEN')
  632. }
  633. await WIKI.auth.resetGuestUser()
  634. return {
  635. responseResult: generateSuccess('Guest user has been reset successfully.')
  636. }
  637. } catch (err) {
  638. return generateError(err)
  639. }
  640. }
  641. },
  642. // ------------------------------------------------------------------
  643. // TYPE: AuthenticationActiveStrategy
  644. // ------------------------------------------------------------------
  645. AuthenticationActiveStrategy: {
  646. config (obj, args, context) {
  647. if (!WIKI.auth.checkAccess(context.req.user, ['manage:system'])) {
  648. throw new Error('ERR_FORBIDDEN')
  649. }
  650. return obj.config ?? {}
  651. },
  652. allowedEmailRegex (obj, args, context) {
  653. if (!WIKI.auth.checkAccess(context.req.user, ['manage:system'])) {
  654. throw new Error('ERR_FORBIDDEN')
  655. }
  656. return obj.allowedEmailRegex ?? ''
  657. },
  658. autoEnrollGroups (obj, args, context) {
  659. if (!WIKI.auth.checkAccess(context.req.user, ['manage:system'])) {
  660. throw new Error('ERR_FORBIDDEN')
  661. }
  662. return obj.autoEnrollGroups ?? []
  663. },
  664. strategy (obj, args, context) {
  665. return _.find(WIKI.data.authentication, ['key', obj.module])
  666. }
  667. }
  668. }