@things-factory/auth-base 8.0.0-beta.9 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/client/actions/auth.ts +24 -0
  2. package/client/auth.ts +272 -0
  3. package/client/bootstrap.ts +47 -0
  4. package/client/directive/privileged.ts +28 -0
  5. package/client/index.ts +3 -0
  6. package/client/profiled.ts +83 -0
  7. package/client/reducers/auth.ts +31 -0
  8. package/dist-client/index.d.ts +0 -1
  9. package/dist-client/index.js +0 -1
  10. package/dist-client/index.js.map +1 -1
  11. package/dist-client/tsconfig.tsbuildinfo +1 -1
  12. package/dist-server/constants/error-code.d.ts +0 -2
  13. package/dist-server/constants/error-code.js +1 -3
  14. package/dist-server/constants/error-code.js.map +1 -1
  15. package/dist-server/controllers/change-pwd.js +2 -2
  16. package/dist-server/controllers/change-pwd.js.map +1 -1
  17. package/dist-server/controllers/delete-user.js +12 -13
  18. package/dist-server/controllers/delete-user.js.map +1 -1
  19. package/dist-server/controllers/invitation.d.ts +1 -2
  20. package/dist-server/controllers/invitation.js +5 -30
  21. package/dist-server/controllers/invitation.js.map +1 -1
  22. package/dist-server/controllers/profile.d.ts +3 -4
  23. package/dist-server/controllers/profile.js +2 -20
  24. package/dist-server/controllers/profile.js.map +1 -1
  25. package/dist-server/controllers/signin.d.ts +1 -4
  26. package/dist-server/controllers/signin.js +1 -17
  27. package/dist-server/controllers/signin.js.map +1 -1
  28. package/dist-server/controllers/signup.js +4 -13
  29. package/dist-server/controllers/signup.js.map +1 -1
  30. package/dist-server/controllers/unlock-user.js +0 -1
  31. package/dist-server/controllers/unlock-user.js.map +1 -1
  32. package/dist-server/controllers/verification.js +0 -1
  33. package/dist-server/controllers/verification.js.map +1 -1
  34. package/dist-server/middlewares/signin-middleware.js +4 -9
  35. package/dist-server/middlewares/signin-middleware.js.map +1 -1
  36. package/dist-server/middlewares/webauthn-middleware.js.map +1 -1
  37. package/dist-server/migrations/1548206416130-SeedUser.js +1 -2
  38. package/dist-server/migrations/1548206416130-SeedUser.js.map +1 -1
  39. package/dist-server/router/auth-checkin-router.js +2 -8
  40. package/dist-server/router/auth-checkin-router.js.map +1 -1
  41. package/dist-server/router/auth-private-process-router.js +7 -12
  42. package/dist-server/router/auth-private-process-router.js.map +1 -1
  43. package/dist-server/router/auth-public-process-router.js +9 -20
  44. package/dist-server/router/auth-public-process-router.js.map +1 -1
  45. package/dist-server/router/auth-signin-router.js +3 -3
  46. package/dist-server/router/auth-signin-router.js.map +1 -1
  47. package/dist-server/router/webauthn-router.js +1 -51
  48. package/dist-server/router/webauthn-router.js.map +1 -1
  49. package/dist-server/service/invitation/invitation-mutation.d.ts +2 -3
  50. package/dist-server/service/invitation/invitation-mutation.js +8 -20
  51. package/dist-server/service/invitation/invitation-mutation.js.map +1 -1
  52. package/dist-server/service/user/user-mutation.d.ts +9 -10
  53. package/dist-server/service/user/user-mutation.js +54 -112
  54. package/dist-server/service/user/user-mutation.js.map +1 -1
  55. package/dist-server/service/user/user-types.d.ts +0 -1
  56. package/dist-server/service/user/user-types.js +0 -4
  57. package/dist-server/service/user/user-types.js.map +1 -1
  58. package/dist-server/service/user/user.d.ts +0 -1
  59. package/dist-server/service/user/user.js +14 -40
  60. package/dist-server/service/user/user.js.map +1 -1
  61. package/dist-server/templates/account-unlock-email.d.ts +1 -2
  62. package/dist-server/templates/account-unlock-email.js +1 -1
  63. package/dist-server/templates/account-unlock-email.js.map +1 -1
  64. package/dist-server/templates/invitation-email.d.ts +1 -2
  65. package/dist-server/templates/invitation-email.js +1 -1
  66. package/dist-server/templates/invitation-email.js.map +1 -1
  67. package/dist-server/templates/verification-email.d.ts +1 -2
  68. package/dist-server/templates/verification-email.js +1 -1
  69. package/dist-server/templates/verification-email.js.map +1 -1
  70. package/dist-server/tsconfig.tsbuildinfo +1 -1
  71. package/package.json +6 -6
  72. package/server/constants/error-code.ts +20 -0
  73. package/server/constants/error-message.ts +0 -0
  74. package/server/constants/max-age.ts +1 -0
  75. package/server/controllers/auth.ts +5 -0
  76. package/server/controllers/change-pwd.ts +99 -0
  77. package/server/controllers/checkin.ts +21 -0
  78. package/server/controllers/delete-user.ts +68 -0
  79. package/server/controllers/invitation.ts +132 -0
  80. package/server/controllers/profile.ts +28 -0
  81. package/server/controllers/reset-password.ts +126 -0
  82. package/server/controllers/signin.ts +79 -0
  83. package/server/controllers/signup.ts +60 -0
  84. package/server/controllers/unlock-user.ts +61 -0
  85. package/server/controllers/utils/make-invitation-token.ts +5 -0
  86. package/server/controllers/utils/make-verification-token.ts +4 -0
  87. package/server/controllers/utils/password-rule.ts +120 -0
  88. package/server/controllers/utils/save-invitation-token.ts +10 -0
  89. package/server/controllers/utils/save-verification-token.ts +12 -0
  90. package/server/controllers/verification.ts +83 -0
  91. package/server/errors/auth-error.ts +24 -0
  92. package/server/errors/index.ts +2 -0
  93. package/server/errors/user-domain-not-match-error.ts +29 -0
  94. package/server/index.ts +37 -0
  95. package/server/middlewares/authenticate-401-middleware.ts +114 -0
  96. package/server/middlewares/domain-authenticate-middleware.ts +78 -0
  97. package/server/middlewares/graphql-authenticate-middleware.ts +13 -0
  98. package/server/middlewares/index.ts +67 -0
  99. package/server/middlewares/jwt-authenticate-middleware.ts +84 -0
  100. package/server/middlewares/signin-middleware.ts +55 -0
  101. package/server/middlewares/webauthn-middleware.ts +127 -0
  102. package/server/migrations/1548206416130-SeedUser.ts +59 -0
  103. package/server/migrations/1566805283882-SeedPrivilege.ts +28 -0
  104. package/server/migrations/index.ts +9 -0
  105. package/server/router/auth-checkin-router.ts +107 -0
  106. package/server/router/auth-private-process-router.ts +107 -0
  107. package/server/router/auth-public-process-router.ts +302 -0
  108. package/server/router/auth-signin-router.ts +55 -0
  109. package/server/router/auth-signup-router.ts +95 -0
  110. package/server/router/index.ts +9 -0
  111. package/server/router/oauth2/index.ts +2 -0
  112. package/server/router/oauth2/oauth2-authorize-router.ts +81 -0
  113. package/server/router/oauth2/oauth2-router.ts +165 -0
  114. package/server/router/oauth2/oauth2-server.ts +262 -0
  115. package/server/router/oauth2/passport-oauth2-client-password.ts +87 -0
  116. package/server/router/oauth2/passport-refresh-token.ts +87 -0
  117. package/server/router/path-base-domain-router.ts +8 -0
  118. package/server/router/site-root-router.ts +48 -0
  119. package/server/router/webauthn-router.ts +87 -0
  120. package/server/routes.ts +80 -0
  121. package/server/service/app-binding/app-binding-mutation.ts +22 -0
  122. package/server/service/app-binding/app-binding-query.ts +92 -0
  123. package/server/service/app-binding/app-binding-types.ts +11 -0
  124. package/server/service/app-binding/app-binding.ts +17 -0
  125. package/server/service/app-binding/index.ts +4 -0
  126. package/server/service/appliance/appliance-mutation.ts +113 -0
  127. package/server/service/appliance/appliance-query.ts +76 -0
  128. package/server/service/appliance/appliance-types.ts +56 -0
  129. package/server/service/appliance/appliance.ts +133 -0
  130. package/server/service/appliance/index.ts +6 -0
  131. package/server/service/application/application-mutation.ts +104 -0
  132. package/server/service/application/application-query.ts +98 -0
  133. package/server/service/application/application-types.ts +76 -0
  134. package/server/service/application/application.ts +216 -0
  135. package/server/service/application/index.ts +6 -0
  136. package/server/service/auth-provider/auth-provider-mutation.ts +159 -0
  137. package/server/service/auth-provider/auth-provider-parameter-spec.ts +24 -0
  138. package/server/service/auth-provider/auth-provider-query.ts +88 -0
  139. package/server/service/auth-provider/auth-provider-type.ts +67 -0
  140. package/server/service/auth-provider/auth-provider.ts +155 -0
  141. package/server/service/auth-provider/index.ts +7 -0
  142. package/server/service/domain-generator/domain-generator-mutation.ts +117 -0
  143. package/server/service/domain-generator/domain-generator-types.ts +46 -0
  144. package/server/service/domain-generator/index.ts +3 -0
  145. package/server/service/granted-role/granted-role-mutation.ts +156 -0
  146. package/server/service/granted-role/granted-role-query.ts +60 -0
  147. package/server/service/granted-role/granted-role.ts +27 -0
  148. package/server/service/granted-role/index.ts +6 -0
  149. package/server/service/index.ts +90 -0
  150. package/server/service/invitation/index.ts +6 -0
  151. package/server/service/invitation/invitation-mutation.ts +63 -0
  152. package/server/service/invitation/invitation-query.ts +33 -0
  153. package/server/service/invitation/invitation-types.ts +11 -0
  154. package/server/service/invitation/invitation.ts +63 -0
  155. package/server/service/login-history/index.ts +5 -0
  156. package/server/service/login-history/login-history-query.ts +51 -0
  157. package/server/service/login-history/login-history-type.ts +12 -0
  158. package/server/service/login-history/login-history.ts +45 -0
  159. package/server/service/partner/index.ts +6 -0
  160. package/server/service/partner/partner-mutation.ts +61 -0
  161. package/server/service/partner/partner-query.ts +102 -0
  162. package/server/service/partner/partner-types.ts +11 -0
  163. package/server/service/partner/partner.ts +57 -0
  164. package/server/service/password-history/index.ts +3 -0
  165. package/server/service/password-history/password-history.ts +16 -0
  166. package/server/service/privilege/index.ts +6 -0
  167. package/server/service/privilege/privilege-directive.ts +77 -0
  168. package/server/service/privilege/privilege-mutation.ts +92 -0
  169. package/server/service/privilege/privilege-query.ts +94 -0
  170. package/server/service/privilege/privilege-types.ts +60 -0
  171. package/server/service/privilege/privilege.ts +102 -0
  172. package/server/service/role/index.ts +6 -0
  173. package/server/service/role/role-mutation.ts +109 -0
  174. package/server/service/role/role-query.ts +155 -0
  175. package/server/service/role/role-types.ts +81 -0
  176. package/server/service/role/role.ts +72 -0
  177. package/server/service/user/domain-query.ts +24 -0
  178. package/server/service/user/index.ts +7 -0
  179. package/server/service/user/user-mutation.ts +413 -0
  180. package/server/service/user/user-query.ts +145 -0
  181. package/server/service/user/user-types.ts +97 -0
  182. package/server/service/user/user.ts +354 -0
  183. package/server/service/users-auth-providers/index.ts +5 -0
  184. package/server/service/users-auth-providers/users-auth-providers.ts +71 -0
  185. package/server/service/verification-token/index.ts +3 -0
  186. package/server/service/verification-token/verification-token.ts +60 -0
  187. package/server/service/web-auth-credential/index.ts +3 -0
  188. package/server/service/web-auth-credential/web-auth-credential.ts +67 -0
  189. package/server/templates/account-unlock-email.ts +65 -0
  190. package/server/templates/invitation-email.ts +66 -0
  191. package/server/templates/reset-password-email.ts +65 -0
  192. package/server/templates/verification-email.ts +66 -0
  193. package/server/types.ts +21 -0
  194. package/server/utils/accepts.ts +11 -0
  195. package/server/utils/access-token-cookie.ts +61 -0
  196. package/server/utils/check-permission.ts +52 -0
  197. package/server/utils/check-user-belongs-domain.ts +19 -0
  198. package/server/utils/check-user-has-role.ts +29 -0
  199. package/server/utils/encrypt-state.ts +22 -0
  200. package/server/utils/get-aes-256-key.ts +13 -0
  201. package/server/utils/get-domain-from-hostname.ts +7 -0
  202. package/server/utils/get-domain-users.ts +38 -0
  203. package/server/utils/get-secret.ts +13 -0
  204. package/server/utils/get-user-domains.ts +112 -0
  205. package/translations/en.json +1 -5
  206. package/translations/ja.json +1 -5
  207. package/translations/ko.json +3 -6
  208. package/translations/ms.json +1 -5
  209. package/translations/zh.json +1 -5
  210. package/dist-client/verify-webauthn.d.ts +0 -13
  211. package/dist-client/verify-webauthn.js +0 -72
  212. package/dist-client/verify-webauthn.js.map +0 -1
