You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

287 lines
8.5 KiB

  1. const _ = require('lodash')
  2. const fs = require('fs-extra')
  3. const path = require('path')
  4. const graphHelper = require('../../helpers/graph')
  5. /* global WIKI */
  6. module.exports = {
  7. Query: {
  8. async authentication () { return {} }
  9. },
  10. Mutation: {
  11. async authentication () { return {} }
  12. },
  13. AuthenticationQuery: {
  14. /**
  15. * List of API Keys
  16. */
  17. async apiKeys (obj, args, context) {
  18. const keys = await WIKI.models.apiKeys.query().orderBy(['isRevoked', 'name'])
  19. return keys.map(k => ({
  20. id: k.id,
  21. name: k.name,
  22. keyShort: '...' + k.key.substring(k.key.length - 20),
  23. isRevoked: k.isRevoked,
  24. expiration: k.expiration,
  25. createdAt: k.createdAt,
  26. updatedAt: k.updatedAt
  27. }))
  28. },
  29. /**
  30. * Current API State
  31. */
  32. apiState () {
  33. return WIKI.config.api.isEnabled
  34. },
  35. async strategies () {
  36. return WIKI.data.authentication.map(stg => ({
  37. ...stg,
  38. isAvailable: stg.isAvailable === true,
  39. props: _.sortBy(_.transform(stg.props, (res, value, key) => {
  40. res.push({
  41. key,
  42. value: JSON.stringify(value)
  43. })
  44. }, []), 'key')
  45. }))
  46. },
  47. /**
  48. * Fetch active authentication strategies
  49. */
  50. async activeStrategies (obj, args, context, info) {
  51. let strategies = await WIKI.models.authentication.getStrategies()
  52. strategies = strategies.map(stg => {
  53. const strategyInfo = _.find(WIKI.data.authentication, ['key', stg.strategyKey]) || {}
  54. return {
  55. ...stg,
  56. strategy: strategyInfo,
  57. config: _.sortBy(_.transform(stg.config, (res, value, key) => {
  58. const configData = _.get(strategyInfo.props, key, false)
  59. if (configData) {
  60. res.push({
  61. key,
  62. value: JSON.stringify({
  63. ...configData,
  64. value
  65. })
  66. })
  67. }
  68. }, []), 'key')
  69. }
  70. })
  71. return args.enabledOnly ? _.filter(strategies, 'isEnabled') : strategies
  72. }
  73. },
  74. AuthenticationMutation: {
  75. /**
  76. * Create New API Key
  77. */
  78. async createApiKey (obj, args, context) {
  79. try {
  80. const key = await WIKI.models.apiKeys.createNewKey(args)
  81. await WIKI.auth.reloadApiKeys()
  82. WIKI.events.outbound.emit('reloadApiKeys')
  83. return {
  84. key,
  85. responseResult: graphHelper.generateSuccess('API Key created successfully')
  86. }
  87. } catch (err) {
  88. return graphHelper.generateError(err)
  89. }
  90. },
  91. /**
  92. * Perform Login
  93. */
  94. async login (obj, args, context) {
  95. try {
  96. const authResult = await WIKI.models.users.login(args, context)
  97. return {
  98. ...authResult,
  99. responseResult: graphHelper.generateSuccess('Login success')
  100. }
  101. } catch (err) {
  102. // LDAP Debug Flag
  103. if (args.strategy === 'ldap' && WIKI.config.flags.ldapdebug) {
  104. WIKI.logger.warn('LDAP LOGIN ERROR (c1): ', err)
  105. }
  106. return graphHelper.generateError(err)
  107. }
  108. },
  109. /**
  110. * Perform 2FA Login
  111. */
  112. async loginTFA (obj, args, context) {
  113. try {
  114. const authResult = await WIKI.models.users.loginTFA(args, context)
  115. return {
  116. ...authResult,
  117. responseResult: graphHelper.generateSuccess('TFA success')
  118. }
  119. } catch (err) {
  120. return graphHelper.generateError(err)
  121. }
  122. },
  123. /**
  124. * Perform Mandatory Password Change after Login
  125. */
  126. async loginChangePassword (obj, args, context) {
  127. try {
  128. const authResult = await WIKI.models.users.loginChangePassword(args, context)
  129. return {
  130. ...authResult,
  131. responseResult: graphHelper.generateSuccess('Password changed successfully')
  132. }
  133. } catch (err) {
  134. return graphHelper.generateError(err)
  135. }
  136. },
  137. /**
  138. * Perform Mandatory Password Change after Login
  139. */
  140. async forgotPassword (obj, args, context) {
  141. try {
  142. await WIKI.models.users.loginForgotPassword(args, context)
  143. return {
  144. responseResult: graphHelper.generateSuccess('Password reset request processed.')
  145. }
  146. } catch (err) {
  147. return graphHelper.generateError(err)
  148. }
  149. },
  150. /**
  151. * Register a new account
  152. */
  153. async register (obj, args, context) {
  154. try {
  155. await WIKI.models.users.register({ ...args, verify: true }, context)
  156. return {
  157. responseResult: graphHelper.generateSuccess('Registration success')
  158. }
  159. } catch (err) {
  160. return graphHelper.generateError(err)
  161. }
  162. },
  163. /**
  164. * Set API state
  165. */
  166. async setApiState (obj, args, context) {
  167. try {
  168. WIKI.config.api.isEnabled = args.enabled
  169. await WIKI.configSvc.saveToDb(['api'])
  170. return {
  171. responseResult: graphHelper.generateSuccess('API State changed successfully')
  172. }
  173. } catch (err) {
  174. return graphHelper.generateError(err)
  175. }
  176. },
  177. /**
  178. * Revoke an API key
  179. */
  180. async revokeApiKey (obj, args, context) {
  181. try {
  182. await WIKI.models.apiKeys.query().findById(args.id).patch({
  183. isRevoked: true
  184. })
  185. await WIKI.auth.reloadApiKeys()
  186. WIKI.events.outbound.emit('reloadApiKeys')
  187. return {
  188. responseResult: graphHelper.generateSuccess('API Key revoked successfully')
  189. }
  190. } catch (err) {
  191. return graphHelper.generateError(err)
  192. }
  193. },
  194. /**
  195. * Update Authentication Strategies
  196. */
  197. async updateStrategies (obj, args, context) {
  198. try {
  199. const previousStrategies = await WIKI.models.authentication.getStrategies()
  200. for (const str of args.strategies) {
  201. const newStr = {
  202. displayName: str.displayName,
  203. order: str.order,
  204. isEnabled: str.isEnabled,
  205. config: _.reduce(str.config, (result, value, key) => {
  206. _.set(result, `${value.key}`, _.get(JSON.parse(value.value), 'v', null))
  207. return result
  208. }, {}),
  209. selfRegistration: str.selfRegistration,
  210. domainWhitelist: { v: str.domainWhitelist },
  211. autoEnrollGroups: { v: str.autoEnrollGroups }
  212. }
  213. if (_.some(previousStrategies, ['key', str.key])) {
  214. await WIKI.models.authentication.query().patch({
  215. key: str.key,
  216. strategyKey: str.strategyKey,
  217. ...newStr
  218. }).where('key', str.key)
  219. } else {
  220. await WIKI.models.authentication.query().insert({
  221. key: str.key,
  222. strategyKey: str.strategyKey,
  223. ...newStr
  224. })
  225. }
  226. }
  227. for (const str of _.differenceBy(previousStrategies, args.strategies, 'key')) {
  228. const hasUsers = await WIKI.models.users.query().count('* as total').where({ providerKey: str.key }).first()
  229. if (_.toSafeInteger(hasUsers.total) > 0) {
  230. throw new Error(`Cannot delete ${str.displayName} as 1 or more users are still using it.`)
  231. } else {
  232. await WIKI.models.authentication.query().delete().where('key', str.key)
  233. }
  234. }
  235. await WIKI.auth.activateStrategies()
  236. WIKI.events.outbound.emit('reloadAuthStrategies')
  237. return {
  238. responseResult: graphHelper.generateSuccess('Strategies updated successfully')
  239. }
  240. } catch (err) {
  241. return graphHelper.generateError(err)
  242. }
  243. },
  244. /**
  245. * Generate New Authentication Public / Private Key Certificates
  246. */
  247. async regenerateCertificates (obj, args, context) {
  248. try {
  249. await WIKI.auth.regenerateCertificates()
  250. return {
  251. responseResult: graphHelper.generateSuccess('Certificates have been regenerated successfully.')
  252. }
  253. } catch (err) {
  254. return graphHelper.generateError(err)
  255. }
  256. },
  257. /**
  258. * Reset Guest User
  259. */
  260. async resetGuestUser (obj, args, context) {
  261. try {
  262. await WIKI.auth.resetGuestUser()
  263. return {
  264. responseResult: graphHelper.generateSuccess('Guest user has been reset successfully.')
  265. }
  266. } catch (err) {
  267. return graphHelper.generateError(err)
  268. }
  269. }
  270. },
  271. AuthenticationStrategy: {
  272. icon (ap, args) {
  273. return fs.readFile(path.join(WIKI.ROOTPATH, `assets/svg/auth-icon-${ap.key}.svg`), 'utf8').catch(err => {
  274. if (err.code === 'ENOENT') {
  275. return null
  276. }
  277. throw err
  278. })
  279. }
  280. }
  281. }