@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,84 @@
|
|
|
1
|
+
import passport from 'koa-passport'
|
|
2
|
+
import { ExtractJwt, Strategy as JWTstrategy } from 'passport-jwt'
|
|
3
|
+
|
|
4
|
+
import { config } from '@things-factory/env'
|
|
5
|
+
|
|
6
|
+
import { makeVerificationToken } from '../controllers/utils/make-verification-token'
|
|
7
|
+
import { saveVerificationToken } from '../controllers/utils/save-verification-token'
|
|
8
|
+
import { User, UserStatus } from '../service/user/user'
|
|
9
|
+
import { VerificationTokenType } from '../service/verification-token/verification-token'
|
|
10
|
+
import { clearAccessTokenCookie, getAccessTokenCookie, setAccessTokenCookie } from '../utils/access-token-cookie'
|
|
11
|
+
import { SECRET } from '../utils/get-secret'
|
|
12
|
+
|
|
13
|
+
const sessionExpiryPolicy = config.get('session/expiryPolicy', 'fixed')
|
|
14
|
+
|
|
15
|
+
passport.use(
|
|
16
|
+
new JWTstrategy(
|
|
17
|
+
{
|
|
18
|
+
secretOrKey: SECRET,
|
|
19
|
+
passReqToCallback: true,
|
|
20
|
+
jwtFromRequest: ExtractJwt.fromExtractors([
|
|
21
|
+
ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
22
|
+
ExtractJwt.fromHeader('authorization'),
|
|
23
|
+
ExtractJwt.fromHeader('x-access-token'),
|
|
24
|
+
ExtractJwt.fromUrlQueryParameter('access_token'),
|
|
25
|
+
ExtractJwt.fromBodyField('access_token'),
|
|
26
|
+
req => {
|
|
27
|
+
var token = null
|
|
28
|
+
token = getAccessTokenCookie(req?.ctx)
|
|
29
|
+
return token
|
|
30
|
+
}
|
|
31
|
+
])
|
|
32
|
+
},
|
|
33
|
+
async (request, decoded, done) => {
|
|
34
|
+
try {
|
|
35
|
+
return done(null, decoded)
|
|
36
|
+
} catch (error) {
|
|
37
|
+
return done(error)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
export async function jwtAuthenticateMiddleware(context, next) {
|
|
44
|
+
const { path } = context
|
|
45
|
+
const { user } = context.state
|
|
46
|
+
if (user) {
|
|
47
|
+
return await next()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return await passport.authenticate('jwt', { session: false }, async (err, decoded, info) => {
|
|
51
|
+
if (err || !decoded) {
|
|
52
|
+
const e = (context.state.error = err || info)
|
|
53
|
+
|
|
54
|
+
clearAccessTokenCookie(context)
|
|
55
|
+
|
|
56
|
+
context.throw(401, e.message)
|
|
57
|
+
} else {
|
|
58
|
+
const userEntity = await User.checkAuth(decoded)
|
|
59
|
+
|
|
60
|
+
if (userEntity.status === UserStatus.PWD_RESET_REQUIRED) {
|
|
61
|
+
try {
|
|
62
|
+
const token = makeVerificationToken()
|
|
63
|
+
await saveVerificationToken(userEntity.id, token, VerificationTokenType.PASSWORD_RESET)
|
|
64
|
+
clearAccessTokenCookie(context)
|
|
65
|
+
context.redirect(`/auth/reset-password?token=${token}`)
|
|
66
|
+
} catch (e) {
|
|
67
|
+
throw err
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
context.state.user = userEntity
|
|
71
|
+
context.state.decodedToken = decoded
|
|
72
|
+
|
|
73
|
+
if (sessionExpiryPolicy == 'rolling') {
|
|
74
|
+
/* To renew the expiry time on each request, a token is issued and the session is updated. */
|
|
75
|
+
|
|
76
|
+
const token = await userEntity.sign()
|
|
77
|
+
setAccessTokenCookie(context, token)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
await next()
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
})(context, next)
|
|
84
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import passport from 'koa-passport'
|
|
2
|
+
import { Strategy as localStrategy } from 'passport-local'
|
|
3
|
+
|
|
4
|
+
import { signin } from '../controllers/signin'
|
|
5
|
+
|
|
6
|
+
passport.use(
|
|
7
|
+
'signin',
|
|
8
|
+
new localStrategy(
|
|
9
|
+
{
|
|
10
|
+
usernameField: 'email',
|
|
11
|
+
passwordField: 'password'
|
|
12
|
+
},
|
|
13
|
+
async (email, password, done) => {
|
|
14
|
+
try {
|
|
15
|
+
const {
|
|
16
|
+
user: userInfo,
|
|
17
|
+
token,
|
|
18
|
+
domains
|
|
19
|
+
} = await signin({
|
|
20
|
+
email,
|
|
21
|
+
password
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
return done(
|
|
25
|
+
null,
|
|
26
|
+
{
|
|
27
|
+
user: userInfo,
|
|
28
|
+
token,
|
|
29
|
+
domains
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
message: 'Logged in Successfully'
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return done(error)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
export async function signinMiddleware(context, next) {
|
|
43
|
+
return passport.authenticate('signin', { session: false }, async (err, user, info) => {
|
|
44
|
+
if (err || !user) {
|
|
45
|
+
throw err
|
|
46
|
+
} else {
|
|
47
|
+
const { user: userInfo, token } = user
|
|
48
|
+
|
|
49
|
+
context.state.user = userInfo
|
|
50
|
+
context.state.token = token
|
|
51
|
+
|
|
52
|
+
await next()
|
|
53
|
+
}
|
|
54
|
+
})(context, next)
|
|
55
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import passport from 'koa-passport'
|
|
2
|
+
import { Strategy as CustomStrategy } from 'passport-custom'
|
|
3
|
+
|
|
4
|
+
import { getRepository } from '@things-factory/shell'
|
|
5
|
+
|
|
6
|
+
import { User } from '../service/user/user'
|
|
7
|
+
import { AuthError } from '../errors/auth-error'
|
|
8
|
+
|
|
9
|
+
import { WebAuthCredential } from '../service/web-auth-credential/web-auth-credential'
|
|
10
|
+
import { verifyRegistrationResponse, verifyAuthenticationResponse } from '@simplewebauthn/server'
|
|
11
|
+
|
|
12
|
+
import { AuthenticatorAssertionResponse } from '@simplewebauthn/types'
|
|
13
|
+
|
|
14
|
+
passport.use(
|
|
15
|
+
'webauthn-register',
|
|
16
|
+
new CustomStrategy(async (context, done) => {
|
|
17
|
+
const { body, session, user, hostname, origin } = context as any
|
|
18
|
+
|
|
19
|
+
const challenge = session.challenge
|
|
20
|
+
|
|
21
|
+
const verification = await verifyRegistrationResponse({
|
|
22
|
+
response: body,
|
|
23
|
+
expectedChallenge: challenge,
|
|
24
|
+
expectedOrigin: origin,
|
|
25
|
+
expectedRPID: hostname,
|
|
26
|
+
expectedType: 'webauthn.create',
|
|
27
|
+
requireUserVerification: false
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
if (verification.verified) {
|
|
31
|
+
const { registrationInfo } = verification
|
|
32
|
+
const publicKey = Buffer.from(registrationInfo.credentialPublicKey).toString('base64')
|
|
33
|
+
|
|
34
|
+
if (user) {
|
|
35
|
+
const webAuthRepository = getRepository(WebAuthCredential)
|
|
36
|
+
await webAuthRepository.save({
|
|
37
|
+
user,
|
|
38
|
+
credentialId: registrationInfo.credentialID,
|
|
39
|
+
publicKey,
|
|
40
|
+
counter: registrationInfo.counter,
|
|
41
|
+
creator: user,
|
|
42
|
+
updater: user
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return done(null, user)
|
|
47
|
+
} else {
|
|
48
|
+
return done(null, false)
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
passport.use(
|
|
54
|
+
'webauthn-login',
|
|
55
|
+
new CustomStrategy(async (context, done) => {
|
|
56
|
+
try {
|
|
57
|
+
const { body, session, origin, hostname } = context as any
|
|
58
|
+
|
|
59
|
+
const challenge = session.challenge
|
|
60
|
+
|
|
61
|
+
const assertionResponse = body as {
|
|
62
|
+
id: string
|
|
63
|
+
response: AuthenticatorAssertionResponse
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const credential = await getRepository(WebAuthCredential).findOne({
|
|
67
|
+
where: {
|
|
68
|
+
credentialId: assertionResponse.id
|
|
69
|
+
},
|
|
70
|
+
relations: ['user']
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
if (!credential) {
|
|
74
|
+
return done(null, false)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const verification = await verifyAuthenticationResponse({
|
|
78
|
+
response: body,
|
|
79
|
+
expectedChallenge: challenge,
|
|
80
|
+
expectedOrigin: origin,
|
|
81
|
+
expectedRPID: hostname,
|
|
82
|
+
requireUserVerification: false,
|
|
83
|
+
authenticator: {
|
|
84
|
+
credentialID: credential.credentialId,
|
|
85
|
+
credentialPublicKey: new Uint8Array(Buffer.from(credential.publicKey, 'base64')),
|
|
86
|
+
counter: credential.counter
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
if (verification.verified) {
|
|
91
|
+
const { authenticationInfo } = verification
|
|
92
|
+
credential.counter = authenticationInfo.newCounter
|
|
93
|
+
await getRepository(WebAuthCredential).save(credential)
|
|
94
|
+
|
|
95
|
+
const user = credential.user
|
|
96
|
+
return done(null, user)
|
|
97
|
+
} else {
|
|
98
|
+
return done(verification, false)
|
|
99
|
+
}
|
|
100
|
+
} catch(error) {
|
|
101
|
+
return done(error, false)
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
export function createWebAuthnMiddleware(strategy: 'webauthn-register' | 'webauthn-login') {
|
|
107
|
+
return async function webAuthnMiddleware(context, next) {
|
|
108
|
+
return passport.authenticate(
|
|
109
|
+
strategy,
|
|
110
|
+
{ session: true, failureMessage: true, failWithError: true },
|
|
111
|
+
async (err, user) => {
|
|
112
|
+
if (err || !user) {
|
|
113
|
+
throw new AuthError({
|
|
114
|
+
errorCode: AuthError.ERROR_CODES.AUTHN_VERIFICATION_FAILED,
|
|
115
|
+
detail: err
|
|
116
|
+
})
|
|
117
|
+
} else {
|
|
118
|
+
context.state.user = user
|
|
119
|
+
|
|
120
|
+
context.body = { user, verified: true }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
await next()
|
|
124
|
+
}
|
|
125
|
+
)(context, next)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ILike, MigrationInterface, QueryRunner } from 'typeorm'
|
|
2
|
+
|
|
3
|
+
import { config, logger } from '@things-factory/env'
|
|
4
|
+
import { Domain, getRepository } from '@things-factory/shell'
|
|
5
|
+
|
|
6
|
+
import { User, UserStatus } from '../service/user/user'
|
|
7
|
+
|
|
8
|
+
const ADMIN_ACCOUNT = config.get('adminAccount', {
|
|
9
|
+
name: 'Admin',
|
|
10
|
+
email: 'admin@hatiolab.com',
|
|
11
|
+
password: 'admin'
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const SEED_USERS = [
|
|
15
|
+
{
|
|
16
|
+
...ADMIN_ACCOUNT,
|
|
17
|
+
userType: 'user',
|
|
18
|
+
status: UserStatus.ACTIVATED
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
export class SeedUsers1548206416130 implements MigrationInterface {
|
|
22
|
+
public async up(queryRunner: QueryRunner): Promise<any> {
|
|
23
|
+
const userRepository = getRepository(User)
|
|
24
|
+
const domainRepository = getRepository(Domain)
|
|
25
|
+
|
|
26
|
+
const domain: Domain = await domainRepository.findOne({ where: { name: 'SYSTEM' } })
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
for (let i = 0; i < SEED_USERS.length; i++) {
|
|
30
|
+
const user = SEED_USERS[i]
|
|
31
|
+
const salt = User.generateSalt()
|
|
32
|
+
const password = User.encode(user.password, salt)
|
|
33
|
+
|
|
34
|
+
await userRepository.save({
|
|
35
|
+
...user,
|
|
36
|
+
salt,
|
|
37
|
+
password,
|
|
38
|
+
domains: [domain]
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
} catch (e) {
|
|
42
|
+
logger.error(e)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const admin = await userRepository.findOne({ where: { email: ILike('admin@hatiolab.com') } })
|
|
46
|
+
domain.owner = admin.id
|
|
47
|
+
|
|
48
|
+
await domainRepository.save(domain)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public async down(queryRunner: QueryRunner): Promise<any> {
|
|
52
|
+
const repository = getRepository(User)
|
|
53
|
+
|
|
54
|
+
SEED_USERS.reverse().forEach(async user => {
|
|
55
|
+
let record = await repository.findOneBy({ email: ILike(user.email) })
|
|
56
|
+
await repository.remove(record)
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from 'typeorm'
|
|
2
|
+
|
|
3
|
+
import { logger } from '@things-factory/env'
|
|
4
|
+
import { getRepository } from '@things-factory/shell'
|
|
5
|
+
|
|
6
|
+
import { Privilege } from '../service/privilege/privilege'
|
|
7
|
+
|
|
8
|
+
export class SeedPrivilege1566805283882 implements MigrationInterface {
|
|
9
|
+
public async up(queryRunner: QueryRunner): Promise<any> {
|
|
10
|
+
const privilegeRepository = getRepository(Privilege)
|
|
11
|
+
|
|
12
|
+
const { schema } = require('@things-factory/shell/dist-server/schema')
|
|
13
|
+
await schema()
|
|
14
|
+
const privileges = process['PRIVILEGES']
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
for (const [category, name] of Object.values(privileges as [string, string])) {
|
|
18
|
+
if (0 == (await privilegeRepository.count({ where: { category, name } }))) {
|
|
19
|
+
await privilegeRepository.save({ category, name })
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
} catch (e) {
|
|
23
|
+
logger.error(e)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public async down(queryRunner: QueryRunner): Promise<any> {}
|
|
28
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const glob = require('glob')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
export var migrations = []
|
|
5
|
+
|
|
6
|
+
glob.sync(path.resolve(__dirname, '.', '**', '*.js')).forEach(function(file) {
|
|
7
|
+
if (file.indexOf('index.js') !== -1) return
|
|
8
|
+
migrations = migrations.concat(Object.values(require(path.resolve(file))) || [])
|
|
9
|
+
})
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import Router from 'koa-router'
|
|
2
|
+
|
|
3
|
+
import { config } from '@things-factory/env'
|
|
4
|
+
import { Domain, findSubdomainFromPath, getRedirectSubdomainPath } from '@things-factory/shell'
|
|
5
|
+
|
|
6
|
+
import { LoginHistory } from '../service/login-history/login-history'
|
|
7
|
+
import { User } from '../service/user/user'
|
|
8
|
+
import { accepts } from '../utils/accepts'
|
|
9
|
+
import { clearAccessTokenCookie } from '../utils/access-token-cookie'
|
|
10
|
+
import { getUserDomains } from '../utils/get-user-domains'
|
|
11
|
+
|
|
12
|
+
const domainType = config.get('domainType')
|
|
13
|
+
|
|
14
|
+
export const authCheckinRouter = new Router()
|
|
15
|
+
|
|
16
|
+
authCheckinRouter.get('/auth/checkin/:subdomain?', async (context, next) => {
|
|
17
|
+
const { request, t } = context
|
|
18
|
+
const header = request.header
|
|
19
|
+
const { user } = context.state
|
|
20
|
+
let { subdomain } = context.params
|
|
21
|
+
|
|
22
|
+
let domains: Partial<Domain>[] = await getUserDomains(user)
|
|
23
|
+
if (domainType) domains = domains.filter(d => d.extType == domainType)
|
|
24
|
+
|
|
25
|
+
if (!accepts(header.accept, ['text/html', '*/*'])) {
|
|
26
|
+
// When request expects non html response
|
|
27
|
+
try {
|
|
28
|
+
if (!subdomain) throw new Error(t('error.domain not specified', { subdomain })) // When params doesn't have subdomain
|
|
29
|
+
const checkInDomain: Partial<Domain> | undefined = domains.find(d => d.subdomain === subdomain) // When no matched domain with subdomain
|
|
30
|
+
if (!checkInDomain) throw new Error(t('error.domain not specified', { subdomain }))
|
|
31
|
+
|
|
32
|
+
await checkIn(checkInDomain, null, context)
|
|
33
|
+
context.body = true
|
|
34
|
+
} catch (e) {
|
|
35
|
+
clearAccessTokenCookie(context)
|
|
36
|
+
throw e
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
// When request expects html response
|
|
40
|
+
const { redirect_to: redirectTo = '/' } = context.query
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
let message: string
|
|
44
|
+
|
|
45
|
+
if (!subdomain) {
|
|
46
|
+
/* try to find domain from redirectTo path */
|
|
47
|
+
subdomain = findSubdomainFromPath(context, redirectTo)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let checkInDomain: Partial<Domain>
|
|
51
|
+
if (subdomain) {
|
|
52
|
+
checkInDomain = domains.find(d => d.subdomain == subdomain)
|
|
53
|
+
if (!checkInDomain) message = t('error.domain not allowed', { subdomain })
|
|
54
|
+
} else if (domains.length === 1) {
|
|
55
|
+
checkInDomain = domains[0]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (checkInDomain) {
|
|
59
|
+
return await checkIn(checkInDomain, redirectTo, context)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
await context.render('auth-page', {
|
|
63
|
+
pageElement: 'auth-checkin',
|
|
64
|
+
elementScript: '/auth/checkin.js',
|
|
65
|
+
data: {
|
|
66
|
+
user: { email: user.email, locale: user.locale, name: user.name, userType: user.userType },
|
|
67
|
+
domains,
|
|
68
|
+
domainType,
|
|
69
|
+
redirectTo,
|
|
70
|
+
message
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
} catch (e) {
|
|
74
|
+
clearAccessTokenCookie(context)
|
|
75
|
+
context.redirect(
|
|
76
|
+
`/auth/signin?email=${encodeURIComponent(user.email)}&redirect_to=${encodeURIComponent(redirectTo)}`
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
authCheckinRouter.get('/auth/domains', async context => {
|
|
83
|
+
const { user } = context.state
|
|
84
|
+
var domains = await getUserDomains(user)
|
|
85
|
+
if (domainType) {
|
|
86
|
+
domains = domains.filter(d => d.extType == domainType)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
context.body = domains
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
async function checkIn(
|
|
93
|
+
checkInDomain: Partial<Domain>,
|
|
94
|
+
redirectTo: string | null,
|
|
95
|
+
context: ResolverContext
|
|
96
|
+
): Promise<void> {
|
|
97
|
+
const { user }: { user: User } = context.state
|
|
98
|
+
const remoteAddress = context.req.headers['x-forwarded-for']
|
|
99
|
+
? (context.req.headers['x-forwarded-for'] as string).split(',')[0].trim()
|
|
100
|
+
: context.req.connection.remoteAddress
|
|
101
|
+
|
|
102
|
+
await LoginHistory.stamp(checkInDomain, user, remoteAddress)
|
|
103
|
+
|
|
104
|
+
if (redirectTo) {
|
|
105
|
+
return context.redirect(getRedirectSubdomainPath(context, checkInDomain.subdomain, redirectTo))
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { ILike } from 'typeorm'
|
|
2
|
+
import Router from 'koa-router'
|
|
3
|
+
|
|
4
|
+
import { config } from '@things-factory/env'
|
|
5
|
+
import { Domain, getRepository } from '@things-factory/shell'
|
|
6
|
+
|
|
7
|
+
import { changePwd } from '../controllers/change-pwd'
|
|
8
|
+
import { deleteUser } from '../controllers/delete-user'
|
|
9
|
+
import { updateProfile } from '../controllers/profile'
|
|
10
|
+
import { User } from '../service/user/user'
|
|
11
|
+
import { clearAccessTokenCookie, setAccessTokenCookie } from '../utils/access-token-cookie'
|
|
12
|
+
import { getUserDomains } from '../utils/get-user-domains'
|
|
13
|
+
|
|
14
|
+
const domainType = config.get('domainType')
|
|
15
|
+
const languages = config.get('i18n/languages') || []
|
|
16
|
+
|
|
17
|
+
export const authPrivateProcessRouter = new Router({
|
|
18
|
+
prefix: '/auth'
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
authPrivateProcessRouter
|
|
22
|
+
.post('/change-pass', async (context, next) => {
|
|
23
|
+
const { t } = context
|
|
24
|
+
let { current_pass, new_pass, confirm_pass } = context.request.body
|
|
25
|
+
|
|
26
|
+
const token = await changePwd(context.state.user, current_pass, new_pass, confirm_pass, context)
|
|
27
|
+
|
|
28
|
+
context.body = t('text.password changed successfully')
|
|
29
|
+
|
|
30
|
+
setAccessTokenCookie(context, token)
|
|
31
|
+
})
|
|
32
|
+
.post('/update-profile', async (context, next) => {
|
|
33
|
+
const { i18next, t } = context
|
|
34
|
+
const newProfiles = context.request.body
|
|
35
|
+
await updateProfile(context.state.user, newProfiles)
|
|
36
|
+
|
|
37
|
+
if (newProfiles.locale) {
|
|
38
|
+
context.body = i18next.getFixedT(newProfiles.locale)('text.profile changed successfully')
|
|
39
|
+
} else {
|
|
40
|
+
context.body = t('text.profile changed successfully')
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
.post('/delete-user', async (context, next) => {
|
|
44
|
+
const { t, session } = context
|
|
45
|
+
var { user } = context.state
|
|
46
|
+
var { email: userEmail } = user
|
|
47
|
+
|
|
48
|
+
var { password, email } = context.request.body
|
|
49
|
+
|
|
50
|
+
const userRepo = getRepository(User)
|
|
51
|
+
const userInfo = await userRepo.findOne({
|
|
52
|
+
where: {
|
|
53
|
+
email: ILike(userEmail)
|
|
54
|
+
},
|
|
55
|
+
relations: ['domains']
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
if (email != userEmail || !User.verify(userInfo.password, password, userInfo.salt)) {
|
|
59
|
+
context.status = 401
|
|
60
|
+
context.body = t('error.user validation failed')
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await deleteUser(user)
|
|
65
|
+
|
|
66
|
+
context.body = t('text.delete account succeed')
|
|
67
|
+
clearAccessTokenCookie(context)
|
|
68
|
+
})
|
|
69
|
+
.get('/profile', async (context, next) => {
|
|
70
|
+
const { t } = context
|
|
71
|
+
const { domain, user, unsafeIP, prohibitedPrivileges } = context.state
|
|
72
|
+
|
|
73
|
+
if (!domain) {
|
|
74
|
+
context.status = 401
|
|
75
|
+
context.body = t('error.user validation failed')
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let domains: Partial<Domain>[] = await getUserDomains(user)
|
|
80
|
+
domains = domains.filter((d: Domain) => d.extType == domainType)
|
|
81
|
+
|
|
82
|
+
var privileges = await User.getPrivilegesByDomain(user, domain)
|
|
83
|
+
|
|
84
|
+
if (prohibitedPrivileges) {
|
|
85
|
+
prohibitedPrivileges.forEach(({ category, privilege }) => {
|
|
86
|
+
privileges = privileges.filter(p => p.category != category || p.privilege != privilege)
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
context.body = {
|
|
91
|
+
user: {
|
|
92
|
+
email: user.email,
|
|
93
|
+
name: user.name,
|
|
94
|
+
userType: user.userType,
|
|
95
|
+
owner: await process.domainOwnerGranted(domain, user),
|
|
96
|
+
super: await process.superUserGranted(domain, user),
|
|
97
|
+
unsafeIP,
|
|
98
|
+
privileges
|
|
99
|
+
},
|
|
100
|
+
domains,
|
|
101
|
+
domain: domain && {
|
|
102
|
+
name: domain.name,
|
|
103
|
+
subdomain: domain.subdomain
|
|
104
|
+
},
|
|
105
|
+
languages
|
|
106
|
+
}
|
|
107
|
+
})
|