@@ -0,0 +1,87 @@
1
+ import Router from 'koa-router'
2
+ import { getRepository } from '@things-factory/shell'
3
+ import { appPackage } from '@things-factory/env'
4
+
5
+ import { generateRegistrationOptions, generateAuthenticationOptions } from '@simplewebauthn/server'
6
+
7
+ import { WebAuthCredential } from '../service/web-auth-credential/web-auth-credential'
8
+ import {
9
+ PublicKeyCredentialCreationOptionsJSON,
10
+ } from '@simplewebauthn/server/script/deps'
11
+ import { setAccessTokenCookie } from '../utils/access-token-cookie'
12
+ import { createWebAuthnMiddleware } from '../middlewares/webauthn-middleware';
13
+
14
+ export const webAuthnGlobalPublicRouter = new Router()
15
+ export const webAuthnGlobalPrivateRouter = new Router()
16
+
17
+ const { name: rpName } = appPackage as any
18
+
19
+ webAuthnGlobalPrivateRouter.get('/auth/register-webauthn/challenge', async (context, next) => {
20
+ const { user } = context.state
21
+ const rpID = context.hostname
22
+
23
+ const webAuthCredentials = await getRepository(WebAuthCredential).find({
24
+ where: {
25
+ user: { id: user.id }
26
+ }
27
+ })
28
+
29
+ const options: PublicKeyCredentialCreationOptionsJSON = await generateRegistrationOptions({
30
+ rpName,
31
+ rpID,
32
+ userName: user.email,
33
+ userDisplayName: user.name,
34
+ // Don't prompt users for additional information about the authenticator
35
+ // (Recommended for smoother UX)
36
+ attestationType: 'none',
37
+ // Prevent users from re-registering existing authenticators
38
+ excludeCredentials: webAuthCredentials.map(credential => ({
39
+ id: credential.credentialId
40
+ // Optional
41
+ // transports: credential.transports
42
+ })),
43
+ authenticatorSelection: {
44
+ // Defaults
45
+ residentKey: 'preferred',
46
+ userVerification: 'preferred',
47
+ // Optional
48
+ authenticatorAttachment: 'platform'
49
+ }
50
+ })
51
+
52
+ context.session.challenge = options.challenge
53
+ context.body = options
54
+ })
55
+
56
+ webAuthnGlobalPrivateRouter.post('/auth/verify-registration', createWebAuthnMiddleware('webauthn-register'));
57
+
58
+ webAuthnGlobalPublicRouter.get('/auth/signin-webauthn/challenge', async (context, next) => {
59
+ const rpID = context.hostname
60
+
61
+ const options = await generateAuthenticationOptions({
62
+ rpID,
63
+ userVerification: 'preferred'
64
+ })
65
+
66
+ context.session.challenge = options.challenge
67
+ context.body = options
68
+ })
69
+
70
+ webAuthnGlobalPublicRouter.post(
71
+ '/auth/signin-webauthn', createWebAuthnMiddleware('webauthn-login'),
72
+ async (context, next) => {
73
+ const { domain, user } = context. state
74
+ const { request } = context
75
+ const { body: reqBody } = request
76
+
77
+ const token = await user.sign({ subdomain: domain?.subdomain })
78
+ setAccessTokenCookie(context, token)
79
+
80
+ var redirectURL = `/auth/checkin${domain ? '/' + domain.subdomain : ''}?redirect_to=${encodeURIComponent(reqBody.redirectTo || '/')}`
81
+
82
+ /* 2단계 인터렉션 때문에 브라우저에서 fetch(...)로 진행될 것이므로, redirect(3xx) 응답으로 처리할 수 없다. 따라서, 데이타로 redirectURL를 응답한다. */
83
+ context.body = { redirectURL, verified: true }
84
+
85
+ await next();
86
+ }
87
+ )
@@ -0,0 +1,80 @@
1
+ import { config } from '@things-factory/env'
2
+
3
+ import { domainAuthenticateMiddleware, jwtAuthenticateMiddleware } from './middlewares'
4
+ import {
5
+ authCheckinRouter,
6
+ authPrivateProcessRouter,
7
+ authPublicProcessRouter,
8
+ authSigninRouter,
9
+ authSignupRouter,
10
+ oauth2AuthorizeRouter,
11
+ oauth2Router,
12
+ pathBaseDomainRouter,
13
+ siteRootRouter,
14
+ webAuthnGlobalPublicRouter,
15
+ webAuthnGlobalPrivateRouter
16
+ } from './router'
17
+
18
+ import { setAccessTokenCookie } from './utils/access-token-cookie'
19
+
20
+ const isPathBaseDomain = !config.get('subdomain') && !config.get('useVirtualHostBasedDomain')
21
+
22
+ process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRouter) => {
23
+ globalPublicRouter.use(siteRootRouter.routes(), siteRootRouter.allowedMethods())
24
+ globalPublicRouter.use(authPublicProcessRouter.routes(), authPublicProcessRouter.allowedMethods())
25
+
26
+ /* ssoMiddleware가 정의되어있다면, /auth/sso-signin 패스를 활성화한다. */
27
+ if (app.ssoMiddlewares.length > 0) {
28
+ authSigninRouter.get('/auth/sso-signin', app.ssoMiddlewares[0], async context => {
29
+ const { user } = context.state
30
+
31
+ const token = await user.sign()
32
+ setAccessTokenCookie(context, token)
33
+
34
+ context.redirect('/auth/checkin')
35
+ })
36
+ }
37
+ })
38
+
39
+ process.on('bootstrap-module-global-private-route' as any, (app, globalPrivateRouter) => {
40
+ globalPrivateRouter.use(jwtAuthenticateMiddleware)
41
+
42
+ /* globalPrivateRouter based nested-routers */
43
+ globalPrivateRouter.use(authCheckinRouter.routes(), authCheckinRouter.allowedMethods())
44
+ globalPrivateRouter.use(authPrivateProcessRouter.routes(), authPrivateProcessRouter.allowedMethods())
45
+ globalPrivateRouter.use(webAuthnGlobalPrivateRouter.routes(), webAuthnGlobalPrivateRouter.allowedMethods())
46
+ })
47
+
48
+ process.on('bootstrap-module-domain-public-route' as any, (app, domainPublicRouter) => {
49
+ /* domainPublicRouter based nested-routers */
50
+ domainPublicRouter.use(authSigninRouter.routes(), authSigninRouter.allowedMethods())
51
+ domainPublicRouter.use(authSignupRouter.routes(), authSignupRouter.allowedMethods())
52
+ domainPublicRouter.use(webAuthnGlobalPublicRouter.routes(), webAuthnGlobalPublicRouter.allowedMethods())
53
+
54
+ /* path '/admin/oauth/...' is deprecated. should use path '/oauth/...' for oauth2 related routing */
55
+ domainPublicRouter.use('/oauth', oauth2Router.routes(), oauth2Router.allowedMethods()) // if i use context
56
+ })
57
+
58
+ process.on('bootstrap-module-domain-private-route' as any, (app, domainPrivateRouter) => {
59
+ domainPrivateRouter.use(jwtAuthenticateMiddleware)
60
+ domainPrivateRouter.use(domainAuthenticateMiddleware)
61
+
62
+ /* domainPrivateRouter based nested-routers */
63
+ if (isPathBaseDomain) {
64
+ // pathBaseDomainRouter는 history-fallback의 경우에 인증 처리를 하기 위한 라우터이다.
65
+ // (보통, URL 링크등을 통해서 domain path URL로 바로 요청하는 경우에 해당한다.)
66
+ // pathBaseDomainRouter는 domain path를 domain-private-router를 사용하는 것을 전제로 한다.
67
+ domainPrivateRouter.use('/domain/:domain/oauth', oauth2AuthorizeRouter.routes(), oauth2AuthorizeRouter.allowedMethods())
68
+ domainPrivateRouter.use('/domain', pathBaseDomainRouter.routes(), pathBaseDomainRouter.allowedMethods())
69
+ }
70
+
71
+ // Client Routing : path 확장자가 없는 경우는 대부분 client 라우팅에 해당한다.
72
+ // 즉, browser-history-fallback 으로 index.html을 send 하는 경우에, 사용자 로그인이 필요한 경우에,
73
+ // 화면깜박임없이 signin page로 redirect 하고자하는 목적의 설정이다.
74
+ // domain-private 라우트를 통과하고 싶지 않다면, regexp를 조정한다.
75
+ // '(.[^.]+)' 은 '', '/'는 제외하고, '/xxx', '/yyy/zzz' 등 모두를 포함하지만, path에 '.'가 있는 경우는 제외한다.
76
+ // (테스트는 여기서 : http://forbeslindesay.github.io/express-route-tester/)
77
+ domainPrivateRouter.get('(.[^.]+)', async (context, next) => {
78
+ await next()
79
+ })
80
+ })
@@ -0,0 +1,22 @@
1
+ import { Arg, Ctx, Mutation, Resolver } from 'type-graphql'
2
+
3
+ import { getRepository } from '@things-factory/shell'
4
+
5
+ import { User } from '../user/user'
6
+ import { AppBinding } from './app-binding'
7
+
8
+ @Resolver(AppBinding)
9
+ export class AppBindingMutation {
10
+ @Mutation(returns => Boolean)
11
+ async deleteAppBinding(@Arg('id') id: string, @Ctx() context: ResolverContext) {
12
+ const { domain } = context.state
13
+
14
+ // TODO 이 사용자가 이 도메인에 속한 사용자인지 확인해야함.
15
+ // TODO 다른 도메인에도 포함되어있다면, domains-users 관게와 해당 도메인 관련 정보만 삭제해야 함.
16
+ await getRepository(User).delete({
17
+ id
18
+ })
19
+
20
+ return true
21
+ }
22
+ }
@@ -0,0 +1,92 @@
1
+ import { Arg, Args, Ctx, FieldResolver, Query, Resolver, Root } from 'type-graphql'
2
+ import { SelectQueryBuilder } from 'typeorm'
3
+
4
+ import { buildQuery, getRepository, ListParam } from '@things-factory/shell'
5
+
6
+ import { buildDomainUsersQueryBuilder } from '../../utils/get-domain-users'
7
+ import { Application } from '../application/application'
8
+ import { User } from '../user/user'
9
+ import { UserList } from '../user/user-types'
10
+ import { AppBinding } from './app-binding'
11
+ import { AppBindingList } from './app-binding-types'
12
+
13
+ @Resolver(AppBinding)
14
+ export class AppBindingQuery {
15
+ @Query(returns => AppBinding)
16
+ async appBinding(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<User> {
17
+ const { domain } = context.state
18
+
19
+ // TODO should check domain is available
20
+ return await getRepository(User).findOneBy({ id, userType: 'application' })
21
+ }
22
+
23
+ /* TODO optimize query */
24
+ @Query(returns => AppBindingList)
25
+ async appBindings(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<UserList> {
26
+ const { domain } = context.state
27
+
28
+ // const convertedParams = convertListParams(params)
29
+ // convertedParams.where = {
30
+ // ...convertedParams.where,
31
+ // userType: 'application'
32
+ // } as any
33
+
34
+ const alias: string = 'USER'
35
+ const qb: SelectQueryBuilder<User> = buildDomainUsersQueryBuilder(domain.id, alias)
36
+ buildQuery(qb, params, null, { domainRef: false })
37
+ var [items] = await qb
38
+ // .leftJoinAndSelect(`${alias}.roles`, 'ROLES')
39
+ // .leftJoinAndSelect(`${alias}.creator`, 'CREATOR')
40
+ // .leftJoinAndSelect(`${alias}.updater`, 'UPDATER')
41
+ .getManyAndCount()
42
+
43
+ items = items.filter((user: User) => user.userType == 'application')
44
+
45
+ // var boundApps = await Promise.all(
46
+ // items
47
+ // .filter((user: User) => user.userType == 'application')
48
+ // .map(async (user: User) => {
49
+ // const email = user.email
50
+ // const appKey = email.substr(0, email.lastIndexOf('@'))
51
+ // const application = await getRepository(Application).findOneBy({
52
+ // appKey
53
+ // })
54
+
55
+ // return {
56
+ // ...user,
57
+ // application,
58
+ // scope: user.roles.map(role => role.name).join(','),
59
+ // refreshToken: user.password
60
+ // }
61
+ // })
62
+ // )
63
+
64
+ return { items, total: items.length }
65
+ }
66
+
67
+ @FieldResolver(type => Application)
68
+ async application(@Root() appBinding: AppBinding): Promise<Application> {
69
+ return await getRepository(Application).findOneBy({ id: appBinding.reference })
70
+ }
71
+
72
+ @FieldResolver(type => String)
73
+ async scope(@Root() appBinding: AppBinding): Promise<string> {
74
+ const u = await getRepository(User).findOne({ where: { reference: appBinding.reference }, relations: ['roles'] })
75
+ return u.roles.map(role => role.name).join(',')
76
+ }
77
+
78
+ @FieldResolver(type => String)
79
+ async refreshToken(@Root() appBinding: AppBinding): Promise<string> {
80
+ return appBinding.password
81
+ }
82
+
83
+ @FieldResolver(type => User)
84
+ async updater(@Root() appBinding: AppBinding): Promise<User> {
85
+ return await getRepository(User).findOneBy({ id: appBinding.updaterId })
86
+ }
87
+
88
+ @FieldResolver(type => User)
89
+ async creator(@Root() appBinding: AppBinding): Promise<User> {
90
+ return await getRepository(User).findOneBy({ id: appBinding.creatorId })
91
+ }
92
+ }
@@ -0,0 +1,11 @@
1
+ import { Field, Int, ObjectType } from 'type-graphql'
2
+ import { AppBinding } from './app-binding'
3
+
4
+ @ObjectType()
5
+ export class AppBindingList {
6
+ @Field(type => [AppBinding], { nullable: true })
7
+ items?: [AppBinding]
8
+
9
+ @Field(type => Int, { nullable: true })
10
+ total?: number
11
+ }
@@ -0,0 +1,17 @@
1
+ import { ObjectType, Field, Directive } from 'type-graphql'
2
+ import { Domain } from '@things-factory/shell'
3
+ import { Application } from '../application/application'
4
+ import { User, UserStatus } from '../user/user'
5
+
6
+ @ObjectType()
7
+ export class AppBinding extends User {
8
+ @Field({ nullable: true })
9
+ application: Application
10
+
11
+ @Field({ nullable: true })
12
+ scope: string
13
+
14
+ @Field({ nullable: true })
15
+ @Directive('@privilege(category: "security", privilege: "query", domainOwnerGranted: true)')
16
+ refreshToken: string
17
+ }
@@ -0,0 +1,4 @@
1
+ import { AppBindingQuery } from './app-binding-query'
2
+ import { AppBindingMutation } from './app-binding-mutation'
3
+
4
+ export const resolvers = [AppBindingQuery, AppBindingMutation]
@@ -0,0 +1,113 @@
1
+ import { Directive, Arg, Ctx, Mutation, Resolver } from 'type-graphql'
2
+
3
+ import { getRepository } from '@things-factory/shell'
4
+
5
+ import { User, UserStatus } from '../user/user'
6
+ import { Appliance } from './appliance'
7
+ import { AppliancePatch, NewAppliance } from './appliance-types'
8
+
9
+ const crypto = require('crypto')
10
+
11
+ @Resolver(Appliance)
12
+ export class ApplianceMutation {
13
+ @Directive('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)')
14
+ @Mutation(returns => Appliance, { description: 'To create new appliance' })
15
+ async createAppliance(
16
+ @Arg('appliance') appliance: NewAppliance,
17
+ @Ctx() context: ResolverContext
18
+ ): Promise<Appliance> {
19
+ return await getRepository(Appliance).save({
20
+ domain: context.state.domain,
21
+ creator: context.state.user,
22
+ updater: context.state.user,
23
+ ...appliance
24
+ })
25
+ }
26
+
27
+ @Directive('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)')
28
+ @Mutation(returns => Boolean, { description: 'To delete appliance' })
29
+ async deleteAppliance(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Boolean> {
30
+ const { domain } = context.state
31
+ // TODO 이 사용자가 이 도메인에 속한 사용자인지 확인해야함.
32
+ // TODO 다른 도메인에도 포함되어있다면, domains-users 관게와 해당 도메인 관련 정보만 삭제해야 함.
33
+ await getRepository(User).delete({
34
+ reference: id,
35
+ userType: 'appliance'
36
+ })
37
+
38
+ await getRepository(Appliance).delete({ domain: { id: domain.id }, id })
39
+
40
+ return true
41
+ }
42
+
43
+ @Directive('@privilege(category: "security", privilege: "mutation", domainOwnerGranted: true)')
44
+ @Mutation(returns => Appliance)
45
+ async generateApplianceSecret(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Appliance> {
46
+ const { domain, user } = context.state
47
+
48
+ const appliance: Appliance = await getRepository(Appliance).findOneBy({ domain: { id: domain.id }, id })
49
+
50
+ const appuserEmail = `${crypto.randomUUID()}@${domain?.subdomain}`
51
+ let appuser: User = await getRepository(User).findOne({
52
+ where: {
53
+ reference: id,
54
+ userType: 'appliance'
55
+ },
56
+ relations: ['domains']
57
+ })
58
+
59
+ if (!appuser) {
60
+ /* newly create appuser */
61
+ appuser = await getRepository(User).save({
62
+ email: appuserEmail,
63
+ name: appliance.name,
64
+ userType: 'appliance',
65
+ reference: id,
66
+ status: UserStatus.ACTIVATED,
67
+ domains: [domain],
68
+ updater: user,
69
+ creator: user
70
+ })
71
+ }
72
+
73
+ if (!appuser.domains.find(d => d.id === domain.id)) {
74
+ context.throw(401, 'appliance is not allowed for this domain')
75
+ }
76
+
77
+ appuser.password = Appliance.generateAccessToken(domain, appuser, appliance)
78
+
79
+ await getRepository(User).save(appuser)
80
+
81
+ return await getRepository(Appliance).save({
82
+ ...appliance,
83
+ accessToken: appuser.password,
84
+ updater: user
85
+ })
86
+ }
87
+
88
+ @Directive('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)')
89
+ @Mutation(returns => Appliance)
90
+ async updateAppliance(
91
+ @Arg('id') id: string,
92
+ @Arg('patch') patch: AppliancePatch,
93
+ @Ctx() context: ResolverContext
94
+ ): Promise<Appliance> {
95
+ const { domain } = context.state
96
+
97
+ const applianceRepository = getRepository(Appliance)
98
+ const userRepository = getRepository(User)
99
+ const appliance = await applianceRepository.findOne({ where: { domain: { id: domain.id }, id } })
100
+ const user = await userRepository.findOne({ where: { reference: id, userType: 'appliance' } })
101
+
102
+ userRepository.save({
103
+ ...user,
104
+ name: patch?.name || user.name
105
+ })
106
+
107
+ return await applianceRepository.save({
108
+ ...appliance,
109
+ ...patch,
110
+ updater: context.state.user
111
+ })
112
+ }
113
+ }
@@ -0,0 +1,76 @@
1
+ import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'
2
+
3
+ import { getQueryBuilderFromListParams, Domain, getRepository, ListParam } from '@things-factory/shell'
4
+
5
+ import { Appliance } from '../appliance/appliance'
6
+ import { User } from '../user/user'
7
+ import { ApplianceList } from './appliance-types'
8
+
9
+ @Resolver(Appliance)
10
+ export class ApplianceQuery {
11
+ @Directive('@privilege(category: "user", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
12
+ @Query(returns => Appliance, { description: ' To fetch appliance' })
13
+ async appliance(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Appliance> {
14
+ const { domain } = context.state
15
+ return await getRepository(Appliance).findOneBy({ domain: { id: domain.id }, id })
16
+ }
17
+
18
+ @Directive('@privilege(category: "user", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
19
+ @Query(returns => ApplianceList, { description: 'To fetch multiple appliance' })
20
+ async appliances(
21
+ @Args(type => ListParam) params: ListParam,
22
+ @Ctx() context: ResolverContext
23
+ ): Promise<ApplianceList> {
24
+ const { domain } = context.state
25
+
26
+ const queryBuilder = getQueryBuilderFromListParams({
27
+ domain,
28
+ params,
29
+ repository: getRepository(Appliance),
30
+ alias: 'appliance',
31
+ searchables: ['name', 'description']
32
+ })
33
+
34
+ const [items, total] = await queryBuilder.getManyAndCount()
35
+
36
+ return { items, total }
37
+ }
38
+
39
+ @Directive('@privilege(category: "user", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
40
+ @Query(returns => ApplianceList, { description: 'To fetch multiple appliance' })
41
+ async edges(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<ApplianceList> {
42
+ const { domain } = context.state
43
+
44
+ const queryBuilder = getQueryBuilderFromListParams({
45
+ domain,
46
+ params,
47
+ repository: getRepository(Appliance),
48
+ alias: 'appliance',
49
+ searchables: ['name', 'description']
50
+ })
51
+
52
+ const [items, total] = await queryBuilder.getManyAndCount()
53
+
54
+ return { items, total }
55
+ }
56
+
57
+ @FieldResolver(type => String)
58
+ async accessToken(@Root() appliance: Appliance, @Ctx() context: ResolverContext) {
59
+ return appliance.accessToken
60
+ }
61
+
62
+ @FieldResolver(type => Domain)
63
+ async domain(@Ctx() context: ResolverContext) {
64
+ return context.state.domain
65
+ }
66
+
67
+ @FieldResolver(type => User)
68
+ async updater(@Root() appliance: Appliance): Promise<User> {
69
+ return await getRepository(User).findOneBy({ id: appliance.updaterId })
70
+ }
71
+
72
+ @FieldResolver(type => User)
73
+ async creator(@Root() appliance: Appliance): Promise<User> {
74
+ return await getRepository(User).findOneBy({ id: appliance.creatorId })
75
+ }
76
+ }
@@ -0,0 +1,56 @@
1
+ import { ObjectType, InputType, Field, ID, Int } from 'type-graphql'
2
+ import { Appliance } from './appliance'
3
+
4
+ @ObjectType()
5
+ export class ApplianceList {
6
+ @Field(type => [Appliance], { nullable: true })
7
+ items?: Appliance[]
8
+
9
+ @Field(type => Int, { nullable: true })
10
+ total?: number
11
+ }
12
+
13
+ @InputType()
14
+ export class AppliancePatch {
15
+ @Field(type => ID, { nullable: true })
16
+ id?: string
17
+
18
+ @Field({ nullable: true })
19
+ serialNo?: string
20
+
21
+ @Field({ nullable: true })
22
+ name?: string
23
+
24
+ @Field({ nullable: true })
25
+ brand?: string
26
+
27
+ @Field({ nullable: true })
28
+ model?: string
29
+
30
+ @Field({ nullable: true })
31
+ description?: string
32
+
33
+ @Field({ nullable: true })
34
+ netmask?: string
35
+ }
36
+
37
+ @InputType()
38
+ export class NewAppliance {
39
+ @Field()
40
+ serialNo: string
41
+
42
+ @Field()
43
+ name: string
44
+
45
+ @Field()
46
+ brand: string
47
+
48
+ @Field()
49
+ model: string
50
+
51
+ @Field({ nullable: true })
52
+ description?: string
53
+
54
+ @Field({ nullable: true })
55
+ netmask?: string
56
+ }