authentication.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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 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. * Register a new account
  139. */
  140. async register (obj, args, context) {
  141. try {
  142. await WIKI.models.users.register({ ...args, verify: true }, context)
  143. return {
  144. responseResult: graphHelper.generateSuccess('Registration success')
  145. }
  146. } catch (err) {
  147. return graphHelper.generateError(err)
  148. }
  149. },
  150. /**
  151. * Set API state
  152. */
  153. async setApiState (obj, args, context) {
  154. try {
  155. WIKI.config.api.isEnabled = args.enabled
  156. await WIKI.configSvc.saveToDb(['api'])
  157. return {
  158. responseResult: graphHelper.generateSuccess('API State changed successfully')
  159. }
  160. } catch (err) {
  161. return graphHelper.generateError(err)
  162. }
  163. },
  164. /**
  165. * Revoke an API key
  166. */
  167. async revokeApiKey (obj, args, context) {
  168. try {
  169. await WIKI.models.apiKeys.query().findById(args.id).patch({
  170. isRevoked: true
  171. })
  172. await WIKI.auth.reloadApiKeys()
  173. WIKI.events.outbound.emit('reloadApiKeys')
  174. return {
  175. responseResult: graphHelper.generateSuccess('API Key revoked successfully')
  176. }
  177. } catch (err) {
  178. return graphHelper.generateError(err)
  179. }
  180. },
  181. /**
  182. * Update Authentication Strategies
  183. */
  184. async updateStrategies (obj, args, context) {
  185. try {
  186. // WIKI.config.auth = {
  187. // audience: _.get(args, 'config.audience', WIKI.config.auth.audience),
  188. // tokenExpiration: _.get(args, 'config.tokenExpiration', WIKI.config.auth.tokenExpiration),
  189. // tokenRenewal: _.get(args, 'config.tokenRenewal', WIKI.config.auth.tokenRenewal)
  190. // }
  191. // await WIKI.configSvc.saveToDb(['auth'])
  192. const previousStrategies = await WIKI.models.authentication.getStrategies()
  193. for (const str of args.strategies) {
  194. const newStr = {
  195. displayName: str.displayName,
  196. order: str.order,
  197. config: _.reduce(str.config, (result, value, key) => {
  198. _.set(result, `${value.key}`, _.get(JSON.parse(value.value), 'v', null))
  199. return result
  200. }, {}),
  201. selfRegistration: str.selfRegistration,
  202. domainWhitelist: { v: str.domainWhitelist },
  203. autoEnrollGroups: { v: str.autoEnrollGroups }
  204. }
  205. if (_.some(previousStrategies, ['key', str.key])) {
  206. await WIKI.models.authentication.query().patch({
  207. key: str.key,
  208. strategyKey: str.strategyKey,
  209. ...newStr
  210. }).where('key', str.key)
  211. } else {
  212. await WIKI.models.authentication.query().insert({
  213. key: str.key,
  214. strategyKey: str.strategyKey,
  215. ...newStr
  216. })
  217. }
  218. }
  219. for (const str of _.differenceBy(previousStrategies, args.strategies, 'key')) {
  220. const hasUsers = await WIKI.models.users.query().count('* as total').where({ providerKey: str.key }).first()
  221. if (_.toSafeInteger(hasUsers.total) > 0) {
  222. throw new Error(`Cannot delete ${str.displayName} as 1 or more users are still using it.`)
  223. } else {
  224. await WIKI.models.authentication.query().delete().where('key', str.key)
  225. }
  226. }
  227. await WIKI.auth.activateStrategies()
  228. WIKI.events.outbound.emit('reloadAuthStrategies')
  229. return {
  230. responseResult: graphHelper.generateSuccess('Strategies updated successfully')
  231. }
  232. } catch (err) {
  233. return graphHelper.generateError(err)
  234. }
  235. },
  236. /**
  237. * Generate New Authentication Public / Private Key Certificates
  238. */
  239. async regenerateCertificates (obj, args, context) {
  240. try {
  241. await WIKI.auth.regenerateCertificates()
  242. return {
  243. responseResult: graphHelper.generateSuccess('Certificates have been regenerated successfully.')
  244. }
  245. } catch (err) {
  246. return graphHelper.generateError(err)
  247. }
  248. },
  249. /**
  250. * Reset Guest User
  251. */
  252. async resetGuestUser (obj, args, context) {
  253. try {
  254. await WIKI.auth.resetGuestUser()
  255. return {
  256. responseResult: graphHelper.generateSuccess('Guest user has been reset successfully.')
  257. }
  258. } catch (err) {
  259. return graphHelper.generateError(err)
  260. }
  261. }
  262. },
  263. AuthenticationStrategy: {
  264. icon (ap, args) {
  265. return fs.readFile(path.join(WIKI.ROOTPATH, `assets/svg/auth-icon-${ap.key}.svg`), 'utf8').catch(err => {
  266. if (err.code === 'ENOENT') {
  267. return null
  268. }
  269. throw err
  270. })
  271. }
  272. }
  273. }