@things-factory/auth-base 8.0.0-beta.9 → 8.0.2
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.
- package/client/actions/auth.ts +24 -0
- package/client/auth.ts +272 -0
- package/client/bootstrap.ts +47 -0
- package/client/directive/privileged.ts +28 -0
- package/client/index.ts +3 -0
- package/client/profiled.ts +83 -0
- package/client/reducers/auth.ts +31 -0
- package/dist-client/index.d.ts +0 -1
- package/dist-client/index.js +0 -1
- package/dist-client/index.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/constants/error-code.d.ts +0 -2
- package/dist-server/constants/error-code.js +1 -3
- package/dist-server/constants/error-code.js.map +1 -1
- package/dist-server/controllers/change-pwd.js +2 -2
- package/dist-server/controllers/change-pwd.js.map +1 -1
- package/dist-server/controllers/delete-user.js +12 -13
- package/dist-server/controllers/delete-user.js.map +1 -1
- package/dist-server/controllers/invitation.d.ts +1 -2
- package/dist-server/controllers/invitation.js +5 -30
- package/dist-server/controllers/invitation.js.map +1 -1
- package/dist-server/controllers/profile.d.ts +3 -4
- package/dist-server/controllers/profile.js +2 -20
- package/dist-server/controllers/profile.js.map +1 -1
- package/dist-server/controllers/signin.d.ts +1 -4
- package/dist-server/controllers/signin.js +1 -17
- package/dist-server/controllers/signin.js.map +1 -1
- package/dist-server/controllers/signup.js +4 -13
- package/dist-server/controllers/signup.js.map +1 -1
- package/dist-server/controllers/unlock-user.js +0 -1
- package/dist-server/controllers/unlock-user.js.map +1 -1
- package/dist-server/controllers/verification.js +0 -1
- package/dist-server/controllers/verification.js.map +1 -1
- package/dist-server/middlewares/signin-middleware.js +4 -9
- package/dist-server/middlewares/signin-middleware.js.map +1 -1
- package/dist-server/middlewares/webauthn-middleware.js.map +1 -1
- package/dist-server/migrations/1548206416130-SeedUser.js +1 -2
- package/dist-server/migrations/1548206416130-SeedUser.js.map +1 -1
- package/dist-server/router/auth-checkin-router.js +2 -8
- package/dist-server/router/auth-checkin-router.js.map +1 -1
- package/dist-server/router/auth-private-process-router.js +7 -12
- package/dist-server/router/auth-private-process-router.js.map +1 -1
- package/dist-server/router/auth-public-process-router.js +9 -20
- package/dist-server/router/auth-public-process-router.js.map +1 -1
- package/dist-server/router/auth-signin-router.js +3 -3
- package/dist-server/router/auth-signin-router.js.map +1 -1
- package/dist-server/router/webauthn-router.js +1 -51
- package/dist-server/router/webauthn-router.js.map +1 -1
- package/dist-server/service/invitation/invitation-mutation.d.ts +2 -3
- package/dist-server/service/invitation/invitation-mutation.js +8 -20
- package/dist-server/service/invitation/invitation-mutation.js.map +1 -1
- package/dist-server/service/user/user-mutation.d.ts +9 -10
- package/dist-server/service/user/user-mutation.js +54 -112
- package/dist-server/service/user/user-mutation.js.map +1 -1
- package/dist-server/service/user/user-types.d.ts +0 -1
- package/dist-server/service/user/user-types.js +0 -4
- package/dist-server/service/user/user-types.js.map +1 -1
- package/dist-server/service/user/user.d.ts +0 -1
- package/dist-server/service/user/user.js +14 -40
- package/dist-server/service/user/user.js.map +1 -1
- package/dist-server/templates/account-unlock-email.d.ts +1 -2
- package/dist-server/templates/account-unlock-email.js +1 -1
- package/dist-server/templates/account-unlock-email.js.map +1 -1
- package/dist-server/templates/invitation-email.d.ts +1 -2
- package/dist-server/templates/invitation-email.js +1 -1
- package/dist-server/templates/invitation-email.js.map +1 -1
- package/dist-server/templates/verification-email.d.ts +1 -2
- package/dist-server/templates/verification-email.js +1 -1
- package/dist-server/templates/verification-email.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/server/constants/error-code.ts +20 -0
- package/server/constants/error-message.ts +0 -0
- package/server/constants/max-age.ts +1 -0
- package/server/controllers/auth.ts +5 -0
- package/server/controllers/change-pwd.ts +99 -0
- package/server/controllers/checkin.ts +21 -0
- package/server/controllers/delete-user.ts +68 -0
- package/server/controllers/invitation.ts +132 -0
- package/server/controllers/profile.ts +28 -0
- package/server/controllers/reset-password.ts +126 -0
- package/server/controllers/signin.ts +79 -0
- package/server/controllers/signup.ts +60 -0
- package/server/controllers/unlock-user.ts +61 -0
- package/server/controllers/utils/make-invitation-token.ts +5 -0
- package/server/controllers/utils/make-verification-token.ts +4 -0
- package/server/controllers/utils/password-rule.ts +120 -0
- package/server/controllers/utils/save-invitation-token.ts +10 -0
- package/server/controllers/utils/save-verification-token.ts +12 -0
- package/server/controllers/verification.ts +83 -0
- package/server/errors/auth-error.ts +24 -0
- package/server/errors/index.ts +2 -0
- package/server/errors/user-domain-not-match-error.ts +29 -0
- package/server/index.ts +37 -0
- package/server/middlewares/authenticate-401-middleware.ts +114 -0
- package/server/middlewares/domain-authenticate-middleware.ts +78 -0
- package/server/middlewares/graphql-authenticate-middleware.ts +13 -0
- package/server/middlewares/index.ts +67 -0
- package/server/middlewares/jwt-authenticate-middleware.ts +84 -0
- package/server/middlewares/signin-middleware.ts +55 -0
- package/server/middlewares/webauthn-middleware.ts +127 -0
- package/server/migrations/1548206416130-SeedUser.ts +59 -0
- package/server/migrations/1566805283882-SeedPrivilege.ts +28 -0
- package/server/migrations/index.ts +9 -0
- package/server/router/auth-checkin-router.ts +107 -0
- package/server/router/auth-private-process-router.ts +107 -0
- package/server/router/auth-public-process-router.ts +302 -0
- package/server/router/auth-signin-router.ts +55 -0
- package/server/router/auth-signup-router.ts +95 -0
- package/server/router/index.ts +9 -0
- package/server/router/oauth2/index.ts +2 -0
- package/server/router/oauth2/oauth2-authorize-router.ts +81 -0
- package/server/router/oauth2/oauth2-router.ts +165 -0
- package/server/router/oauth2/oauth2-server.ts +262 -0
- package/server/router/oauth2/passport-oauth2-client-password.ts +87 -0
- package/server/router/oauth2/passport-refresh-token.ts +87 -0
- package/server/router/path-base-domain-router.ts +8 -0
- package/server/router/site-root-router.ts +48 -0
- package/server/router/webauthn-router.ts +87 -0
- package/server/routes.ts +80 -0
- package/server/service/app-binding/app-binding-mutation.ts +22 -0
- package/server/service/app-binding/app-binding-query.ts +92 -0
- package/server/service/app-binding/app-binding-types.ts +11 -0
- package/server/service/app-binding/app-binding.ts +17 -0
- package/server/service/app-binding/index.ts +4 -0
- package/server/service/appliance/appliance-mutation.ts +113 -0
- package/server/service/appliance/appliance-query.ts +76 -0
- package/server/service/appliance/appliance-types.ts +56 -0
- package/server/service/appliance/appliance.ts +133 -0
- package/server/service/appliance/index.ts +6 -0
- package/server/service/application/application-mutation.ts +104 -0
- package/server/service/application/application-query.ts +98 -0
- package/server/service/application/application-types.ts +76 -0
- package/server/service/application/application.ts +216 -0
- package/server/service/application/index.ts +6 -0
- package/server/service/auth-provider/auth-provider-mutation.ts +159 -0
- package/server/service/auth-provider/auth-provider-parameter-spec.ts +24 -0
- package/server/service/auth-provider/auth-provider-query.ts +88 -0
- package/server/service/auth-provider/auth-provider-type.ts +67 -0
- package/server/service/auth-provider/auth-provider.ts +155 -0
- package/server/service/auth-provider/index.ts +7 -0
- package/server/service/domain-generator/domain-generator-mutation.ts +117 -0
- package/server/service/domain-generator/domain-generator-types.ts +46 -0
- package/server/service/domain-generator/index.ts +3 -0
- package/server/service/granted-role/granted-role-mutation.ts +156 -0
- package/server/service/granted-role/granted-role-query.ts +60 -0
- package/server/service/granted-role/granted-role.ts +27 -0
- package/server/service/granted-role/index.ts +6 -0
- package/server/service/index.ts +90 -0
- package/server/service/invitation/index.ts +6 -0
- package/server/service/invitation/invitation-mutation.ts +63 -0
- package/server/service/invitation/invitation-query.ts +33 -0
- package/server/service/invitation/invitation-types.ts +11 -0
- package/server/service/invitation/invitation.ts +63 -0
- package/server/service/login-history/index.ts +5 -0
- package/server/service/login-history/login-history-query.ts +51 -0
- package/server/service/login-history/login-history-type.ts +12 -0
- package/server/service/login-history/login-history.ts +45 -0
- package/server/service/partner/index.ts +6 -0
- package/server/service/partner/partner-mutation.ts +61 -0
- package/server/service/partner/partner-query.ts +102 -0
- package/server/service/partner/partner-types.ts +11 -0
- package/server/service/partner/partner.ts +57 -0
- package/server/service/password-history/index.ts +3 -0
- package/server/service/password-history/password-history.ts +16 -0
- package/server/service/privilege/index.ts +6 -0
- package/server/service/privilege/privilege-directive.ts +77 -0
- package/server/service/privilege/privilege-mutation.ts +92 -0
- package/server/service/privilege/privilege-query.ts +94 -0
- package/server/service/privilege/privilege-types.ts +60 -0
- package/server/service/privilege/privilege.ts +102 -0
- package/server/service/role/index.ts +6 -0
- package/server/service/role/role-mutation.ts +109 -0
- package/server/service/role/role-query.ts +155 -0
- package/server/service/role/role-types.ts +81 -0
- package/server/service/role/role.ts +72 -0
- package/server/service/user/domain-query.ts +24 -0
- package/server/service/user/index.ts +7 -0
- package/server/service/user/user-mutation.ts +413 -0
- package/server/service/user/user-query.ts +145 -0
- package/server/service/user/user-types.ts +97 -0
- package/server/service/user/user.ts +354 -0
- package/server/service/users-auth-providers/index.ts +5 -0
- package/server/service/users-auth-providers/users-auth-providers.ts +71 -0
- package/server/service/verification-token/index.ts +3 -0
- package/server/service/verification-token/verification-token.ts +60 -0
- package/server/service/web-auth-credential/index.ts +3 -0
- package/server/service/web-auth-credential/web-auth-credential.ts +67 -0
- package/server/templates/account-unlock-email.ts +65 -0
- package/server/templates/invitation-email.ts +66 -0
- package/server/templates/reset-password-email.ts +65 -0
- package/server/templates/verification-email.ts +66 -0
- package/server/types.ts +21 -0
- package/server/utils/accepts.ts +11 -0
- package/server/utils/access-token-cookie.ts +61 -0
- package/server/utils/check-permission.ts +52 -0
- package/server/utils/check-user-belongs-domain.ts +19 -0
- package/server/utils/check-user-has-role.ts +29 -0
- package/server/utils/encrypt-state.ts +22 -0
- package/server/utils/get-aes-256-key.ts +13 -0
- package/server/utils/get-domain-from-hostname.ts +7 -0
- package/server/utils/get-domain-users.ts +38 -0
- package/server/utils/get-secret.ts +13 -0
- package/server/utils/get-user-domains.ts +112 -0
- package/translations/en.json +1 -5
- package/translations/ja.json +1 -5
- package/translations/ko.json +3 -6
- package/translations/ms.json +1 -5
- package/translations/zh.json +1 -5
- package/dist-client/verify-webauthn.d.ts +0 -13
- package/dist-client/verify-webauthn.js +0 -72
- package/dist-client/verify-webauthn.js.map +0 -1
@@ -0,0 +1,65 @@
|
|
1
|
+
export function getResetPasswordEmailForm({ name, resetUrl }) {
|
2
|
+
return `
|
3
|
+
<html lang="en">
|
4
|
+
<head>
|
5
|
+
<meta charset="utf-8" />
|
6
|
+
|
7
|
+
<title>reset password</title>
|
8
|
+
<meta name="description" content="Password Reset" />
|
9
|
+
<meta name="author" content="hatiolab" />
|
10
|
+
<meta name="google" content="notranslate"/>
|
11
|
+
</head>
|
12
|
+
|
13
|
+
<body>
|
14
|
+
<div style="background-color:#f6f6f6">
|
15
|
+
<!--header begin-->
|
16
|
+
<div style="background-color:#fff;padding:0 10px;border-top: 2px solid #394e64;">
|
17
|
+
<a href="#" target="_blank"
|
18
|
+
><img
|
19
|
+
src="http://www.hatiolab.com/assets/img/logo-operato.png"
|
20
|
+
style="max-height:50px"
|
21
|
+
/></a>
|
22
|
+
</div>
|
23
|
+
<!--header end-->
|
24
|
+
|
25
|
+
<!--title begin-->
|
26
|
+
<div
|
27
|
+
style="background-color:#22a6a7;padding:12px 10px 10px 10px;min-height:50px;"
|
28
|
+
>
|
29
|
+
<img
|
30
|
+
src="http://www.hatiolab.com/assets/img/icon-mail.png"
|
31
|
+
style="float:left;margin:0 10px 0 40px"
|
32
|
+
/>
|
33
|
+
<span style="display:block;color:#fff;font-size:20px"
|
34
|
+
>Hi ${name}!</span
|
35
|
+
>
|
36
|
+
<span style="display:block;color:#fff;font-size:34px;font-weight:bold"
|
37
|
+
>Reset password</span
|
38
|
+
>
|
39
|
+
</div>
|
40
|
+
<!--title end-->
|
41
|
+
|
42
|
+
<!--body begin-->
|
43
|
+
<p style="padding:10px 20px;line-height:1.5;font-size:16px">
|
44
|
+
Click the button below to reset password.
|
45
|
+
<br />
|
46
|
+
<a
|
47
|
+
href="${resetUrl}"
|
48
|
+
style="display:inline-block;margin:10px 5px 5px 0;border-radius:7px;background-color:#22a6a7;padding:7px 15px;color:#fff;font-size:18px;text-decoration:none;text-transform:capitalize;"
|
49
|
+
>reset password</a
|
50
|
+
>
|
51
|
+
</p>
|
52
|
+
<!--body end-->
|
53
|
+
|
54
|
+
<!--footer begin-->
|
55
|
+
<div
|
56
|
+
style="background-color:#3d5874;padding:7px 20px 5px 20px;font-size:12px;color:#efefef"
|
57
|
+
>
|
58
|
+
© Hatio, Lab. Inc. All rights reserved.
|
59
|
+
</div>
|
60
|
+
<!--footer end-->
|
61
|
+
</div>
|
62
|
+
</body>
|
63
|
+
</html>
|
64
|
+
`
|
65
|
+
}
|
@@ -0,0 +1,66 @@
|
|
1
|
+
export function getVerificationEmailForm({ name, verifyUrl }) {
|
2
|
+
return `
|
3
|
+
<html lang="en">
|
4
|
+
<head>
|
5
|
+
<meta charset="utf-8" />
|
6
|
+
|
7
|
+
<title>Verify your email</title>
|
8
|
+
<meta name="description" content="Email Verification" />
|
9
|
+
<meta name="author" content="hatiolab" />
|
10
|
+
<meta name="google" content="notranslate"/>
|
11
|
+
</head>
|
12
|
+
|
13
|
+
<body>
|
14
|
+
<div style="background-color:#f6f6f6">
|
15
|
+
<!--header begin-->
|
16
|
+
<div style="background-color:#fff;padding:0 10px;border-top: 2px solid #394e64;">
|
17
|
+
<a href="#" target="_blank"
|
18
|
+
><img
|
19
|
+
src="http://www.hatiolab.com/assets/img/logo-operato.png"
|
20
|
+
style="max-height:50px"
|
21
|
+
/></a>
|
22
|
+
</div>
|
23
|
+
<!--header end-->
|
24
|
+
|
25
|
+
<!--title begin-->
|
26
|
+
<div
|
27
|
+
style="background-color:#22a6a7;padding:12px 10px 10px 10px;min-height:50px;"
|
28
|
+
>
|
29
|
+
<img
|
30
|
+
src="http://www.hatiolab.com/assets/img/icon-mail.png"
|
31
|
+
style="float:left;margin:0 10px 0 40px"
|
32
|
+
/>
|
33
|
+
<span style="display:block;color:#fff;font-size:20px"
|
34
|
+
>Hi ${name}!</span
|
35
|
+
>
|
36
|
+
<span style="display:block;color:#fff;font-size:34px;font-weight:bold"
|
37
|
+
>Verify your email</span
|
38
|
+
>
|
39
|
+
</div>
|
40
|
+
<!--title end-->
|
41
|
+
|
42
|
+
<!--body begin-->
|
43
|
+
<p style="padding:10px 20px;line-height:1.5;font-size:16px">
|
44
|
+
You're almost ready to start enjoying Operato.
|
45
|
+
Simply click the button below to verify your email address.
|
46
|
+
<br />
|
47
|
+
<a
|
48
|
+
href="${verifyUrl}"
|
49
|
+
style="display:inline-block;margin:10px 5px 5px 0;border-radius:7px;background-color:#22a6a7;padding:7px 15px;color:#fff;font-size:18px;text-decoration:none;text-transform:capitalize;"
|
50
|
+
>Verify</a
|
51
|
+
>
|
52
|
+
</p>
|
53
|
+
<!--body end-->
|
54
|
+
|
55
|
+
<!--footer begin-->
|
56
|
+
<div
|
57
|
+
style="background-color:#3d5874;padding:7px 20px 5px 20px;font-size:12px;color:#efefef"
|
58
|
+
>
|
59
|
+
© Hatio, Lab. Inc. All rights reserved.
|
60
|
+
</div>
|
61
|
+
<!--footer end-->
|
62
|
+
</div>
|
63
|
+
</body>
|
64
|
+
</html>
|
65
|
+
`
|
66
|
+
}
|
package/server/types.ts
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
import { TFunction } from 'i18next'
|
2
|
+
import { EntityManager } from 'typeorm'
|
3
|
+
|
4
|
+
import { Domain } from '@things-factory/shell'
|
5
|
+
|
6
|
+
import { User } from './service/user/user'
|
7
|
+
|
8
|
+
declare global {
|
9
|
+
export type ResolverContext = {
|
10
|
+
state: IContextState
|
11
|
+
t?: TFunction
|
12
|
+
[key: string]: any
|
13
|
+
}
|
14
|
+
|
15
|
+
interface IContextState {
|
16
|
+
domain: Domain
|
17
|
+
user: User
|
18
|
+
tx?: EntityManager
|
19
|
+
[key: string]: any
|
20
|
+
}
|
21
|
+
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import { getCookieDomainFromHostname } from '@things-factory/shell'
|
2
|
+
import { config } from '@things-factory/env'
|
3
|
+
import { MAX_AGE } from '../constants/max-age'
|
4
|
+
|
5
|
+
const accessTokenCookieKey = config.get('accessTokenCookieKey', 'access_token')
|
6
|
+
|
7
|
+
export function getAccessTokenCookie(context) {
|
8
|
+
return context?.cookies?.get(accessTokenCookieKey)
|
9
|
+
}
|
10
|
+
|
11
|
+
export function setAccessTokenCookie(context, token) {
|
12
|
+
const { secure } = context
|
13
|
+
|
14
|
+
var cookie = {
|
15
|
+
secure,
|
16
|
+
httpOnly: true,
|
17
|
+
maxAge: MAX_AGE,
|
18
|
+
sameSite: 'Lax'
|
19
|
+
}
|
20
|
+
|
21
|
+
const cookieDomain = getCookieDomainFromHostname(context.hostname)
|
22
|
+
if (cookieDomain) {
|
23
|
+
cookie['domain'] = cookieDomain
|
24
|
+
}
|
25
|
+
|
26
|
+
context.cookies.set(accessTokenCookieKey, token, cookie)
|
27
|
+
}
|
28
|
+
|
29
|
+
export function setSessionAccessToken(context) {
|
30
|
+
/* koa-session 을 사용하는 경우에는, cookie 직접 설정이 작동되지 않는다. 그런 경우에는 session에 설정해서 cookie를 변경한다. */
|
31
|
+
const { user } = context.state
|
32
|
+
|
33
|
+
context.session = {
|
34
|
+
id: user.id,
|
35
|
+
userType: user.type,
|
36
|
+
status: user.state
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
export function clearAccessTokenCookie(context) {
|
41
|
+
const { secure } = context
|
42
|
+
|
43
|
+
var cookie = {
|
44
|
+
secure,
|
45
|
+
httpOnly: true,
|
46
|
+
sameSite: 'Lax'
|
47
|
+
}
|
48
|
+
|
49
|
+
const cookieDomain = getCookieDomainFromHostname(context.hostname)
|
50
|
+
if (cookieDomain) {
|
51
|
+
cookie['domain'] = cookieDomain
|
52
|
+
}
|
53
|
+
|
54
|
+
context.cookies.set(accessTokenCookieKey, '', cookie)
|
55
|
+
/*
|
56
|
+
* TODO clear i18next cookie as well - need to support domain
|
57
|
+
* https://github.com/hatiolab/things-factory/issues/70
|
58
|
+
*/
|
59
|
+
context.cookies.set('i18next', '', cookie)
|
60
|
+
context.session = null
|
61
|
+
}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import { Domain } from '@things-factory/shell'
|
2
|
+
import { PrivilegeObject } from '../service/privilege/privilege'
|
3
|
+
import { User } from '../service/user/user'
|
4
|
+
|
5
|
+
export async function checkPermission(
|
6
|
+
privilegeObject: PrivilegeObject,
|
7
|
+
user: User,
|
8
|
+
domain: Domain,
|
9
|
+
unsafeIP?: boolean,
|
10
|
+
prohibitedPrivileges?: { category: string; privilege: string }[]
|
11
|
+
): Promise<boolean> {
|
12
|
+
if (!privilegeObject) {
|
13
|
+
return true
|
14
|
+
}
|
15
|
+
|
16
|
+
const { owner: domainOwnerGranted, super: superUserGranted, category, privilege } = privilegeObject
|
17
|
+
|
18
|
+
if (unsafeIP) {
|
19
|
+
if (privilege && category) {
|
20
|
+
// unsafeIP 상황에서는 ownership granted는 적용되지 않는다.
|
21
|
+
if ((prohibitedPrivileges || []).find(pp => pp.category == category && pp.privilege == privilege)) {
|
22
|
+
return false
|
23
|
+
}
|
24
|
+
|
25
|
+
return await User.hasPrivilege(privilege, category, domain, user)
|
26
|
+
}
|
27
|
+
|
28
|
+
// privilege, category가 설정되지 않은 경우에는 ownership granted가 설정되었다면 허가하지 않는다.
|
29
|
+
return !domainOwnerGranted && !superUserGranted
|
30
|
+
} else {
|
31
|
+
if (!privilege || !category) {
|
32
|
+
// privilege, category가 설정되지 않은 경우에는 ownership granted만을 적용한다.
|
33
|
+
return (
|
34
|
+
(domainOwnerGranted && (await process.domainOwnerGranted(domain, user))) ||
|
35
|
+
(superUserGranted && (await process.superUserGranted(domain, user)))
|
36
|
+
)
|
37
|
+
}
|
38
|
+
|
39
|
+
if (
|
40
|
+
(domainOwnerGranted && (await process.domainOwnerGranted(domain, user))) ||
|
41
|
+
(superUserGranted && (await process.superUserGranted(domain, user)))
|
42
|
+
) {
|
43
|
+
return true
|
44
|
+
}
|
45
|
+
|
46
|
+
if ((prohibitedPrivileges || []).find(pp => pp.category == category && pp.privilege == privilege)) {
|
47
|
+
return false
|
48
|
+
}
|
49
|
+
|
50
|
+
return await User.hasPrivilege(privilege, category, domain, user)
|
51
|
+
}
|
52
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import { Domain, getRepository } from '@things-factory/shell'
|
2
|
+
|
3
|
+
import { User } from '../service/user/user'
|
4
|
+
|
5
|
+
/**
|
6
|
+
* @description Based on domain and user information,
|
7
|
+
* Find out whether the user belongs domain or user has partnership with domain
|
8
|
+
*
|
9
|
+
* @param domain
|
10
|
+
* @param user
|
11
|
+
*/
|
12
|
+
export async function checkUserBelongsDomain(domain: Domain, user: User): Promise<Boolean> {
|
13
|
+
if (!user.domains?.length) {
|
14
|
+
user = await getRepository(User).findOne({ where: { id: user.id }, relations: ['domains'] })
|
15
|
+
}
|
16
|
+
|
17
|
+
const { domains: userDomains }: User = user
|
18
|
+
return Boolean(userDomains.find((userDomain: Domain) => userDomain.id === domain.id))
|
19
|
+
}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { Domain, getRepository } from '@things-factory/shell'
|
2
|
+
|
3
|
+
import { User } from '../service/user/user'
|
4
|
+
import { Role } from 'service'
|
5
|
+
|
6
|
+
/**
|
7
|
+
* @description 사용자가 특정 도메인 또는 상위 도메인에서 특정 역할을 가지고 있는지 확인합니다.
|
8
|
+
*
|
9
|
+
* @param roleId 확인할 역할의 ID
|
10
|
+
* @param domain 역할을 확인할 도메인
|
11
|
+
* @param user 역할을 확인할 사용자
|
12
|
+
*
|
13
|
+
* @returns 사용자가 도메인 또는 상위 도메인에서 역할을 가지고 있는지 여부를 나타내는 boolean을 반환하는 Promise
|
14
|
+
*/
|
15
|
+
export async function checkUserHasRole(roleId: string, domain: Domain, user: User): Promise<Boolean> {
|
16
|
+
if (!roleId) {
|
17
|
+
return true
|
18
|
+
}
|
19
|
+
|
20
|
+
const me = await getRepository(User).findOne({
|
21
|
+
where: { id: user.id },
|
22
|
+
relations: ['roles']
|
23
|
+
})
|
24
|
+
|
25
|
+
return me.roles
|
26
|
+
.filter(role => role.domainId === domain.id || (domain.parentId && role.domainId === domain.parentId))
|
27
|
+
.map(role => role.id)
|
28
|
+
.includes(roleId)
|
29
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import crypto from 'crypto'
|
2
|
+
|
3
|
+
/* only for short-term life state encryption */
|
4
|
+
const KEY = crypto.randomBytes(32)
|
5
|
+
|
6
|
+
export function encryptState(text: string) {
|
7
|
+
const iv = crypto.randomBytes(16)
|
8
|
+
const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(KEY), iv)
|
9
|
+
const encrypted = cipher.update(text)
|
10
|
+
|
11
|
+
return iv.toString('hex') + ':' + Buffer.concat([encrypted, cipher.final()]).toString('hex')
|
12
|
+
}
|
13
|
+
|
14
|
+
export function decryptState(text: string) {
|
15
|
+
const textParts = text.split(':')
|
16
|
+
const iv = Buffer.from(textParts.shift(), 'hex')
|
17
|
+
const encryptedText = Buffer.from(textParts.join(':'), 'hex')
|
18
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(KEY), iv)
|
19
|
+
const decrypted = decipher.update(encryptedText)
|
20
|
+
|
21
|
+
return Buffer.concat([decrypted, decipher.final()]).toString()
|
22
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { config } from '@things-factory/env'
|
2
|
+
|
3
|
+
var _AES_256_KEY = config.get('AES_256_KEY')
|
4
|
+
|
5
|
+
if (!_AES_256_KEY) {
|
6
|
+
if (process.env.NODE_ENV == 'production') {
|
7
|
+
throw new TypeError('AES_256_KEY not configured.')
|
8
|
+
} else {
|
9
|
+
_AES_256_KEY = 'V6g5oHJZb7KcYzIyL6cM95XvIDouon5b'
|
10
|
+
}
|
11
|
+
}
|
12
|
+
|
13
|
+
export const AES_256_KEY = _AES_256_KEY
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import { EntityManager, Repository, SelectQueryBuilder } from 'typeorm'
|
2
|
+
|
3
|
+
import { Domain, getRepository } from '@things-factory/shell'
|
4
|
+
|
5
|
+
import { User } from '../service/user/user'
|
6
|
+
|
7
|
+
export async function getDomainUsers(domain: Partial<Domain>, trxMgr?: EntityManager): Promise<User[]> {
|
8
|
+
const domainRepo: Repository<Domain> = trxMgr?.getRepository(Domain) || getRepository(Domain)
|
9
|
+
|
10
|
+
if (!domain.id) {
|
11
|
+
const foundDomain: Domain = await domainRepo.findOne({ where: { id: domain.id } })
|
12
|
+
if (!foundDomain) throw new Error(`Failed to find domain by passed condition, ${domain}`)
|
13
|
+
|
14
|
+
domain = foundDomain
|
15
|
+
}
|
16
|
+
|
17
|
+
const qb: SelectQueryBuilder<User> = buildDomainUsersQueryBuilder(domain.id)
|
18
|
+
return await qb.getMany()
|
19
|
+
}
|
20
|
+
|
21
|
+
export function buildDomainUsersQueryBuilder(
|
22
|
+
domainId: string,
|
23
|
+
alias: string = 'USER',
|
24
|
+
trxMgr?: EntityManager
|
25
|
+
): SelectQueryBuilder<User> {
|
26
|
+
const userRepo: Repository<User> = trxMgr?.getRepository(User) || getRepository(User)
|
27
|
+
const qb: SelectQueryBuilder<User> = userRepo.createQueryBuilder(alias)
|
28
|
+
qb.select().andWhere(
|
29
|
+
`${alias}.id IN ${qb
|
30
|
+
.subQuery()
|
31
|
+
.select('USERS_DOMAINS.users_id')
|
32
|
+
.from('users_domains', 'USERS_DOMAINS')
|
33
|
+
.where('USERS_DOMAINS.domains_id = :domainId', { domainId })
|
34
|
+
.getQuery()}`
|
35
|
+
)
|
36
|
+
|
37
|
+
return qb
|
38
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { config } from '@things-factory/env'
|
2
|
+
|
3
|
+
var _SECRET = config.get('SECRET')
|
4
|
+
|
5
|
+
if (!_SECRET) {
|
6
|
+
if (process.env.NODE_ENV == 'production') {
|
7
|
+
throw new TypeError('SECRET key not configured.')
|
8
|
+
} else {
|
9
|
+
_SECRET = '0xD58F835B69D207A76CC5F84a70a1D0d4C79dAC95'
|
10
|
+
}
|
11
|
+
}
|
12
|
+
|
13
|
+
export const SECRET = _SECRET
|
@@ -0,0 +1,112 @@
|
|
1
|
+
import { In } from 'typeorm'
|
2
|
+
|
3
|
+
import { Domain, getRepository } from '@things-factory/shell'
|
4
|
+
|
5
|
+
import { User } from '../service/user/user'
|
6
|
+
|
7
|
+
export async function getUserDomains(user: User): Promise<Partial<Domain>[]> {
|
8
|
+
return (
|
9
|
+
await getRepository(Domain)
|
10
|
+
.createQueryBuilder('DOMAIN')
|
11
|
+
.where(qb => {
|
12
|
+
const subQuery = qb
|
13
|
+
.subQuery()
|
14
|
+
.distinct(true)
|
15
|
+
.select('DOMAIN.id')
|
16
|
+
.from(User, 'USER')
|
17
|
+
.leftJoin('USER.roles', 'ROLE')
|
18
|
+
.leftJoin('ROLE.domain', 'DOMAIN')
|
19
|
+
.where('USER.id = :userId', { userId: user.id })
|
20
|
+
.getQuery()
|
21
|
+
return 'DOMAIN.id IN ' + subQuery
|
22
|
+
})
|
23
|
+
.orWhere(qb => {
|
24
|
+
const subQuery = qb
|
25
|
+
.subQuery()
|
26
|
+
.select('DOMAIN.id')
|
27
|
+
.from(Domain, 'DOMAIN')
|
28
|
+
.where('DOMAIN.owner = :owner', { owner: user.id })
|
29
|
+
.getQuery()
|
30
|
+
return 'DOMAIN.id IN ' + subQuery
|
31
|
+
})
|
32
|
+
.orderBy('DOMAIN.name', 'ASC')
|
33
|
+
.getMany()
|
34
|
+
).map(domain => {
|
35
|
+
const { id, name, description, subdomain, extType, brandName, brandImage } = domain
|
36
|
+
return { id, name, description, subdomain, extType, brandName, brandImage }
|
37
|
+
})
|
38
|
+
}
|
39
|
+
|
40
|
+
export async function getRoleBasedDomains(user: User): Promise<Partial<Domain>[]> {
|
41
|
+
return (
|
42
|
+
await getRepository(Domain)
|
43
|
+
.createQueryBuilder('DOMAIN')
|
44
|
+
.where(qb => {
|
45
|
+
const subQuery = qb
|
46
|
+
.subQuery()
|
47
|
+
.distinct(true)
|
48
|
+
.select('DOMAIN.id')
|
49
|
+
.from(User, 'USER')
|
50
|
+
.leftJoin('USER.roles', 'ROLE')
|
51
|
+
.leftJoin('ROLE.domain', 'DOMAIN')
|
52
|
+
.where('USER.id = :userId', { userId: user.id })
|
53
|
+
.getQuery()
|
54
|
+
return 'DOMAIN.id IN ' + subQuery
|
55
|
+
})
|
56
|
+
.getMany()
|
57
|
+
).map(domain => {
|
58
|
+
const { id, name, description, subdomain, extType, brandName, brandImage } = domain
|
59
|
+
return { id, name, description, subdomain, extType, brandName, brandImage }
|
60
|
+
})
|
61
|
+
}
|
62
|
+
|
63
|
+
export async function getDomainsWithPrivilege(
|
64
|
+
user: User,
|
65
|
+
privilege: string,
|
66
|
+
category: string
|
67
|
+
): Promise<Partial<Domain>[]> {
|
68
|
+
return (
|
69
|
+
await getRepository(Domain)
|
70
|
+
.createQueryBuilder('DOMAIN')
|
71
|
+
.where(qb => {
|
72
|
+
const subQuery = qb
|
73
|
+
.subQuery()
|
74
|
+
.distinct(true)
|
75
|
+
.select('DOMAIN.id')
|
76
|
+
.from(User, 'USER')
|
77
|
+
.leftJoin('USER.roles', 'ROLE')
|
78
|
+
.leftJoin('ROLE.domain', 'DOMAIN')
|
79
|
+
.leftJoin('ROLE.privileges', 'PRIVILEGE')
|
80
|
+
.where('USER.id = :userId', { userId: user.id })
|
81
|
+
.andWhere('PRIVILEGE.name = :privilege', { privilege })
|
82
|
+
.andWhere('PRIVILEGE.category = :category', { category })
|
83
|
+
.getQuery()
|
84
|
+
return 'DOMAIN.id IN ' + subQuery
|
85
|
+
})
|
86
|
+
.orWhere(qb => {
|
87
|
+
const subQuery = qb
|
88
|
+
.subQuery()
|
89
|
+
.select('DOMAIN.id')
|
90
|
+
.from(Domain, 'DOMAIN')
|
91
|
+
.where('DOMAIN.owner = :owner', { owner: user.id })
|
92
|
+
.getQuery()
|
93
|
+
return 'DOMAIN.id IN ' + subQuery
|
94
|
+
})
|
95
|
+
.orderBy('DOMAIN.name', 'ASC')
|
96
|
+
.getMany()
|
97
|
+
).map(domain => {
|
98
|
+
const { id, name, description, subdomain, extType, brandName, brandImage } = domain
|
99
|
+
return { id, name, description, subdomain, extType, brandName, brandImage }
|
100
|
+
})
|
101
|
+
}
|
102
|
+
|
103
|
+
export async function getDomainsAsOwner(user: User): Promise<Partial<Domain>[]> {
|
104
|
+
return (
|
105
|
+
await getRepository(Domain).find({
|
106
|
+
where: { owner: user.id }
|
107
|
+
})
|
108
|
+
).map(domain => {
|
109
|
+
const { id, name, description, subdomain, extType, brandName, brandImage } = domain
|
110
|
+
return { id, name, description, subdomain, extType, brandName, brandImage }
|
111
|
+
})
|
112
|
+
}
|
package/translations/en.json
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
"error.confirm password not matched": "new password and confirm password is not matched",
|
5
5
|
"error.domain mismatch": "certificate is not for this domain",
|
6
6
|
"error.domain not allowed": "user not allowed domain `{subdomain}`",
|
7
|
-
"error.email already exists": "email already used by another user",
|
8
7
|
"error.failed to find x": "failed to find {x}",
|
9
8
|
"error.password should be supported": "initial password or default password should be supported",
|
10
9
|
"error.password should match the rule": "password should match following rule. ${rule}",
|
@@ -12,15 +11,13 @@
|
|
12
11
|
"error.subdomain not found": "domain not found",
|
13
12
|
"error.token or password is invalid": "token or password is invalid",
|
14
13
|
"error.unavailable-domain": "unavailable domain",
|
15
|
-
"error.user credential not found": "user credential not found. You need to register device to use biometric authentication.",
|
16
14
|
"error.user credential registeration failed": "user credential registration failed. It may be an already registered credential.",
|
17
15
|
"error.user credential registration not allowed": "user credential registration failed. The registration timed out or was not allowed.",
|
18
|
-
"error.user duplicated
|
16
|
+
"error.user duplicated": "user duplicated",
|
19
17
|
"error.user not activated": "user is not activated",
|
20
18
|
"error.user not found": "user not found",
|
21
19
|
"error.user or verification token not found": "user or verification token not found",
|
22
20
|
"error.user validation failed": "user validation failed",
|
23
|
-
"error.username already exists": "username already used by another user",
|
24
21
|
"error.x is not a member of y": "{x} is not a member of {y}",
|
25
22
|
"field.active": "active",
|
26
23
|
"field.appliance_id": "appliance id",
|
@@ -59,7 +56,6 @@
|
|
59
56
|
"text.signout successfully": "signout successfully",
|
60
57
|
"text.user activated successfully": "user activated successfully",
|
61
58
|
"text.user credential registered successfully": "device registration has been successfully completed. You can now use biometric authentication.",
|
62
|
-
"text.user inactivated successfully": "user inactivated successfully",
|
63
59
|
"text.user registered successfully": "user registered successfully. find your email to activate account",
|
64
60
|
"text.verification email sent": "verification email sent"
|
65
61
|
}
|
package/translations/ja.json
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
"error.confirm password not matched": "新しいパスワードと確認パスワードが一致しません.",
|
5
5
|
"error.domain mismatch": "証明書のドメインと現在のドメインが一致しません.",
|
6
6
|
"error.domain not allowed": "'{subdomain}' 領域はこのユーザに許可されていません.",
|
7
|
-
"error.email already exists": "メールはすでに他のユーザーによって使用されています.",
|
8
7
|
"error.failed to find x": "{x}が見つかりません.",
|
9
8
|
"error.password should be supported": "初期パスワードまたはデフォルトパスワードがサポートされるべきです",
|
10
9
|
"error.password should match the rule": "パスワードは次の規則を守らなければなりません. {rule}",
|
@@ -12,15 +11,13 @@
|
|
12
11
|
"error.subdomain not found": "サブドメインが見つかりません.",
|
13
12
|
"error.token or password is invalid": "トークンまたはパスワードが無効です.",
|
14
13
|
"error.unavailable-domain": "使用できないドメインです.",
|
15
|
-
"error.user credential not found": "ユーザー資格情報が見つかりません. 生体認証を使用するにはデバイスを登録する必要があります.",
|
16
14
|
"error.user credential registeration failed": "ユーザー資格情報の登録に失敗しました。既に登録されている資格情報の可能性があります。",
|
17
15
|
"error.user credential registration not allowed": "ユーザー資格情報の登録に失敗しました。登録のタイムアウトまたは登録が許可されていません。",
|
18
|
-
"error.user duplicated
|
16
|
+
"error.user duplicated": "同じメールで登録されたアカウントが存在します.",
|
19
17
|
"error.user not activated": "ユーザーがアクティブ化されていません.",
|
20
18
|
"error.user not found": "ユーザーが存在しません.",
|
21
19
|
"error.user or verification token not found": "ユーザーまたは確認トークンが見つかりません.",
|
22
20
|
"error.user validation failed": "ユーザー確認に失敗しました.",
|
23
|
-
"error.username already exists": "ユーザー名はすでに他のユーザーによって使用されています.",
|
24
21
|
"error.x is not a member of y": "{x}は{y}のメンバーではありません.",
|
25
22
|
"field.active": "アクティブ",
|
26
23
|
"field.appliance_id": "器具ID",
|
@@ -59,7 +56,6 @@
|
|
59
56
|
"text.signout successfully": "ログアウトに成功しました.",
|
60
57
|
"text.user activated successfully": "ユーザーが正常に活性化されました.",
|
61
58
|
"text.user credential registered successfully": "デバイスの登録が正常に完了しました。今後は生体認証を使用できます。",
|
62
|
-
"text.user inactivated successfully": "ユーザーが正常に非アクティブ化されました.",
|
63
59
|
"text.user registered successfully": "ユーザーが正常に登録されました. 確認メールでアカウントを有効にしてください.",
|
64
60
|
"text.verification email sent": "確認メールを送りました."
|
65
61
|
}
|
package/translations/ko.json
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
"error.confirm password not matched": "새 비밀번호와 확인 비밀번호가 일치하지 않습니다.",
|
5
5
|
"error.domain mismatch": "인증서의 도메인과 현재 도메인이 일치하지 않습니다.",
|
6
6
|
"error.domain not allowed": "'{subdomain}' 영역은 이 사용자에게 허가되지 않았습니다.",
|
7
|
-
"error.email already exists": "이메일이 이미 사용되고 있습니다.",
|
8
7
|
"error.failed to find x": "{x}을(를) 찾을 수 없습니다.",
|
9
8
|
"error.password should be supported": "초기 비밀번호나 디폴트 비밀번호가 제공되어야 합니다.",
|
10
9
|
"error.password should match the rule": "비밀번호는 다음 규칙을 지켜야 합니다. {rule}",
|
@@ -13,14 +12,13 @@
|
|
13
12
|
"error.token or password is invalid": "토큰 또는 비밀번호가 유효하지 않습니다.",
|
14
13
|
"error.unavailable-domain": "사용할 수 없는 도메인입니다.",
|
15
14
|
"error.user credential not found": "사용자 자격 증명을 찾을 수 없습니다. 바이오메트릭 인증을 사용하기 위해서는 먼저 기기를 등록해야 합니다.",
|
16
|
-
"error.user
|
17
|
-
"error.user credential registration not allowed": "사용자 인증서 등록이 실패하였습니다. 등록 시간이 초과되었거나 등록이 허용되지 않았습니다.",
|
18
|
-
"error.user duplicated": "동일한 이메일이나 사용자아이디로 가입된 계정이 이미 존재합니다.",
|
15
|
+
"error.user duplicated": "동일한 이메일로 가입된 계정이 이미 존재합니다.",
|
19
16
|
"error.user not activated": "사용자가 활성화되지 않았습니다.",
|
20
17
|
"error.user not found": "사용자가 존재하지 않습니다.",
|
21
18
|
"error.user or verification token not found": "사용자 또는 확인토큰을 찾을 수 없습니다.",
|
19
|
+
"error.user credential registeration failed": "사용자 인증서 등록이 실패하였습니다. 이미 등록된 인증서일 수 있습니다.",
|
20
|
+
"error.user credential registration not allowed": "사용자 인증서 등록이 실패하였습니다. 등록 시간이 초과되었거나 등록이 허용되지 않았습니다.",
|
22
21
|
"error.user validation failed": "사용자 확인에 실패하였습니다.",
|
23
|
-
"error.username already exists": "사용자 아이디가 이미 사용되고 있습니다.",
|
24
22
|
"error.x is not a member of y": "{x}은(는) {y}의 멤버가 아닙니다.",
|
25
23
|
"field.active": "활성화",
|
26
24
|
"field.appliance_id": "기구 아이디",
|
@@ -59,7 +57,6 @@
|
|
59
57
|
"text.signout successfully": "성공적으로 로그아웃 하였습니다.",
|
60
58
|
"text.user activated successfully": "사용자가 성공적으로 활성화되었습니다.",
|
61
59
|
"text.user credential registered successfully": "기기 등록이 성공적으로 완료되었습니다. 이제 바이오메트릭 인증을 사용할 수 있습니다.",
|
62
|
-
"text.user inactivated successfully": "사용자가 성공적으로 비활성화되었습니다.",
|
63
60
|
"text.user registered successfully": "사용자가 성공적으로 등록되었습니다. 확인 이메일을 통해서 계정을 활성화하시기 바랍니다.",
|
64
61
|
"text.verification email sent": "확인 이메일을 보냈습니다."
|
65
62
|
}
|