@drax/identity-back 3.13.0 → 3.15.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.
- package/dist/config/PasswordPolicyConfig.js +14 -0
- package/dist/controllers/UserApiKeyController.js +8 -119
- package/dist/controllers/UserController.js +10 -0
- package/dist/factory/PasswordPolicyResolverFactory.js +9 -0
- package/dist/factory/PasswordPolicyServiceFactory.js +27 -0
- package/dist/factory/UserPasswordHistoryServiceFactory.js +25 -0
- package/dist/factory/UserServiceFactory.js +3 -1
- package/dist/graphql/resolvers/user-api-key.resolvers.js +2 -3
- package/dist/index.js +20 -8
- package/dist/interfaces/IUserPasswordHistory.js +1 -0
- package/dist/interfaces/IUserPasswordHistoryRepository.js +1 -0
- package/dist/middleware/apiKeyMiddleware.js +1 -1
- package/dist/middleware/rbacMiddleware.js +1 -1
- package/dist/models/UserPasswordHistoryModel.js +30 -0
- package/dist/permissions/UserApiKeyPermissions.js +1 -2
- package/dist/policies/defaultPasswordPolicy.js +12 -0
- package/dist/repository/mongo/UserPasswordHistoryMongoRepository.js +20 -0
- package/dist/repository/sqlite/UserPasswordHistorySqliteRepository.js +38 -0
- package/dist/resolver/PasswordPolicyResolver.js +27 -0
- package/dist/routes/UserRoutes.js +10 -0
- package/dist/schemas/PasswordPolicySchema.js +18 -0
- package/dist/schemas/RegisterSchema.js +1 -3
- package/dist/schemas/UserSchema.js +1 -3
- package/dist/security/constants/defaultPasswordPolicy.js +12 -0
- package/dist/security/interfaces/IPasswordPolicy.js +1 -0
- package/dist/security/interfaces/IPasswordPolicyProjectContext.js +1 -0
- package/dist/security/schemas/PasswordPolicySchema.js +18 -0
- package/dist/security/services/PasswordPolicyResolver.js +21 -0
- package/dist/security/services/PasswordPolicyService.js +147 -0
- package/dist/security/utils/PasswordPolicySchemaFactory.js +36 -0
- package/dist/security/utils/getPasswordEnvPolicy.js +19 -0
- package/dist/services/PasswordPolicyService.js +147 -0
- package/dist/services/UserPasswordHistoryService.js +18 -0
- package/dist/services/UserService.js +34 -9
- package/dist/setup/LoadIdentityConfigFromEnv.js +10 -0
- package/dist/setup/SetProjectPasswordPolicy.js +7 -0
- package/dist/utils/PasswordPolicySchemaFactory.js +36 -0
- package/dist/utils/getPasswordEnvPolicy.js +19 -0
- package/docs/password-policy.md +33 -0
- package/package.json +6 -6
- package/src/config/PasswordPolicyConfig.ts +14 -0
- package/src/controllers/UserApiKeyController.ts +15 -129
- package/src/controllers/UserController.ts +10 -1
- package/src/factory/PasswordPolicyResolverFactory.ts +14 -0
- package/src/factory/PasswordPolicyServiceFactory.ts +38 -0
- package/src/factory/UserPasswordHistoryServiceFactory.ts +31 -0
- package/src/factory/UserServiceFactory.ts +7 -1
- package/src/graphql/resolvers/user-api-key.resolvers.ts +2 -3
- package/src/index.ts +28 -3
- package/src/interfaces/IUserPasswordHistory.ts +21 -0
- package/src/interfaces/IUserPasswordHistoryRepository.ts +8 -0
- package/src/middleware/apiKeyMiddleware.ts +1 -1
- package/src/middleware/rbacMiddleware.ts +1 -1
- package/src/models/UserPasswordHistoryModel.ts +42 -0
- package/src/permissions/UserApiKeyPermissions.ts +1 -2
- package/src/policies/defaultPasswordPolicy.ts +17 -0
- package/src/repository/mongo/UserPasswordHistoryMongoRepository.ts +25 -0
- package/src/repository/sqlite/UserPasswordHistorySqliteRepository.ts +47 -0
- package/src/resolver/PasswordPolicyResolver.ts +33 -0
- package/src/routes/UserRoutes.ts +11 -0
- package/src/schemas/PasswordPolicySchema.ts +29 -0
- package/src/schemas/RegisterSchema.ts +1 -3
- package/src/schemas/UserSchema.ts +1 -3
- package/src/services/PasswordPolicyService.ts +184 -0
- package/src/services/UserPasswordHistoryService.ts +23 -0
- package/src/services/UserService.ts +38 -9
- package/src/setup/LoadIdentityConfigFromEnv.ts +11 -0
- package/src/setup/SetProjectPasswordPolicy.ts +12 -0
- package/src/utils/PasswordPolicySchemaFactory.ts +47 -0
- package/src/utils/getPasswordEnvPolicy.ts +25 -0
- package/test/data-obj/users/root-mongo-user.ts +1 -1
- package/test/data-obj/users/root-sqlite-user.ts +1 -1
- package/test/endpoints/data/users-data.ts +3 -3
- package/test/endpoints/password-policy-route.test.ts +33 -0
- package/test/endpoints/user-route.test.ts +17 -4
- package/test/security/password-policy-resolver.test.ts +55 -0
- package/test/security/password-policy-schema-factory.test.ts +40 -0
- package/test/services/user-service.test.ts +218 -31
- package/test/setup/TestSetup.ts +22 -4
- package/test/setup/data/basic-user.ts +1 -1
- package/test/setup/data/root-user.ts +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/types/config/PasswordPolicyConfig.d.ts +14 -0
- package/types/config/PasswordPolicyConfig.d.ts.map +1 -0
- package/types/controllers/UserApiKeyController.d.ts +10 -6
- package/types/controllers/UserApiKeyController.d.ts.map +1 -1
- package/types/controllers/UserController.d.ts +1 -0
- package/types/controllers/UserController.d.ts.map +1 -1
- package/types/factory/PasswordPolicyResolverFactory.d.ts +4 -0
- package/types/factory/PasswordPolicyResolverFactory.d.ts.map +1 -0
- package/types/factory/PasswordPolicyServiceFactory.d.ts +4 -0
- package/types/factory/PasswordPolicyServiceFactory.d.ts.map +1 -0
- package/types/factory/UserPasswordHistoryServiceFactory.d.ts +4 -0
- package/types/factory/UserPasswordHistoryServiceFactory.d.ts.map +1 -0
- package/types/factory/UserServiceFactory.d.ts.map +1 -1
- package/types/graphql/resolvers/user-api-key.resolvers.d.ts.map +1 -1
- package/types/index.d.ts +15 -2
- package/types/index.d.ts.map +1 -1
- package/types/interfaces/IUserPasswordHistory.d.ts +17 -0
- package/types/interfaces/IUserPasswordHistory.d.ts.map +1 -0
- package/types/interfaces/IUserPasswordHistoryRepository.d.ts +7 -0
- package/types/interfaces/IUserPasswordHistoryRepository.d.ts.map +1 -0
- package/types/models/UserPasswordHistoryModel.d.ts +15 -0
- package/types/models/UserPasswordHistoryModel.d.ts.map +1 -0
- package/types/permissions/UserApiKeyPermissions.d.ts +1 -2
- package/types/permissions/UserApiKeyPermissions.d.ts.map +1 -1
- package/types/permissions/index.d.ts +0 -2
- package/types/permissions/index.d.ts.map +1 -1
- package/types/policies/defaultPasswordPolicy.d.ts +4 -0
- package/types/policies/defaultPasswordPolicy.d.ts.map +1 -0
- package/types/repository/mongo/UserPasswordHistoryMongoRepository.d.ts +10 -0
- package/types/repository/mongo/UserPasswordHistoryMongoRepository.d.ts.map +1 -0
- package/types/repository/sqlite/UserPasswordHistorySqliteRepository.d.ts +25 -0
- package/types/repository/sqlite/UserPasswordHistorySqliteRepository.d.ts.map +1 -0
- package/types/resolver/PasswordPolicyResolver.d.ts +10 -0
- package/types/resolver/PasswordPolicyResolver.d.ts.map +1 -0
- package/types/routes/UserRoutes.d.ts.map +1 -1
- package/types/schemas/PasswordPolicySchema.d.ts +25 -0
- package/types/schemas/PasswordPolicySchema.d.ts.map +1 -0
- package/types/schemas/RegisterSchema.d.ts.map +1 -1
- package/types/schemas/UserSchema.d.ts.map +1 -1
- package/types/security/constants/defaultPasswordPolicy.d.ts +4 -0
- package/types/security/constants/defaultPasswordPolicy.d.ts.map +1 -0
- package/types/security/interfaces/IPasswordPolicy.d.ts +13 -0
- package/types/security/interfaces/IPasswordPolicy.d.ts.map +1 -0
- package/types/security/interfaces/IPasswordPolicyProjectContext.d.ts +6 -0
- package/types/security/interfaces/IPasswordPolicyProjectContext.d.ts.map +1 -0
- package/types/security/schemas/PasswordPolicySchema.d.ts +25 -0
- package/types/security/schemas/PasswordPolicySchema.d.ts.map +1 -0
- package/types/security/services/PasswordPolicyResolver.d.ts +9 -0
- package/types/security/services/PasswordPolicyResolver.d.ts.map +1 -0
- package/types/security/services/PasswordPolicyService.d.ts +35 -0
- package/types/security/services/PasswordPolicyService.d.ts.map +1 -0
- package/types/security/utils/PasswordPolicySchemaFactory.d.ts +9 -0
- package/types/security/utils/PasswordPolicySchemaFactory.d.ts.map +1 -0
- package/types/security/utils/getPasswordEnvPolicy.d.ts +5 -0
- package/types/security/utils/getPasswordEnvPolicy.d.ts.map +1 -0
- package/types/services/PasswordPolicyService.d.ts +34 -0
- package/types/services/PasswordPolicyService.d.ts.map +1 -0
- package/types/services/UserPasswordHistoryService.d.ts +10 -0
- package/types/services/UserPasswordHistoryService.d.ts.map +1 -0
- package/types/services/UserService.d.ts +5 -1
- package/types/services/UserService.d.ts.map +1 -1
- package/types/setup/LoadIdentityConfigFromEnv.d.ts.map +1 -1
- package/types/setup/SetProjectPasswordPolicy.d.ts +5 -0
- package/types/setup/SetProjectPasswordPolicy.d.ts.map +1 -0
- package/types/utils/PasswordPolicySchemaFactory.d.ts +9 -0
- package/types/utils/PasswordPolicySchemaFactory.d.ts.map +1 -0
- package/types/utils/getPasswordEnvPolicy.d.ts +5 -0
- package/types/utils/getPasswordEnvPolicy.d.ts.map +1 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
var PasswordPolicyConfig;
|
|
2
|
+
(function (PasswordPolicyConfig) {
|
|
3
|
+
PasswordPolicyConfig["MinLength"] = "PASSWORD_POLICY_MIN_LENGTH";
|
|
4
|
+
PasswordPolicyConfig["MaxLength"] = "PASSWORD_POLICY_MAX_LENGTH";
|
|
5
|
+
PasswordPolicyConfig["RequireUppercase"] = "PASSWORD_POLICY_REQUIRE_UPPERCASE";
|
|
6
|
+
PasswordPolicyConfig["RequireLowercase"] = "PASSWORD_POLICY_REQUIRE_LOWERCASE";
|
|
7
|
+
PasswordPolicyConfig["RequireNumber"] = "PASSWORD_POLICY_REQUIRE_NUMBER";
|
|
8
|
+
PasswordPolicyConfig["RequireSpecialChar"] = "PASSWORD_POLICY_REQUIRE_SPECIAL_CHAR";
|
|
9
|
+
PasswordPolicyConfig["DisallowSpaces"] = "PASSWORD_POLICY_DISALLOW_SPACES";
|
|
10
|
+
PasswordPolicyConfig["PreventReuse"] = "PASSWORD_POLICY_PREVENT_REUSE";
|
|
11
|
+
PasswordPolicyConfig["ExpirationDays"] = "PASSWORD_POLICY_EXPIRATION_DAYS";
|
|
12
|
+
})(PasswordPolicyConfig || (PasswordPolicyConfig = {}));
|
|
13
|
+
export default PasswordPolicyConfig;
|
|
14
|
+
export { PasswordPolicyConfig };
|
|
@@ -1,129 +1,18 @@
|
|
|
1
1
|
import { AbstractFastifyController } from "@drax/crud-back";
|
|
2
|
-
import { ValidationError, UnauthorizedError } from "@drax/common-back";
|
|
3
2
|
import UserApiKeyServiceFactory from "../factory/UserApiKeyServiceFactory.js";
|
|
4
3
|
import UserApiKeyPermissions from "../permissions/UserApiKeyPermissions.js";
|
|
5
4
|
class UserApiKeyController extends AbstractFastifyController {
|
|
6
5
|
constructor() {
|
|
7
6
|
super(UserApiKeyServiceFactory(), UserApiKeyPermissions);
|
|
7
|
+
this.userField = 'user';
|
|
8
|
+
this.userFilter = true;
|
|
9
|
+
this.userSetter = true;
|
|
10
|
+
this.userAssert = true;
|
|
8
11
|
}
|
|
9
|
-
async
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
UserApiKeyPermissions.View,
|
|
14
|
-
UserApiKeyPermissions.ViewMy
|
|
15
|
-
]);
|
|
16
|
-
const filters = [];
|
|
17
|
-
if (!request.rbac.hasPermission(UserApiKeyPermissions.View)) {
|
|
18
|
-
filters.push({ field: "user", operator: "eq", value: request.rbac.userId });
|
|
19
|
-
}
|
|
20
|
-
const page = request.query.page;
|
|
21
|
-
const limit = request.query.limit;
|
|
22
|
-
const orderBy = request.query.orderBy;
|
|
23
|
-
const order = request.query.order;
|
|
24
|
-
const search = request.query.search;
|
|
25
|
-
const userApiKeyService = UserApiKeyServiceFactory();
|
|
26
|
-
let paginateResult = await userApiKeyService.paginate({ page, limit, orderBy, order, search, filters });
|
|
27
|
-
return paginateResult;
|
|
28
|
-
}
|
|
29
|
-
catch (e) {
|
|
30
|
-
console.log("/api/user-api-keys", e);
|
|
31
|
-
if (e instanceof ValidationError) {
|
|
32
|
-
reply.statusCode = e.statusCode;
|
|
33
|
-
reply.send({ error: e.message, inputErrors: e.errors });
|
|
34
|
-
}
|
|
35
|
-
else if (e instanceof UnauthorizedError) {
|
|
36
|
-
reply.statusCode = e.statusCode;
|
|
37
|
-
reply.send({ error: e.message });
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
reply.statusCode = 500;
|
|
41
|
-
reply.send({ error: 'error.server' });
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
async create(request, reply) {
|
|
46
|
-
try {
|
|
47
|
-
request.rbac.assertOrPermissions([UserApiKeyPermissions.Create, UserApiKeyPermissions.CreateMy]);
|
|
48
|
-
const payload = request.body;
|
|
49
|
-
if (!request.rbac.hasPermission(UserApiKeyPermissions.Create) || !payload.user) {
|
|
50
|
-
payload.user = request.rbac.userId;
|
|
51
|
-
}
|
|
52
|
-
payload.createdBy = request.rbac.userId;
|
|
53
|
-
const userApiKeyService = UserApiKeyServiceFactory();
|
|
54
|
-
let userApiKey = await userApiKeyService.create(payload);
|
|
55
|
-
return userApiKey;
|
|
56
|
-
}
|
|
57
|
-
catch (e) {
|
|
58
|
-
if (e instanceof ValidationError) {
|
|
59
|
-
reply.statusCode = e.statusCode;
|
|
60
|
-
reply.send({ error: e.message, inputErrors: e.errors });
|
|
61
|
-
}
|
|
62
|
-
else if (e instanceof UnauthorizedError) {
|
|
63
|
-
reply.statusCode = e.statusCode;
|
|
64
|
-
reply.send({ error: e.message });
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
reply.statusCode = 500;
|
|
68
|
-
reply.send({ error: 'error.server' });
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
async update(request, reply) {
|
|
73
|
-
try {
|
|
74
|
-
request.rbac.assertPermission(UserApiKeyPermissions.Update);
|
|
75
|
-
const id = request.params.id;
|
|
76
|
-
const payload = request.body;
|
|
77
|
-
const userApiKeyService = UserApiKeyServiceFactory();
|
|
78
|
-
let userApiKey = await userApiKeyService.update(id, payload);
|
|
79
|
-
return userApiKey;
|
|
80
|
-
}
|
|
81
|
-
catch (e) {
|
|
82
|
-
if (e instanceof ValidationError) {
|
|
83
|
-
reply.statusCode = e.statusCode;
|
|
84
|
-
reply.send({ error: e.message, inputErrors: e.errors });
|
|
85
|
-
}
|
|
86
|
-
if (e instanceof UnauthorizedError) {
|
|
87
|
-
reply.statusCode = e.statusCode;
|
|
88
|
-
reply.send({ error: e.message });
|
|
89
|
-
}
|
|
90
|
-
else if (e instanceof UnauthorizedError) {
|
|
91
|
-
reply.statusCode = e.statusCode;
|
|
92
|
-
reply.send({ error: e.message });
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
reply.statusCode = 500;
|
|
96
|
-
reply.send({ error: 'error.server' });
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
async delete(request, reply) {
|
|
101
|
-
try {
|
|
102
|
-
request.rbac.assertPermission(UserApiKeyPermissions.Delete);
|
|
103
|
-
const id = request.params.id;
|
|
104
|
-
const userApiKeyService = UserApiKeyServiceFactory();
|
|
105
|
-
let r = await userApiKeyService.delete(id);
|
|
106
|
-
if (r) {
|
|
107
|
-
reply.send({ message: 'Deleted successfully' });
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
reply.statusCode(400).send({ message: 'Not deleted' });
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
catch (e) {
|
|
114
|
-
if (e instanceof ValidationError) {
|
|
115
|
-
reply.statusCode = e.statusCode;
|
|
116
|
-
reply.send({ error: e.message, inputErrors: e.errors });
|
|
117
|
-
}
|
|
118
|
-
else if (e instanceof UnauthorizedError) {
|
|
119
|
-
reply.statusCode = e.statusCode;
|
|
120
|
-
reply.send({ error: e.message });
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
reply.statusCode = 500;
|
|
124
|
-
reply.send({ error: 'error.server' });
|
|
125
|
-
}
|
|
126
|
-
}
|
|
12
|
+
async preCreate(request, payload) {
|
|
13
|
+
request.rbac.assertAuthenticated();
|
|
14
|
+
payload.createdBy = request.rbac.userId;
|
|
15
|
+
return payload;
|
|
127
16
|
}
|
|
128
17
|
}
|
|
129
18
|
export default UserApiKeyController;
|
|
@@ -9,6 +9,7 @@ import { join } from "path";
|
|
|
9
9
|
import { IdentityConfig } from "../config/IdentityConfig.js";
|
|
10
10
|
import UserEmailService from "../services/UserEmailService.js";
|
|
11
11
|
import TenantServiceFactory from "../factory/TenantServiceFactory.js";
|
|
12
|
+
import PasswordPolicyServiceFactory from "../factory/PasswordPolicyServiceFactory.js";
|
|
12
13
|
const BASE_FILE_DIR = DraxConfig.getOrLoad(CommonConfig.FileDir) || 'files';
|
|
13
14
|
const AVATAR_DIR = DraxConfig.getOrLoad(IdentityConfig.AvatarDir) || 'avatar';
|
|
14
15
|
const BASE_URL = DraxConfig.getOrLoad(CommonConfig.BaseUrl) ? DraxConfig.get(CommonConfig.BaseUrl).replace(/\/$/, '') : '';
|
|
@@ -72,6 +73,15 @@ class UserController extends AbstractFastifyController {
|
|
|
72
73
|
reply.send({ error: 'error.server' });
|
|
73
74
|
}
|
|
74
75
|
}
|
|
76
|
+
async passwordPolicy(request, reply) {
|
|
77
|
+
try {
|
|
78
|
+
const passwordPolicyService = PasswordPolicyServiceFactory();
|
|
79
|
+
return await passwordPolicyService.getFinalPolicy();
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
this.handleError(e, reply);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
75
85
|
async me(request, reply) {
|
|
76
86
|
try {
|
|
77
87
|
if (request.authUser) {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import PasswordPolicyResolver from "../resolver/PasswordPolicyResolver.js";
|
|
2
|
+
let passwordPolicyResolver;
|
|
3
|
+
const PasswordPolicyResolverFactory = () => {
|
|
4
|
+
if (!passwordPolicyResolver) {
|
|
5
|
+
passwordPolicyResolver = new PasswordPolicyResolver();
|
|
6
|
+
}
|
|
7
|
+
return passwordPolicyResolver;
|
|
8
|
+
};
|
|
9
|
+
export default PasswordPolicyResolverFactory;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import PasswordPolicyService from "../services/PasswordPolicyService.js";
|
|
2
|
+
import PasswordPolicyResolverFactory from "./PasswordPolicyResolverFactory.js";
|
|
3
|
+
import UserPasswordHistoryServiceFactory from "./UserPasswordHistoryServiceFactory.js";
|
|
4
|
+
import UserMongoRepository from "../repository/mongo/UserMongoRepository.js";
|
|
5
|
+
import UserSqliteRepository from "../repository/sqlite/UserSqliteRepository.js";
|
|
6
|
+
import { COMMON, CommonConfig, DraxConfig } from "@drax/common-back";
|
|
7
|
+
let passwordPolicyService;
|
|
8
|
+
const PasswordPolicyServiceFactory = (verbose = false) => {
|
|
9
|
+
if (!passwordPolicyService) {
|
|
10
|
+
let userRepository;
|
|
11
|
+
switch (DraxConfig.getOrLoad(CommonConfig.DbEngine)) {
|
|
12
|
+
case COMMON.DB_ENGINES.MONGODB:
|
|
13
|
+
userRepository = new UserMongoRepository();
|
|
14
|
+
break;
|
|
15
|
+
case COMMON.DB_ENGINES.SQLITE:
|
|
16
|
+
userRepository = new UserSqliteRepository(DraxConfig.getOrLoad(CommonConfig.SqliteDbFile), verbose);
|
|
17
|
+
userRepository.build();
|
|
18
|
+
break;
|
|
19
|
+
default:
|
|
20
|
+
throw new Error("DraxConfig.DB_ENGINE must be one of " + Object.values(COMMON.DB_ENGINES).join(", "));
|
|
21
|
+
}
|
|
22
|
+
const passwordPolicyResolver = PasswordPolicyResolverFactory();
|
|
23
|
+
passwordPolicyService = new PasswordPolicyService(passwordPolicyResolver, userRepository, UserPasswordHistoryServiceFactory(verbose));
|
|
24
|
+
}
|
|
25
|
+
return passwordPolicyService;
|
|
26
|
+
};
|
|
27
|
+
export default PasswordPolicyServiceFactory;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { COMMON, CommonConfig, DraxConfig } from "@drax/common-back";
|
|
2
|
+
import UserPasswordHistoryMongoRepository from "../repository/mongo/UserPasswordHistoryMongoRepository.js";
|
|
3
|
+
import UserPasswordHistorySqliteRepository from "../repository/sqlite/UserPasswordHistorySqliteRepository.js";
|
|
4
|
+
import UserPasswordHistoryService from "../services/UserPasswordHistoryService.js";
|
|
5
|
+
let userPasswordHistoryService;
|
|
6
|
+
const UserPasswordHistoryServiceFactory = (verbose = false) => {
|
|
7
|
+
if (!userPasswordHistoryService) {
|
|
8
|
+
let repository;
|
|
9
|
+
switch (DraxConfig.getOrLoad(CommonConfig.DbEngine)) {
|
|
10
|
+
case COMMON.DB_ENGINES.MONGODB:
|
|
11
|
+
repository = new UserPasswordHistoryMongoRepository();
|
|
12
|
+
break;
|
|
13
|
+
case COMMON.DB_ENGINES.SQLITE:
|
|
14
|
+
const sqliteRepository = new UserPasswordHistorySqliteRepository(DraxConfig.getOrLoad(CommonConfig.SqliteDbFile), verbose);
|
|
15
|
+
sqliteRepository.build();
|
|
16
|
+
repository = sqliteRepository;
|
|
17
|
+
break;
|
|
18
|
+
default:
|
|
19
|
+
throw new Error("DraxConfig.DB_ENGINE must be one of " + Object.values(COMMON.DB_ENGINES).join(", "));
|
|
20
|
+
}
|
|
21
|
+
userPasswordHistoryService = new UserPasswordHistoryService(repository);
|
|
22
|
+
}
|
|
23
|
+
return userPasswordHistoryService;
|
|
24
|
+
};
|
|
25
|
+
export default UserPasswordHistoryServiceFactory;
|
|
@@ -2,6 +2,8 @@ import UserMongoRepository from "../repository/mongo/UserMongoRepository.js";
|
|
|
2
2
|
import UserService from "../services/UserService.js";
|
|
3
3
|
import UserSqliteRepository from "../repository/sqlite/UserSqliteRepository.js";
|
|
4
4
|
import { COMMON, CommonConfig, DraxConfig } from "@drax/common-back";
|
|
5
|
+
import PasswordPolicyServiceFactory from "./PasswordPolicyServiceFactory.js";
|
|
6
|
+
import UserPasswordHistoryServiceFactory from "./UserPasswordHistoryServiceFactory.js";
|
|
5
7
|
let userService;
|
|
6
8
|
const UserServiceFactory = (verbose = false) => {
|
|
7
9
|
if (!userService) {
|
|
@@ -18,7 +20,7 @@ const UserServiceFactory = (verbose = false) => {
|
|
|
18
20
|
default:
|
|
19
21
|
throw new Error("DraxConfig.DB_ENGINE must be one of " + Object.values(COMMON.DB_ENGINES).join(", "));
|
|
20
22
|
}
|
|
21
|
-
userService = new UserService(userRepository);
|
|
23
|
+
userService = new UserService(userRepository, PasswordPolicyServiceFactory(verbose), UserPasswordHistoryServiceFactory(verbose));
|
|
22
24
|
}
|
|
23
25
|
return userService;
|
|
24
26
|
};
|
|
@@ -9,13 +9,12 @@ export default {
|
|
|
9
9
|
try {
|
|
10
10
|
rbac.assertAuthenticated();
|
|
11
11
|
rbac.assertOrPermissions([
|
|
12
|
-
UserApiKeyPermissions.View
|
|
13
|
-
UserApiKeyPermissions.ViewMy
|
|
12
|
+
UserApiKeyPermissions.View
|
|
14
13
|
]);
|
|
15
14
|
if (!Array.isArray(options.filters)) {
|
|
16
15
|
options.filters = [];
|
|
17
16
|
}
|
|
18
|
-
if (!rbac.hasPermission(UserApiKeyPermissions.
|
|
17
|
+
if (!rbac.hasPermission(UserApiKeyPermissions.ViewAll)) {
|
|
19
18
|
options.filters.push({ field: "user", operator: "eq", value: rbac.userId });
|
|
20
19
|
}
|
|
21
20
|
const userApiKeyService = UserApiKeyServiceFactory();
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,8 @@ import TenantServiceFactory from "./factory/TenantServiceFactory.js";
|
|
|
5
5
|
import UserApiKeyServiceFactory from "./factory/UserApiKeyServiceFactory.js";
|
|
6
6
|
import UserLoginFailServiceFactory from "./factory/UserLoginFailServiceFactory.js";
|
|
7
7
|
import UserSessionServiceFactory from "./factory/UserSessionServiceFactory.js";
|
|
8
|
+
import PasswordPolicyServiceFactory from "./factory/PasswordPolicyServiceFactory.js";
|
|
9
|
+
import UserPasswordHistoryServiceFactory from "./factory/UserPasswordHistoryServiceFactory.js";
|
|
8
10
|
import RoleService from "./services/RoleService.js";
|
|
9
11
|
import UserService from "./services/UserService.js";
|
|
10
12
|
import TenantService from "./services/TenantService.js";
|
|
@@ -12,6 +14,9 @@ import PermissionService from "./services/PermissionService.js";
|
|
|
12
14
|
import UserApiKeyService from "./services/UserApiKeyService.js";
|
|
13
15
|
import UserSessionService from "./services/UserSessionService.js";
|
|
14
16
|
import UserLoginFailService from "./services/UserLoginFailService.js";
|
|
17
|
+
import UserPasswordHistoryService from "./services/UserPasswordHistoryService.js";
|
|
18
|
+
import PasswordPolicyService from "./services/PasswordPolicyService.js";
|
|
19
|
+
import PasswordPolicyResolver from "./resolver/PasswordPolicyResolver.js";
|
|
15
20
|
import Rbac from "./rbac/Rbac.js";
|
|
16
21
|
import { UserRoutes } from "./routes/UserRoutes.js";
|
|
17
22
|
import { RoleRoutes } from "./routes/RoleRoutes.js";
|
|
@@ -24,6 +29,7 @@ import { jwtMiddleware } from "./middleware/jwtMiddleware.js";
|
|
|
24
29
|
import { rbacMiddleware } from "./middleware/rbacMiddleware.js";
|
|
25
30
|
import { apiKeyMiddleware } from "./middleware/apiKeyMiddleware.js";
|
|
26
31
|
import IdentityConfig from "./config/IdentityConfig.js";
|
|
32
|
+
import PasswordPolicyConfig from "./config/PasswordPolicyConfig.js";
|
|
27
33
|
import BadCredentialsError from "./errors/BadCredentialsError.js";
|
|
28
34
|
import CreateUserIfNotExist from "./setup/CreateUserIfNotExist.js";
|
|
29
35
|
import CreateTenantIfNotExist from "./setup/CreateTenantIfNotExist.js";
|
|
@@ -31,24 +37,28 @@ import CreateOrUpdateRole from "./setup/CreateOrUpdateRole.js";
|
|
|
31
37
|
import LoadPermissions from "./setup/LoadPermissions.js";
|
|
32
38
|
import LoadIdentityConfigFromEnv from "./setup/LoadIdentityConfigFromEnv.js";
|
|
33
39
|
import RecoveryUserPassword from "./setup/RecoveryUserPassword.js";
|
|
40
|
+
import SetProjectPasswordPolicy from "./setup/SetProjectPasswordPolicy.js";
|
|
34
41
|
import { RoleModel, RoleMongoSchema } from "./models/RoleModel.js";
|
|
35
42
|
import { TenantModel, TenantMongoSchema } from "./models/TenantModel.js";
|
|
36
43
|
import { UserModel, UserMongoSchema } from "./models/UserModel.js";
|
|
37
44
|
import { UserApiKeyModel, UserApiKeyMongoSchema } from "./models/UserApiKeyModel.js";
|
|
38
45
|
import { UserSessionModel, UserSessionMongoSchema } from "./models/UserSessionModel.js";
|
|
39
46
|
import { UserLoginFailModel, UserLoginFailMongoSchema } from "./models/UserLoginFailModel.js";
|
|
47
|
+
import { UserPasswordHistoryModel, UserPasswordHistoryMongoSchema } from "./models/UserPasswordHistoryModel.js";
|
|
40
48
|
import RoleMongoRepository from "./repository/mongo/RoleMongoRepository.js";
|
|
41
49
|
import TenantMongoRepository from "./repository/mongo/TenantMongoRepository.js";
|
|
42
50
|
import UserMongoRepository from "./repository/mongo/UserMongoRepository.js";
|
|
43
51
|
import UserApiKeyMongoRepository from "./repository/mongo/UserApiKeyMongoRepository.js";
|
|
44
52
|
import UserSessionMongoRepository from "./repository/mongo/UserSessionMongoRepository.js";
|
|
45
53
|
import UserLoginFailMongoRepository from "./repository/mongo/UserLoginFailMongoRepository.js";
|
|
54
|
+
import UserPasswordHistoryMongoRepository from "./repository/mongo/UserPasswordHistoryMongoRepository.js";
|
|
46
55
|
import RoleSqliteRepository from "./repository/sqlite/RoleSqliteRepository.js";
|
|
47
56
|
import TenantSqliteRepository from "./repository/sqlite/TenantSqliteRepository.js";
|
|
48
57
|
import UserSqliteRepository from "./repository/sqlite/UserSqliteRepository.js";
|
|
49
58
|
import UserApiKeySqliteRepository from "./repository/sqlite/UserApiKeySqliteRepository.js";
|
|
50
59
|
import UserLoginFailSqliteRepository from "./repository/sqlite/UserLoginFailSqliteRepository.js";
|
|
51
60
|
import UserSessionSqliteRepository from "./repository/sqlite/UserSessionSqliteRepository.js";
|
|
61
|
+
import UserPasswordHistorySqliteRepository from "./repository/sqlite/UserPasswordHistorySqliteRepository.js";
|
|
52
62
|
import { RolePermissions } from "./permissions/RolePermissions.js";
|
|
53
63
|
import { TenantPermissions } from "./permissions/TenantPermissions.js";
|
|
54
64
|
import { UserPermissions } from "./permissions/UserPermissions.js";
|
|
@@ -61,6 +71,8 @@ import { RoleSchema, RoleBaseSchema } from "./schemas/RoleSchema.js";
|
|
|
61
71
|
import { UserApiKeySchema, UserApiKeyBaseSchema } from "./schemas/UserApiKeySchema.js";
|
|
62
72
|
import { UserLoginFailBaseSchema } from "./schemas/UserLoginFailSchema.js";
|
|
63
73
|
import { UserSessionBaseSchema } from "./schemas/UserSessionSchema.js";
|
|
74
|
+
import { defaultPasswordPolicy } from "./policies/defaultPasswordPolicy.js";
|
|
75
|
+
import { PasswordPolicySchema } from "./schemas/PasswordPolicySchema.js";
|
|
64
76
|
const graphqlMergeResult = await GraphqlMerge();
|
|
65
77
|
const identityTypeDefs = await graphqlMergeResult.typeDefs;
|
|
66
78
|
const identityResolvers = await graphqlMergeResult.resolvers;
|
|
@@ -68,9 +80,9 @@ export {
|
|
|
68
80
|
//Schemas
|
|
69
81
|
UserSchema, UserBaseSchema, TenantSchema, TenantBaseSchema, RoleSchema, RoleBaseSchema, UserApiKeyBaseSchema, UserApiKeySchema, UserLoginFailBaseSchema, UserSessionBaseSchema,
|
|
70
82
|
//Service
|
|
71
|
-
UserService, RoleService, TenantService, UserApiKeyService, UserSessionService, UserLoginFailService, PermissionService, Rbac,
|
|
83
|
+
UserService, RoleService, TenantService, UserApiKeyService, UserSessionService, UserLoginFailService, UserPasswordHistoryService, PasswordPolicyService, PasswordPolicyResolver, PermissionService, Rbac,
|
|
72
84
|
//Factories
|
|
73
|
-
UserServiceFactory, RoleServiceFactory, TenantServiceFactory, UserApiKeyServiceFactory, UserSessionServiceFactory, UserLoginFailServiceFactory,
|
|
85
|
+
UserServiceFactory, RoleServiceFactory, TenantServiceFactory, UserApiKeyServiceFactory, UserSessionServiceFactory, UserLoginFailServiceFactory, UserPasswordHistoryServiceFactory, PasswordPolicyServiceFactory,
|
|
74
86
|
//GQL
|
|
75
87
|
identityTypeDefs, identityResolvers,
|
|
76
88
|
//API REST
|
|
@@ -80,14 +92,14 @@ jwtMiddleware, rbacMiddleware, apiKeyMiddleware,
|
|
|
80
92
|
//Permissions
|
|
81
93
|
RolePermissions, TenantPermissions, UserPermissions, UserApiKeyPermissions, UserSessionPermissions, UserLoginFailPermissions,
|
|
82
94
|
//Mongo Repositories
|
|
83
|
-
RoleMongoRepository, TenantMongoRepository, UserMongoRepository, UserApiKeyMongoRepository, UserSessionMongoRepository, UserLoginFailMongoRepository,
|
|
95
|
+
RoleMongoRepository, TenantMongoRepository, UserMongoRepository, UserApiKeyMongoRepository, UserSessionMongoRepository, UserLoginFailMongoRepository, UserPasswordHistoryMongoRepository,
|
|
84
96
|
//Mongo Models
|
|
85
|
-
RoleModel, TenantModel, UserModel, UserApiKeyModel, UserSessionModel, UserLoginFailModel, RoleMongoSchema, TenantMongoSchema, UserMongoSchema, UserApiKeyMongoSchema, UserSessionMongoSchema, UserLoginFailMongoSchema,
|
|
97
|
+
RoleModel, TenantModel, UserModel, UserApiKeyModel, UserSessionModel, UserLoginFailModel, UserPasswordHistoryModel, RoleMongoSchema, TenantMongoSchema, UserMongoSchema, UserApiKeyMongoSchema, UserSessionMongoSchema, UserLoginFailMongoSchema, UserPasswordHistoryMongoSchema,
|
|
86
98
|
//Sqlite Repositories
|
|
87
|
-
RoleSqliteRepository, TenantSqliteRepository, UserSqliteRepository, UserApiKeySqliteRepository, UserLoginFailSqliteRepository, UserSessionSqliteRepository,
|
|
99
|
+
RoleSqliteRepository, TenantSqliteRepository, UserSqliteRepository, UserApiKeySqliteRepository, UserLoginFailSqliteRepository, UserSessionSqliteRepository, UserPasswordHistorySqliteRepository,
|
|
88
100
|
//Config
|
|
89
|
-
IdentityConfig,
|
|
101
|
+
IdentityConfig, PasswordPolicyConfig,
|
|
90
102
|
//Errors
|
|
91
|
-
BadCredentialsError,
|
|
103
|
+
BadCredentialsError, defaultPasswordPolicy, PasswordPolicySchema,
|
|
92
104
|
//Setup
|
|
93
|
-
LoadIdentityConfigFromEnv, LoadPermissions, CreateOrUpdateRole, CreateUserIfNotExist, CreateTenantIfNotExist, RecoveryUserPassword };
|
|
105
|
+
LoadIdentityConfigFromEnv, LoadPermissions, CreateOrUpdateRole, CreateUserIfNotExist, CreateTenantIfNotExist, RecoveryUserPassword, SetProjectPasswordPolicy };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -3,7 +3,7 @@ import UserApiKeyServiceFactory from "../factory/UserApiKeyServiceFactory.js";
|
|
|
3
3
|
import IdentityConfig from "../config/IdentityConfig.js";
|
|
4
4
|
const verifyIp = DraxConfig.getOrLoad(IdentityConfig.VerifyIP, 'boolean', true);
|
|
5
5
|
const cacheTTL = DraxConfig.getOrLoad(IdentityConfig.ApiKeyCacheTTL, 'number', 10000);
|
|
6
|
-
const draxCache = new DraxCache(cacheTTL);
|
|
6
|
+
const draxCache = new DraxCache({ ttl: cacheTTL, namespace: 'identity:api-key' });
|
|
7
7
|
async function userApiKeyLoader(k) {
|
|
8
8
|
const userApiKeyService = UserApiKeyServiceFactory();
|
|
9
9
|
const userApiKey = await userApiKeyService.findBySecret(k);
|
|
@@ -3,7 +3,7 @@ import RoleServiceFactory from "../factory/RoleServiceFactory.js";
|
|
|
3
3
|
import Rbac from "../rbac/Rbac.js";
|
|
4
4
|
import IdentityConfig from "../config/IdentityConfig.js";
|
|
5
5
|
const cacheTTL = DraxConfig.getOrLoad(IdentityConfig.RbacCacheTTL, 'number', 10000);
|
|
6
|
-
const draxCache = new DraxCache(cacheTTL);
|
|
6
|
+
const draxCache = new DraxCache({ ttl: cacheTTL, namespace: 'identity:role' });
|
|
7
7
|
async function roleLoader(k) {
|
|
8
8
|
const roleService = RoleServiceFactory();
|
|
9
9
|
const role = await roleService.findById(k);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { mongoose } from "@drax/common-back";
|
|
2
|
+
import uniqueValidator from "mongoose-unique-validator";
|
|
3
|
+
import mongoosePaginate from "mongoose-paginate-v2";
|
|
4
|
+
const UserPasswordHistoryMongoSchema = new mongoose.Schema({
|
|
5
|
+
user: { type: String, required: true, index: true },
|
|
6
|
+
passwordHash: { type: String, required: true, index: false }
|
|
7
|
+
}, { timestamps: true });
|
|
8
|
+
UserPasswordHistoryMongoSchema.plugin(uniqueValidator, { message: "validation.unique" });
|
|
9
|
+
UserPasswordHistoryMongoSchema.plugin(mongoosePaginate);
|
|
10
|
+
UserPasswordHistoryMongoSchema.virtual("id").get(function () {
|
|
11
|
+
return this._id.toString();
|
|
12
|
+
});
|
|
13
|
+
UserPasswordHistoryMongoSchema.set("toJSON", { getters: true, virtuals: true });
|
|
14
|
+
UserPasswordHistoryMongoSchema.set("toObject", { getters: true, virtuals: true });
|
|
15
|
+
const MODEL_NAME = "UserPasswordHistory";
|
|
16
|
+
const COLLECTION_NAME = "user_password_history";
|
|
17
|
+
let UserPasswordHistoryModel;
|
|
18
|
+
try {
|
|
19
|
+
UserPasswordHistoryModel = mongoose.model(MODEL_NAME, UserPasswordHistoryMongoSchema, COLLECTION_NAME);
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
if (e.name === "OverwriteModelError") {
|
|
23
|
+
UserPasswordHistoryModel = mongoose.model(MODEL_NAME);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
throw e;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export { UserPasswordHistoryMongoSchema, UserPasswordHistoryModel };
|
|
30
|
+
export default UserPasswordHistoryModel;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
var UserApiKeyPermissions;
|
|
2
2
|
(function (UserApiKeyPermissions) {
|
|
3
3
|
UserApiKeyPermissions["Create"] = "userApiKey:create";
|
|
4
|
-
UserApiKeyPermissions["CreateMy"] = "userApiKey:createMy";
|
|
5
4
|
UserApiKeyPermissions["Update"] = "userApiKey:update";
|
|
6
5
|
UserApiKeyPermissions["Delete"] = "userApiKey:delete";
|
|
7
6
|
UserApiKeyPermissions["View"] = "userApiKey:view";
|
|
8
|
-
UserApiKeyPermissions["
|
|
7
|
+
UserApiKeyPermissions["ViewAll"] = "userApiKey:viewAll";
|
|
9
8
|
UserApiKeyPermissions["Manage"] = "userApiKey:manage";
|
|
10
9
|
})(UserApiKeyPermissions || (UserApiKeyPermissions = {}));
|
|
11
10
|
export default UserApiKeyPermissions;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const defaultPasswordPolicy = {
|
|
2
|
+
minLength: 8,
|
|
3
|
+
maxLength: 64,
|
|
4
|
+
requireUppercase: true,
|
|
5
|
+
requireLowercase: true,
|
|
6
|
+
requireNumber: true,
|
|
7
|
+
requireSpecialChar: false,
|
|
8
|
+
disallowSpaces: true,
|
|
9
|
+
preventReuse: 3,
|
|
10
|
+
expirationDays: null
|
|
11
|
+
};
|
|
12
|
+
export { defaultPasswordPolicy };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AbstractMongoRepository } from "@drax/crud-back";
|
|
2
|
+
import { UserPasswordHistoryModel } from "../../models/UserPasswordHistoryModel.js";
|
|
3
|
+
class UserPasswordHistoryMongoRepository extends AbstractMongoRepository {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
this._model = UserPasswordHistoryModel;
|
|
7
|
+
this._searchFields = ["user"];
|
|
8
|
+
this._populateFields = ["user"];
|
|
9
|
+
}
|
|
10
|
+
async findLatestByUserId(userId, limit) {
|
|
11
|
+
return UserPasswordHistoryModel
|
|
12
|
+
.find({ user: userId })
|
|
13
|
+
.sort({ createdAt: -1 })
|
|
14
|
+
.limit(limit)
|
|
15
|
+
.lean(true)
|
|
16
|
+
.exec();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export default UserPasswordHistoryMongoRepository;
|
|
20
|
+
export { UserPasswordHistoryMongoRepository };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AbstractSqliteRepository } from "@drax/crud-back";
|
|
2
|
+
class UserPasswordHistorySqliteRepository extends AbstractSqliteRepository {
|
|
3
|
+
constructor() {
|
|
4
|
+
super(...arguments);
|
|
5
|
+
this.tableName = "user_password_history";
|
|
6
|
+
this.searchFields = [];
|
|
7
|
+
this.booleanFields = [];
|
|
8
|
+
this.identifier = "_id";
|
|
9
|
+
this.populateFields = [
|
|
10
|
+
{ field: "user", table: "users", identifier: "_id" }
|
|
11
|
+
];
|
|
12
|
+
this.tableFields = [
|
|
13
|
+
{ name: "user", type: "TEXT", unique: false, primary: false },
|
|
14
|
+
{ name: "passwordHash", type: "TEXT", unique: false, primary: false },
|
|
15
|
+
{ name: "createdAt", type: "TEXT", unique: false, primary: false },
|
|
16
|
+
{ name: "updatedAt", type: "TEXT", unique: false, primary: false },
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
async prepareData() {
|
|
20
|
+
}
|
|
21
|
+
async prepareItem(item) {
|
|
22
|
+
if (item.createdAt && typeof item.createdAt === "string") {
|
|
23
|
+
item.createdAt = new Date(item.createdAt);
|
|
24
|
+
}
|
|
25
|
+
if (item.updatedAt && typeof item.updatedAt === "string") {
|
|
26
|
+
item.updatedAt = new Date(item.updatedAt);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async findLatestByUserId(userId, limit) {
|
|
30
|
+
const rows = this.db.prepare(`SELECT * FROM ${this.tableName} WHERE user = ? ORDER BY createdAt DESC LIMIT ?`).all(userId, limit);
|
|
31
|
+
for (const row of rows) {
|
|
32
|
+
await this.decorate(row);
|
|
33
|
+
}
|
|
34
|
+
return rows;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export default UserPasswordHistorySqliteRepository;
|
|
38
|
+
export { UserPasswordHistorySqliteRepository };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defaultPasswordPolicy } from "../policies/defaultPasswordPolicy.js";
|
|
2
|
+
import { PartialPasswordPolicySchema, PasswordPolicySchema } from "../schemas/PasswordPolicySchema.js";
|
|
3
|
+
import getPasswordEnvPolicy from "../utils/getPasswordEnvPolicy.js";
|
|
4
|
+
class PasswordPolicyResolver {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.projectPolicy = {};
|
|
7
|
+
}
|
|
8
|
+
setProjectPolicy(projectPolicy) {
|
|
9
|
+
this.projectPolicy = projectPolicy;
|
|
10
|
+
}
|
|
11
|
+
async resolve() {
|
|
12
|
+
const projectPolicy = await this.getProjectPolicy();
|
|
13
|
+
const envPolicy = getPasswordEnvPolicy();
|
|
14
|
+
return PasswordPolicySchema.parse({
|
|
15
|
+
...defaultPasswordPolicy,
|
|
16
|
+
...projectPolicy,
|
|
17
|
+
...envPolicy
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async getProjectPolicy() {
|
|
21
|
+
return this.projectPolicy
|
|
22
|
+
? PartialPasswordPolicySchema.parse(this.projectPolicy)
|
|
23
|
+
: {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export default PasswordPolicyResolver;
|
|
27
|
+
export { PasswordPolicyResolver };
|
|
@@ -6,6 +6,7 @@ import { RegisterBodyRequestSchema, RegisterBodyResponseSchema } from "../schema
|
|
|
6
6
|
import { MyPasswordBodyRequestSchema, PasswordBodyRequestSchema, PasswordBodyResponseSchema } from "../schemas/PasswordSchema.js";
|
|
7
7
|
import { SwitchTenantBodyRequestSchema, SwitchTenantBodyResponseSchema } from "../schemas/SwitchTenantSchema.js";
|
|
8
8
|
import zod from "zod";
|
|
9
|
+
import { PasswordPolicySchema } from "../schemas/PasswordPolicySchema.js";
|
|
9
10
|
async function UserRoutes(fastify, options) {
|
|
10
11
|
const controller = new UserController();
|
|
11
12
|
const schemas = new CrudSchemaBuilder(UserSchema, UserCreateSchema, UserUpdateSchema, 'tenant', 'openapi-3.0', ['Identity']);
|
|
@@ -26,6 +27,15 @@ async function UserRoutes(fastify, options) {
|
|
|
26
27
|
},
|
|
27
28
|
},
|
|
28
29
|
}, (req, rep) => controller.auth(req, rep));
|
|
30
|
+
fastify.get('/api/auth/password-policy', {
|
|
31
|
+
schema: {
|
|
32
|
+
tags: ['Auth'],
|
|
33
|
+
response: {
|
|
34
|
+
200: zod.toJSONSchema(PasswordPolicySchema),
|
|
35
|
+
500: schemas.jsonErrorBodyResponse,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
}, (req, rep) => controller.passwordPolicy(req, rep));
|
|
29
39
|
fastify.get('/api/auth/me', {
|
|
30
40
|
schema: {
|
|
31
41
|
tags: ['Auth'],
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
const PasswordPolicySchemaBase = z.object({
|
|
3
|
+
minLength: z.number().int().min(1),
|
|
4
|
+
maxLength: z.number().int().min(1),
|
|
5
|
+
requireUppercase: z.boolean(),
|
|
6
|
+
requireLowercase: z.boolean(),
|
|
7
|
+
requireNumber: z.boolean(),
|
|
8
|
+
requireSpecialChar: z.boolean(),
|
|
9
|
+
disallowSpaces: z.boolean(),
|
|
10
|
+
preventReuse: z.number().int().min(0),
|
|
11
|
+
expirationDays: z.number().int().min(1).nullable(),
|
|
12
|
+
});
|
|
13
|
+
const PasswordPolicySchema = PasswordPolicySchemaBase.refine((policy) => policy.maxLength >= policy.minLength, {
|
|
14
|
+
message: "validation.password.maxLength",
|
|
15
|
+
path: ["maxLength"]
|
|
16
|
+
});
|
|
17
|
+
const PartialPasswordPolicySchema = PasswordPolicySchemaBase.partial();
|
|
18
|
+
export { PasswordPolicySchema, PartialPasswordPolicySchema };
|
|
@@ -7,9 +7,7 @@ const RegisterBodyRequestSchema = z.object({
|
|
|
7
7
|
email: email("validation.email.invalid"),
|
|
8
8
|
phone: string({ error: "validation.required" }).optional(),
|
|
9
9
|
password: string({ error: "validation.required" })
|
|
10
|
-
.min(1, "validation.required")
|
|
11
|
-
.min(8, "validation.password.min8")
|
|
12
|
-
.max(64, "validation.password.max64"),
|
|
10
|
+
.min(1, "validation.required"),
|
|
13
11
|
});
|
|
14
12
|
const RegisterBodyResponseSchema = z.object({
|
|
15
13
|
success: z.boolean(),
|
|
@@ -13,9 +13,7 @@ const UserBaseSchema = object({
|
|
|
13
13
|
});
|
|
14
14
|
const UserCreateSchema = UserBaseSchema.extend({
|
|
15
15
|
password: string({ error: "validation.required" })
|
|
16
|
-
.min(1, "validation.required")
|
|
17
|
-
.min(8, "validation.password.min8")
|
|
18
|
-
.max(64, "validation.password.max64"),
|
|
16
|
+
.min(1, "validation.required"),
|
|
19
17
|
});
|
|
20
18
|
const UserUpdateSchema = UserBaseSchema.extend({});
|
|
21
19
|
const UserSchema = UserBaseSchema
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const defaultPasswordPolicy = {
|
|
2
|
+
minLength: 8,
|
|
3
|
+
maxLength: 64,
|
|
4
|
+
requireUppercase: true,
|
|
5
|
+
requireLowercase: true,
|
|
6
|
+
requireNumber: true,
|
|
7
|
+
requireSpecialChar: false,
|
|
8
|
+
disallowSpaces: true,
|
|
9
|
+
preventReuse: 3,
|
|
10
|
+
expirationDays: null
|
|
11
|
+
};
|
|
12
|
+
export { defaultPasswordPolicy };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|