@drax/identity-back 0.0.31 → 0.1.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.
Files changed (156) hide show
  1. package/dist/config/IdentityConfig.js +2 -3
  2. package/dist/factory/RoleServiceFactory.js +8 -7
  3. package/dist/factory/TenantServiceFactory.js +8 -7
  4. package/dist/factory/UserApiKeyServiceFactory.js +24 -0
  5. package/dist/factory/UserServiceFactory.js +8 -7
  6. package/dist/graphql/resolvers/user-api-key.resolvers.js +89 -0
  7. package/dist/graphql/resolvers/user.resolvers.js +7 -2
  8. package/dist/graphql/types/userApiKey.graphql +33 -0
  9. package/dist/index.js +4 -2
  10. package/dist/interfaces/IUserApiKeyRepository.js +1 -0
  11. package/dist/middleware/apiKeyMiddleware.js +30 -0
  12. package/dist/middleware/rbacMiddleware.js +0 -1
  13. package/dist/models/UserApiKeyModel.js +44 -0
  14. package/dist/permissions/IdentityPermissions.js +6 -0
  15. package/dist/rbac/Rbac.js +8 -0
  16. package/dist/repository/mongo/UserApiKeyMongoRepository.js +82 -0
  17. package/dist/repository/mongo/UserMongoRepository.js +3 -3
  18. package/dist/repository/sqlite/RoleSqliteRepository.js +16 -18
  19. package/dist/repository/sqlite/TenantSqliteRepository.js +13 -11
  20. package/dist/repository/sqlite/UserApiKeySqliteRepository.js +147 -0
  21. package/dist/repository/sqlite/UserSqliteRepository.js +29 -26
  22. package/dist/routes/UserApiKeyRoutes.js +119 -0
  23. package/dist/routes/UserRoutes.js +5 -1
  24. package/dist/services/TenantService.js +42 -11
  25. package/dist/services/UserApiKeyService.js +90 -0
  26. package/dist/services/UserService.js +34 -8
  27. package/dist/setup/LoadIdentityConfigFromEnv.js +3 -3
  28. package/dist/utils/AuthUtils.js +10 -0
  29. package/dist/zod/UserApiKeyZod.js +9 -0
  30. package/package.json +7 -6
  31. package/src/config/IdentityConfig.ts +4 -3
  32. package/src/factory/RoleServiceFactory.ts +11 -11
  33. package/src/factory/TenantServiceFactory.ts +11 -11
  34. package/src/factory/UserApiKeyServiceFactory.ts +30 -0
  35. package/src/factory/UserServiceFactory.ts +8 -7
  36. package/src/graphql/resolvers/tenant.resolvers.ts +0 -1
  37. package/src/graphql/resolvers/user-api-key.resolvers.ts +94 -0
  38. package/src/graphql/resolvers/user.resolvers.ts +9 -2
  39. package/src/graphql/types/userApiKey.graphql +33 -0
  40. package/src/index.ts +10 -0
  41. package/src/interfaces/IUserApiKeyRepository.ts +8 -0
  42. package/src/middleware/apiKeyMiddleware.ts +35 -0
  43. package/src/middleware/rbacMiddleware.ts +1 -2
  44. package/src/models/UserApiKeyModel.ts +59 -0
  45. package/src/permissions/IdentityPermissions.ts +7 -0
  46. package/src/rbac/Rbac.ts +13 -2
  47. package/src/repository/mongo/UserApiKeyMongoRepository.ts +114 -0
  48. package/src/repository/mongo/UserMongoRepository.ts +3 -3
  49. package/src/repository/sqlite/RoleSqliteRepository.ts +28 -20
  50. package/src/repository/sqlite/TenantSqliteRepository.ts +25 -11
  51. package/src/repository/sqlite/UserApiKeySqliteRepository.ts +197 -0
  52. package/src/repository/sqlite/UserSqliteRepository.ts +37 -27
  53. package/src/routes/UserApiKeyRoutes.ts +128 -0
  54. package/src/routes/UserRoutes.ts +5 -1
  55. package/src/services/TenantService.ts +49 -17
  56. package/src/services/UserApiKeyService.ts +111 -0
  57. package/src/services/UserService.ts +74 -48
  58. package/src/setup/LoadIdentityConfigFromEnv.ts +5 -3
  59. package/src/utils/AuthUtils.ts +11 -0
  60. package/src/zod/UserApiKeyZod.ts +15 -0
  61. package/test/data-obj/apikey/root-mongo-user-apikey.ts +10 -0
  62. package/test/data-obj/roles/admin-mongo-role.ts +0 -3
  63. package/test/initializers/RoleMongoInitializer.ts +1 -0
  64. package/test/initializers/RoleSqliteInitializer.ts +1 -0
  65. package/test/initializers/UserMongoInitializer.ts +18 -0
  66. package/test/repository/mongo/user-apikey-mongo-repository.test.ts +73 -0
  67. package/tsconfig.tsbuildinfo +1 -1
  68. package/types/config/IdentityConfig.d.ts +2 -3
  69. package/types/config/IdentityConfig.d.ts.map +1 -1
  70. package/types/errors/BadCredentialsError.d.ts +6 -0
  71. package/types/errors/BadCredentialsError.d.ts.map +1 -0
  72. package/types/errors/UnauthorizedError.d.ts +6 -0
  73. package/types/errors/UnauthorizedError.d.ts.map +1 -0
  74. package/types/factory/RoleServiceFactory.d.ts +4 -0
  75. package/types/factory/RoleServiceFactory.d.ts.map +1 -1
  76. package/types/factory/TenantServiceFactory.d.ts +4 -0
  77. package/types/factory/TenantServiceFactory.d.ts.map +1 -1
  78. package/types/factory/UserApiKeyServiceFactory.d.ts +4 -0
  79. package/types/factory/UserApiKeyServiceFactory.d.ts.map +1 -0
  80. package/types/factory/UserServiceFactory.d.ts +4 -0
  81. package/types/factory/UserServiceFactory.d.ts.map +1 -1
  82. package/types/graphql/index.d.ts +6 -0
  83. package/types/graphql/resolvers/tenant.resolvers.d.ts.map +1 -1
  84. package/types/graphql/resolvers/user-api-key.resolvers.d.ts +37 -0
  85. package/types/graphql/resolvers/user-api-key.resolvers.d.ts.map +1 -0
  86. package/types/graphql/resolvers/user.resolvers.d.ts.map +1 -1
  87. package/types/index.d.ts +35 -0
  88. package/types/index.d.ts.map +1 -1
  89. package/types/interfaces/IRoleRepository.d.ts +9 -0
  90. package/types/interfaces/ITenantRepository.d.ts +9 -0
  91. package/types/interfaces/IUserApiKeyRepository.d.ts +7 -0
  92. package/types/interfaces/IUserApiKeyRepository.d.ts.map +1 -0
  93. package/types/interfaces/IUserRepository.d.ts +10 -0
  94. package/types/middleware/apiKeyMiddleware.d.ts +4 -0
  95. package/types/middleware/apiKeyMiddleware.d.ts.map +1 -0
  96. package/types/middleware/jwtMiddleware.d.ts +4 -0
  97. package/types/middleware/rbacMiddleware.d.ts +4 -0
  98. package/types/middleware/rbacMiddleware.d.ts.map +1 -1
  99. package/types/models/RoleModel.d.ts +16 -0
  100. package/types/models/TenantModel.d.ts +16 -0
  101. package/types/models/UserApiKeyModel.d.ts +16 -0
  102. package/types/models/UserApiKeyModel.d.ts.map +1 -0
  103. package/types/models/UserGroupModel.d.ts +16 -0
  104. package/types/models/UserModel.d.ts +16 -0
  105. package/types/permissions/IdentityPermissions.d.ts +27 -0
  106. package/types/permissions/IdentityPermissions.d.ts.map +1 -0
  107. package/types/rbac/Rbac.d.ts +15 -0
  108. package/types/rbac/Rbac.d.ts.map +1 -1
  109. package/types/repository/mongo/RoleMongoRepository.d.ts +14 -0
  110. package/types/repository/mongo/TenantMongoRepository.d.ts +14 -0
  111. package/types/repository/mongo/UserApiKeyMongoRepository.d.ts +14 -0
  112. package/types/repository/mongo/UserApiKeyMongoRepository.d.ts.map +1 -0
  113. package/types/repository/mongo/UserMongoRepository.d.ts +17 -0
  114. package/types/repository/sqlite/RoleSqliteRepository.d.ts +22 -0
  115. package/types/repository/sqlite/RoleSqliteRepository.d.ts.map +1 -1
  116. package/types/repository/sqlite/TenantSqliteRepository.d.ts +19 -0
  117. package/types/repository/sqlite/TenantSqliteRepository.d.ts.map +1 -1
  118. package/types/repository/sqlite/UserApiKeySqliteRepository.d.ts +19 -0
  119. package/types/repository/sqlite/UserApiKeySqliteRepository.d.ts.map +1 -0
  120. package/types/repository/sqlite/UserSqliteRepository.d.ts +25 -0
  121. package/types/repository/sqlite/UserSqliteRepository.d.ts.map +1 -1
  122. package/types/routes/RoleRoutes.d.ts +4 -0
  123. package/types/routes/TenantRoutes.d.ts +4 -0
  124. package/types/routes/UserApiKeyRoutes.d.ts +4 -0
  125. package/types/routes/UserApiKeyRoutes.d.ts.map +1 -0
  126. package/types/routes/UserAvatarRoutes.d.ts +4 -0
  127. package/types/routes/UserRoutes.d.ts +4 -0
  128. package/types/routes/UserRoutes.d.ts.map +1 -1
  129. package/types/services/PermissionService.d.ts +9 -0
  130. package/types/services/PermissionService.d.ts.map +1 -0
  131. package/types/services/RoleService.d.ts +16 -0
  132. package/types/services/TenantService.d.ts +16 -0
  133. package/types/services/TenantService.d.ts.map +1 -1
  134. package/types/services/UserApiKeyService.d.ts +15 -0
  135. package/types/services/UserApiKeyService.d.ts.map +1 -0
  136. package/types/services/UserService.d.ts +21 -0
  137. package/types/services/UserService.d.ts.map +1 -1
  138. package/types/setup/CreateOrUpdateRole.d.ts +5 -0
  139. package/types/setup/CreateUserIfNotExist.d.ts +5 -0
  140. package/types/setup/LoadIdentityConfigFromEnv.d.ts +4 -0
  141. package/types/setup/LoadIdentityConfigFromEnv.d.ts.map +1 -1
  142. package/types/setup/LoadPermissions.d.ts +4 -0
  143. package/types/setup/LoadPermissions.d.ts.map +1 -0
  144. package/types/setup/RecoveryUserPassword.d.ts +4 -0
  145. package/types/utils/AuthUtils.d.ts +18 -0
  146. package/types/utils/AuthUtils.d.ts.map +1 -1
  147. package/types/zod/RoleZod.d.ts +10 -0
  148. package/types/zod/RoleZod.d.ts.map +1 -0
  149. package/types/zod/TenantZod.d.ts +10 -0
  150. package/types/zod/TenantZod.d.ts.map +1 -0
  151. package/types/zod/UserApiKeyZod.d.ts +16 -0
  152. package/types/zod/UserApiKeyZod.d.ts.map +1 -0
  153. package/types/zod/UserZod.d.ts +53 -0
  154. package/types/zod/UserZod.d.ts.map +1 -0
  155. package/src/utils/DbSetupUtils.ts +0 -41
  156. package/types/utils/DbSetupUtils.d.ts.map +0 -1
@@ -1,11 +1,10 @@
1
1
  var IdentityConfig;
2
2
  (function (IdentityConfig) {
3
- IdentityConfig["DbEngine"] = "DRAX_DB_ENGINE";
4
- IdentityConfig["SqliteDbFile"] = "DRAX_SQLITE_FILE";
5
- IdentityConfig["MongoDbUri"] = "DRAX_MONGO_URI";
6
3
  IdentityConfig["JwtSecret"] = "DRAX_JWT_SECRET";
7
4
  IdentityConfig["JwtExpiration"] = "DRAX_JWT_EXPIRATION";
8
5
  IdentityConfig["JwtIssuer"] = "DRAX_JWT_ISSUER";
6
+ IdentityConfig["ApiKeySecret"] = "DRAX_APIKEY_SECRET";
7
+ IdentityConfig["ApiKeyCacheTTL"] = "DRAX_APIKEY_CACHE_TTL";
9
8
  IdentityConfig["RbacCacheTTL"] = "DRAX_RBAC_CACHE_TTL";
10
9
  IdentityConfig["AvatarDir"] = "DRAX_AVATAR_DIR";
11
10
  })(IdentityConfig || (IdentityConfig = {}));
@@ -1,20 +1,21 @@
1
+ import { DraxConfig, CommonConfig, COMMON } from "@drax/common-back";
1
2
  import RoleService from "../services/RoleService.js";
2
3
  import RoleMongoRepository from "../repository/mongo/RoleMongoRepository.js";
3
4
  import RoleSqliteRepository from "../repository/sqlite/RoleSqliteRepository.js";
4
- import { DbSetupUtils, DbEngine } from "../utils/DbSetupUtils.js";
5
5
  let roleService;
6
6
  const RoleServiceFactory = (verbose = false) => {
7
7
  if (!roleService) {
8
8
  let roleRepository;
9
- switch (DbSetupUtils.getDbEngine()) {
10
- case DbEngine.Mongo:
11
- console.log("RoleServiceFactory DB ENGINE MONGODB");
9
+ switch (DraxConfig.getOrLoad(CommonConfig.DbEngine)) {
10
+ case COMMON.DB_ENGINES.MONGODB:
12
11
  roleRepository = new RoleMongoRepository();
13
12
  break;
14
- case DbEngine.Sqlite:
15
- console.log("RoleServiceFactory DB ENGINE SQLITE");
16
- roleRepository = new RoleSqliteRepository(DbSetupUtils.getDbConfig(), verbose);
13
+ case COMMON.DB_ENGINES.SQLITE:
14
+ const dbFile = DraxConfig.getOrLoad(CommonConfig.SqliteDbFile);
15
+ roleRepository = new RoleSqliteRepository(dbFile, verbose);
17
16
  break;
17
+ default:
18
+ throw new Error("DraxConfig.DB_ENGINE must be one of " + Object.values(COMMON.DB_ENGINES).join(", "));
18
19
  }
19
20
  roleService = new RoleService(roleRepository);
20
21
  }
@@ -1,20 +1,21 @@
1
+ import { COMMON, CommonConfig, DraxConfig } from "@drax/common-back";
1
2
  import TenantService from "../services/TenantService.js";
2
3
  import TenantMongoRepository from "../repository/mongo/TenantMongoRepository.js";
3
4
  import TenantSqliteRepository from "../repository/sqlite/TenantSqliteRepository.js";
4
- import { DbSetupUtils, DbEngine } from "../utils/DbSetupUtils.js";
5
5
  let tenantService;
6
6
  const TenantServiceFactory = (verbose = false) => {
7
7
  if (!tenantService) {
8
8
  let tenantRepository;
9
- switch (DbSetupUtils.getDbEngine()) {
10
- case DbEngine.Mongo:
11
- console.log("TenantServiceFactory DB ENGINE MONGODB");
9
+ switch (DraxConfig.getOrLoad(CommonConfig.DbEngine)) {
10
+ case COMMON.DB_ENGINES.MONGODB:
12
11
  tenantRepository = new TenantMongoRepository();
13
12
  break;
14
- case DbEngine.Sqlite:
15
- console.log("TenantServiceFactory DB ENGINE SQLITE");
16
- tenantRepository = new TenantSqliteRepository(DbSetupUtils.getDbConfig(), verbose);
13
+ case COMMON.DB_ENGINES.SQLITE:
14
+ const dbFile = DraxConfig.getOrLoad(CommonConfig.SqliteDbFile);
15
+ tenantRepository = new TenantSqliteRepository(dbFile, verbose);
17
16
  break;
17
+ default:
18
+ throw new Error("DraxConfig.DB_ENGINE must be one of " + Object.values(COMMON.DB_ENGINES).join(", "));
18
19
  }
19
20
  tenantService = new TenantService(tenantRepository);
20
21
  }
@@ -0,0 +1,24 @@
1
+ import UserApiKeyMongoRepository from "../repository/mongo/UserApiKeyMongoRepository.js";
2
+ import UserApiKeyService from "../services/UserApiKeyService.js";
3
+ import UserApiKeySqliteRepository from "../repository/sqlite/UserApiKeySqliteRepository.js";
4
+ import { COMMON, CommonConfig, DraxConfig } from "@drax/common-back";
5
+ let userService;
6
+ const UserApiKeyServiceFactory = (verbose = false) => {
7
+ if (!userService) {
8
+ let userRepository;
9
+ switch (DraxConfig.getOrLoad(CommonConfig.DbEngine)) {
10
+ case COMMON.DB_ENGINES.MONGODB:
11
+ userRepository = new UserApiKeyMongoRepository();
12
+ break;
13
+ case COMMON.DB_ENGINES.SQLITE:
14
+ const dbFile = DraxConfig.getOrLoad(CommonConfig.SqliteDbFile);
15
+ userRepository = new UserApiKeySqliteRepository(dbFile, verbose);
16
+ break;
17
+ default:
18
+ throw new Error("DraxConfig.DB_ENGINE must be one of " + Object.values(COMMON.DB_ENGINES).join(", "));
19
+ }
20
+ userService = new UserApiKeyService(userRepository);
21
+ }
22
+ return userService;
23
+ };
24
+ export default UserApiKeyServiceFactory;
@@ -1,20 +1,21 @@
1
1
  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
- import { DbEngine, DbSetupUtils } from "../utils/DbSetupUtils.js";
4
+ import { COMMON, CommonConfig, DraxConfig } from "@drax/common-back";
5
5
  let userService;
6
6
  const UserServiceFactory = (verbose = false) => {
7
7
  if (!userService) {
8
8
  let userRepository;
9
- switch (DbSetupUtils.getDbEngine()) {
10
- case DbEngine.Mongo:
11
- console.log("UserServiceFactory DB ENGINE MONGODB");
9
+ switch (DraxConfig.getOrLoad(CommonConfig.DbEngine)) {
10
+ case COMMON.DB_ENGINES.MONGODB:
12
11
  userRepository = new UserMongoRepository();
13
12
  break;
14
- case DbEngine.Sqlite:
15
- console.log("UserServiceFactory DB ENGINE SQLITE");
16
- userRepository = new UserSqliteRepository(DbSetupUtils.getDbConfig(), verbose);
13
+ case COMMON.DB_ENGINES.SQLITE:
14
+ const dbFile = DraxConfig.getOrLoad(CommonConfig.SqliteDbFile);
15
+ userRepository = new UserSqliteRepository(dbFile, verbose);
17
16
  break;
17
+ default:
18
+ throw new Error("DraxConfig.DB_ENGINE must be one of " + Object.values(COMMON.DB_ENGINES).join(", "));
18
19
  }
19
20
  userService = new UserService(userRepository);
20
21
  }
@@ -0,0 +1,89 @@
1
+ import UserApiKeyServiceFactory from "../../factory/UserApiKeyServiceFactory.js";
2
+ import { IdentityPermissions } from "../../permissions/IdentityPermissions.js";
3
+ import { ValidationError, ValidationErrorToGraphQLError } from "@drax/common-back";
4
+ import { GraphQLError } from "graphql";
5
+ import UnauthorizedError from "../../errors/UnauthorizedError.js";
6
+ import * as crypto from "node:crypto";
7
+ export default {
8
+ Query: {
9
+ paginateUserApiKey: async (_, { options = { page: 1, limit: 5, orderBy: "", orderDesc: false, search: "", filters: [] } }, { rbac, authUser }) => {
10
+ try {
11
+ rbac.assertAuthenticated();
12
+ rbac.assertOrPermissions([
13
+ IdentityPermissions.ViewUserApiKey,
14
+ IdentityPermissions.ViewMyUserApiKey
15
+ ]);
16
+ if (!Array.isArray(options.filters)) {
17
+ options.filters = [];
18
+ }
19
+ if (!rbac.hasPermission(IdentityPermissions.ViewUserApiKey)) {
20
+ options.filters.push({ field: "user", operator: "eq", value: rbac.authUser.id });
21
+ }
22
+ const userApiKeyService = UserApiKeyServiceFactory();
23
+ return await userApiKeyService.paginate(options);
24
+ }
25
+ catch (e) {
26
+ console.error("paginateUserApiKey", e);
27
+ if (e instanceof UnauthorizedError) {
28
+ throw new GraphQLError(e.message);
29
+ }
30
+ throw new GraphQLError('error.server');
31
+ }
32
+ }
33
+ },
34
+ Mutation: {
35
+ createUserApiKey: async (_, { input }, { rbac }) => {
36
+ try {
37
+ rbac.assertPermission(IdentityPermissions.CreateUserApiKey);
38
+ input.user = rbac.authUser.id;
39
+ input.secret = crypto.randomUUID();
40
+ const userApiKeyService = UserApiKeyServiceFactory(true);
41
+ return await userApiKeyService.create(input);
42
+ }
43
+ catch (e) {
44
+ console.error("createUserApiKey", e);
45
+ if (e instanceof ValidationError) {
46
+ throw ValidationErrorToGraphQLError(e);
47
+ }
48
+ if (e instanceof UnauthorizedError) {
49
+ throw new GraphQLError(e.message);
50
+ }
51
+ throw new GraphQLError('error.server');
52
+ }
53
+ },
54
+ updateUserApiKey: async (_, { id, input }, { rbac }) => {
55
+ try {
56
+ rbac.assertPermission(IdentityPermissions.UpdateUserApiKey);
57
+ const userApiKeyService = UserApiKeyServiceFactory();
58
+ return await userApiKeyService.update(id, input);
59
+ }
60
+ catch (e) {
61
+ console.error("updateUserApiKey", e);
62
+ if (e instanceof ValidationError) {
63
+ throw ValidationErrorToGraphQLError(e);
64
+ }
65
+ if (e instanceof UnauthorizedError) {
66
+ throw new GraphQLError(e.message);
67
+ }
68
+ throw new GraphQLError('error.server');
69
+ }
70
+ },
71
+ deleteUserApiKey: async (_, { id }, { rbac }) => {
72
+ try {
73
+ rbac.assertPermission(IdentityPermissions.DeleteUserApiKey);
74
+ const userApiKeyService = UserApiKeyServiceFactory();
75
+ return await userApiKeyService.delete(id);
76
+ }
77
+ catch (e) {
78
+ console.error("deleteUserApiKey", e);
79
+ if (e instanceof ValidationError) {
80
+ throw ValidationErrorToGraphQLError(e);
81
+ }
82
+ if (e instanceof UnauthorizedError) {
83
+ throw new GraphQLError(e.message);
84
+ }
85
+ throw new GraphQLError('error.server');
86
+ }
87
+ }
88
+ }
89
+ };
@@ -13,7 +13,7 @@ export default {
13
13
  if (authUser) {
14
14
  let userService = UserServiceFactory();
15
15
  let user = await userService.findById(authUser.id);
16
- delete user.password;
16
+ user.password = undefined;
17
17
  return user;
18
18
  }
19
19
  throw new UnauthorizedError();
@@ -27,7 +27,9 @@ export default {
27
27
  try {
28
28
  rbac.assertPermission(IdentityPermissions.ViewUser);
29
29
  let userService = UserServiceFactory();
30
- return await userService.findById(id);
30
+ let user = await userService.findById(id);
31
+ user.password = undefined;
32
+ return user;
31
33
  }
32
34
  catch (e) {
33
35
  if (e instanceof UnauthorizedError) {
@@ -40,6 +42,9 @@ export default {
40
42
  try {
41
43
  rbac.assertPermission(IdentityPermissions.ViewUser);
42
44
  let userService = UserServiceFactory();
45
+ if (!options.filters) {
46
+ options.filters = [];
47
+ }
43
48
  if (rbac.getAuthUser.tenantId) {
44
49
  options.filters.push({ field: 'tenant', operator: '$eq', value: rbac.getAuthUser.tenantId });
45
50
  }
@@ -0,0 +1,33 @@
1
+ type UserApiKey {
2
+ id: ID!
3
+ name: String
4
+ secret: String
5
+ ipv4: [String]
6
+ ipv6: [String]
7
+ user: User
8
+ createdAt: Date
9
+ updatedAt: Date
10
+ }
11
+
12
+ type UserApiKeyPaginated{
13
+ total: Int
14
+ page: Int
15
+ limit: Int
16
+ items: [UserApiKey]
17
+ }
18
+
19
+ type Query{
20
+ paginateUserApiKey(options: PaginateOptions): UserApiKeyPaginated
21
+ }
22
+
23
+ input UserApiKeyInput{
24
+ name: String
25
+ ipv4: [String]
26
+ ipv6: [String]
27
+ }
28
+
29
+ type Mutation{
30
+ createUserApiKey(input: UserApiKeyInput): UserApiKey
31
+ updateUserApiKey(id: ID!, input: UserApiKeyInput): UserApiKey
32
+ deleteUserApiKey(id: ID!): Boolean
33
+ }
package/dist/index.js CHANGED
@@ -11,9 +11,11 @@ import { UserRoutes } from "./routes/UserRoutes.js";
11
11
  import { UserAvatarRoutes } from "./routes/UserAvatarRoutes.js";
12
12
  import { RoleRoutes } from "./routes/RoleRoutes.js";
13
13
  import { TenantRoutes } from "./routes/TenantRoutes.js";
14
+ import { UserApiKeyRoutes } from "./routes/UserApiKeyRoutes.js";
14
15
  import AuthUtils from "./utils/AuthUtils.js";
15
16
  import { jwtMiddleware } from "./middleware/jwtMiddleware.js";
16
17
  import { rbacMiddleware } from "./middleware/rbacMiddleware.js";
18
+ import { apiKeyMiddleware } from "./middleware/apiKeyMiddleware.js";
17
19
  import IdentityPermissions from "./permissions/IdentityPermissions.js";
18
20
  import IdentityConfig from "./config/IdentityConfig.js";
19
21
  import UnauthorizedError from "./errors/UnauthorizedError.js";
@@ -34,9 +36,9 @@ UserServiceFactory, RoleServiceFactory, TenantServiceFactory,
34
36
  //GQL
35
37
  identityTypeDefs, identityResolvers,
36
38
  //API REST
37
- UserRoutes, RoleRoutes, TenantRoutes, UserAvatarRoutes, AuthUtils,
39
+ UserRoutes, RoleRoutes, TenantRoutes, UserAvatarRoutes, UserApiKeyRoutes, AuthUtils,
38
40
  //API MIDDLEWARE
39
- jwtMiddleware, rbacMiddleware,
41
+ jwtMiddleware, rbacMiddleware, apiKeyMiddleware,
40
42
  //Permissions
41
43
  IdentityPermissions,
42
44
  //Config
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ import { DraxCache, DraxConfig } from "@drax/common-back";
2
+ import UserApiKeyServiceFactory from "../factory/UserApiKeyServiceFactory.js";
3
+ import IdentityConfig from "../config/IdentityConfig.js";
4
+ const cacheTTL = DraxConfig.getOrLoad(IdentityConfig.ApiKeyCacheTTL) ? parseInt(DraxConfig.getOrLoad(IdentityConfig.ApiKeyCacheTTL)) : 10000;
5
+ const draxCache = new DraxCache(cacheTTL);
6
+ async function userApiKeyLoader(k) {
7
+ const userApiKeyService = UserApiKeyServiceFactory();
8
+ const userApiKey = await userApiKeyService.findBySecret(k);
9
+ return userApiKey;
10
+ }
11
+ async function apiKeyMiddleware(request, reply) {
12
+ try {
13
+ const apiKey = request.headers['x-api-key'];
14
+ if (apiKey) {
15
+ const userApiKey = await draxCache.getOrLoad(apiKey, userApiKeyLoader);
16
+ if (userApiKey && userApiKey.user) {
17
+ request.authUser = userApiKey.user;
18
+ request.authUser.roleId = userApiKey.user.role.id;
19
+ request.authUser.tenantId = userApiKey.user.tenant.id;
20
+ }
21
+ }
22
+ return;
23
+ }
24
+ catch (e) {
25
+ console.error(e);
26
+ reply.code(500).send({ error: 'APIKEY ERROR' });
27
+ }
28
+ }
29
+ export default apiKeyMiddleware;
30
+ export { apiKeyMiddleware };
@@ -11,7 +11,6 @@ async function roleLoader(k) {
11
11
  }
12
12
  async function rbacMiddleware(request, reply) {
13
13
  try {
14
- //console.log("rbacMiddleware authUser",request.authUser)
15
14
  if (request.authUser) {
16
15
  const authUser = request.authUser;
17
16
  const cacheKey = authUser.roleId;
@@ -0,0 +1,44 @@
1
+ import { mongoose, MongooseSoftDelete } from '@drax/common-back';
2
+ import uniqueValidator from 'mongoose-unique-validator';
3
+ import mongoosePaginate from 'mongoose-paginate-v2';
4
+ // Defining user Mongoose Schema
5
+ const UserApiKeySchema = new mongoose.Schema({
6
+ name: {
7
+ type: String,
8
+ unique: false,
9
+ required: true,
10
+ index: false,
11
+ },
12
+ secret: {
13
+ type: String,
14
+ unique: false,
15
+ required: true,
16
+ index: true,
17
+ },
18
+ user: {
19
+ type: mongoose.Schema.Types.ObjectId,
20
+ ref: 'User',
21
+ required: true,
22
+ },
23
+ ipv4: [{
24
+ type: String,
25
+ unique: false,
26
+ required: false,
27
+ index: false,
28
+ }],
29
+ ipv6: [{
30
+ type: String,
31
+ unique: false,
32
+ required: false,
33
+ index: false,
34
+ }],
35
+ }, { timestamps: true });
36
+ UserApiKeySchema.set('toJSON', { getters: true });
37
+ UserApiKeySchema.plugin(uniqueValidator, { message: 'validation.unique' });
38
+ UserApiKeySchema.plugin(MongooseSoftDelete);
39
+ UserApiKeySchema.plugin(mongoosePaginate);
40
+ const MODEL_NAME = 'UserApiKey';
41
+ const COLLECTION_NAME = 'userApiKeys';
42
+ const UserApiKeyModel = mongoose.model(MODEL_NAME, UserApiKeySchema, COLLECTION_NAME);
43
+ export { UserApiKeySchema, UserApiKeyModel };
44
+ export default UserApiKeyModel;
@@ -5,6 +5,12 @@ var IdentityPermissions;
5
5
  IdentityPermissions["DeleteUser"] = "user:delete";
6
6
  IdentityPermissions["ViewUser"] = "user:view";
7
7
  IdentityPermissions["ManageUser"] = "user:manage";
8
+ IdentityPermissions["CreateUserApiKey"] = "userApiKey:create";
9
+ IdentityPermissions["UpdateUserApiKey"] = "userApiKey:update";
10
+ IdentityPermissions["DeleteUserApiKey"] = "userApiKey:delete";
11
+ IdentityPermissions["ViewUserApiKey"] = "userApiKey:view";
12
+ IdentityPermissions["ViewMyUserApiKey"] = "userApiKey:myView";
13
+ IdentityPermissions["ManageUserApiKey"] = "userApiKey:manage";
8
14
  IdentityPermissions["CreateRole"] = "role:create";
9
15
  IdentityPermissions["UpdateRole"] = "role:update";
10
16
  IdentityPermissions["DeleteRole"] = "role:delete";
package/dist/rbac/Rbac.js CHANGED
@@ -21,6 +21,14 @@ class Rbac {
21
21
  throw new UnauthorizedError();
22
22
  }
23
23
  }
24
+ assertOrPermissions(requiredPermissions) {
25
+ for (let requiredPermission of requiredPermissions) {
26
+ if (this.hasPermission(requiredPermission)) {
27
+ return true;
28
+ }
29
+ }
30
+ throw new UnauthorizedError();
31
+ }
24
32
  assertAuthenticated() {
25
33
  if (!this.authUser) {
26
34
  throw new UnauthorizedError();
@@ -0,0 +1,82 @@
1
+ import { UserApiKeyModel } from "../../models/UserApiKeyModel.js";
2
+ import { mongoose, MongooseErrorToValidationError, MongoServerErrorToValidationError } from "@drax/common-back";
3
+ import { MongoServerError } from "mongodb";
4
+ class UserMongoRepository {
5
+ constructor() {
6
+ }
7
+ async create(data) {
8
+ try {
9
+ const userApiKey = new UserApiKeyModel(data);
10
+ await userApiKey.save();
11
+ await userApiKey.populate({ path: 'user', populate: { path: 'tenant role' } });
12
+ return userApiKey;
13
+ }
14
+ catch (e) {
15
+ if (e instanceof mongoose.Error.ValidationError) {
16
+ throw MongooseErrorToValidationError(e);
17
+ }
18
+ throw e;
19
+ }
20
+ }
21
+ async update(id, data) {
22
+ try {
23
+ delete data.secret;
24
+ const userApiKey = await UserApiKeyModel.findOneAndUpdate({ _id: id }, data, { new: true }).populate({ path: 'user', populate: { path: 'tenant role' } }).exec();
25
+ return userApiKey;
26
+ }
27
+ catch (e) {
28
+ if (e instanceof mongoose.Error.ValidationError) {
29
+ throw MongooseErrorToValidationError(e);
30
+ }
31
+ if (e instanceof MongoServerError || e.name === 'MongoServerError') {
32
+ throw MongoServerErrorToValidationError(e);
33
+ }
34
+ throw e;
35
+ }
36
+ }
37
+ async delete(id) {
38
+ const userApiKey = await UserApiKeyModel.findById(id);
39
+ userApiKey.softDelete();
40
+ return true;
41
+ }
42
+ async findById(id) {
43
+ const userApiKey = await UserApiKeyModel.findById(id).populate({ path: 'user', populate: { path: 'tenant role' } }).exec();
44
+ return userApiKey;
45
+ }
46
+ async findBySecret(secret) {
47
+ const userApiKey = await UserApiKeyModel.findOne({ secret: secret }).populate({ path: 'user', populate: { path: 'tenant role' } }).exec();
48
+ return userApiKey;
49
+ }
50
+ async paginate({ page = 1, limit = 5, orderBy = '', orderDesc = false, search = '', filters = [] }) {
51
+ const query = {
52
+ deleted: false
53
+ };
54
+ if (search) {
55
+ query['$or'] = [
56
+ { name: new RegExp(search, 'i') },
57
+ ];
58
+ }
59
+ if (filters) {
60
+ for (const filter of filters) {
61
+ if (['eq', '$eq'].includes(filter.operator)) {
62
+ query[filter.field] = { $eq: filter.value };
63
+ }
64
+ if (['ne', '$ne'].includes(filter.operator)) {
65
+ query[filter.field] = { $ne: filter.value };
66
+ }
67
+ if (['in', '$in'].includes(filter.operator)) {
68
+ query[filter.field] = { $in: filter.value };
69
+ }
70
+ }
71
+ }
72
+ const options = { populate: ['user', 'user.tenant', 'user.role'], page: page, limit: limit };
73
+ const userApiKeyPaginated = await UserApiKeyModel.paginate(query, options);
74
+ return {
75
+ page: page,
76
+ limit: limit,
77
+ total: userApiKeyPaginated.totalDocs,
78
+ items: userApiKeyPaginated.docs
79
+ };
80
+ }
81
+ }
82
+ export default UserMongoRepository;
@@ -61,13 +61,13 @@ class UserMongoRepository {
61
61
  }
62
62
  if (filters) {
63
63
  for (const filter of filters) {
64
- if (filter.operator === '$eq') {
64
+ if (['eq', '$eq'].includes(filter.operator)) {
65
65
  query[filter.field] = { $eq: filter.value };
66
66
  }
67
- if (filter.operator === '$ne') {
67
+ if (['ne', '$ne'].includes(filter.operator)) {
68
68
  query[filter.field] = { $ne: filter.value };
69
69
  }
70
- if (filter.operator === '$in') {
70
+ if (['in', '$in'].includes(filter.operator)) {
71
71
  query[filter.field] = { $in: filter.value };
72
72
  }
73
73
  }
@@ -1,24 +1,23 @@
1
1
  import sqlite from "better-sqlite3";
2
2
  import { randomUUID } from "node:crypto";
3
- import { SqliteErrorToValidationError } from "@drax/common-back";
4
- const roleTableSQL = `
5
- CREATE TABLE IF NOT EXISTS roles
6
- (
7
- id TEXT PRIMARY KEY,
8
- name TEXT,
9
- permissions TEXT,
10
- readonly INTEGER,
11
- childRoles TEXT
12
-
13
- );
14
- `;
3
+ import { SqliteErrorToValidationError, SqliteTableBuilder } from "@drax/common-back";
4
+ const tableFields = [
5
+ { name: "name", type: "TEXT", unique: true, primary: false },
6
+ { name: "permissions", type: "TEXT", unique: false, primary: false },
7
+ { name: "childRoles", type: "TEXT", unique: false, primary: false },
8
+ { name: "readonly", type: "INTEGER", unique: false, primary: false },
9
+ { name: "createdAt", type: "TEXT", unique: false, primary: false },
10
+ { name: "updatedAt", type: "TEXT", unique: false, primary: false },
11
+ ];
15
12
  class RoleSqliteRepository {
16
- constructor(DATABASE, verbose = false) {
17
- this.db = new sqlite(DATABASE, { verbose: verbose ? console.log : null });
13
+ constructor(dataBaseFile, verbose = false) {
14
+ this.dataBaseFile = dataBaseFile;
15
+ this.db = new sqlite(dataBaseFile, { verbose: verbose ? console.log : null });
18
16
  this.table();
19
17
  }
20
18
  table() {
21
- this.db.exec(roleTableSQL);
19
+ const builder = new SqliteTableBuilder(this.dataBaseFile, 'roles', tableFields, false);
20
+ builder.build('id');
22
21
  }
23
22
  normalizeData(roleData) {
24
23
  roleData.readonly = roleData.readonly ? 1 : 0;
@@ -35,15 +34,13 @@ class RoleSqliteRepository {
35
34
  roleData.id = randomUUID();
36
35
  }
37
36
  this.normalizeData(roleData);
37
+ roleData.createdAt = (new Date().toISOString());
38
38
  const fields = Object.keys(roleData)
39
39
  .map(field => `${field}`)
40
40
  .join(', ');
41
41
  const values = Object.keys(roleData)
42
42
  .map(field => `@${field}`)
43
43
  .join(', ');
44
- /* console.log("fields", fields)
45
- console.log("values",values)
46
- console.log("userData",roleData)*/
47
44
  const stmt = this.db.prepare(`INSERT INTO roles (${fields}) VALUES (${values})`);
48
45
  stmt.run(roleData);
49
46
  return this.findById(roleData.id);
@@ -56,6 +53,7 @@ class RoleSqliteRepository {
56
53
  async update(id, roleData) {
57
54
  try {
58
55
  this.normalizeData(roleData);
56
+ roleData.updatedAt = (new Date().toISOString());
59
57
  const setClauses = Object.keys(roleData)
60
58
  .map(field => `${field} = @${field}`)
61
59
  .join(', ');
@@ -1,26 +1,27 @@
1
1
  import sqlite from "better-sqlite3";
2
2
  import { randomUUID } from "node:crypto";
3
- import { SqliteErrorToValidationError } from "@drax/common-back";
4
- const tenantTableSQL = `
5
- CREATE TABLE IF NOT EXISTS tenants
6
- (
7
- id TEXT PRIMARY KEY,
8
- name TEXT
9
- );
10
- `;
3
+ import { SqliteErrorToValidationError, SqliteTableBuilder } from "@drax/common-back";
4
+ const tableFields = [
5
+ { name: "name", type: "TEXT", unique: false, primary: false },
6
+ { name: "createdAt", type: "TEXT", unique: false, primary: false },
7
+ { name: "updatedAt", type: "TEXT", unique: false, primary: false },
8
+ ];
11
9
  class TenantSqliteRepository {
12
- constructor(DATABASE, verbose = false) {
13
- this.db = new sqlite(DATABASE, { verbose: verbose ? console.log : null });
10
+ constructor(dataBaseFile, verbose = false) {
11
+ this.dataBaseFile = dataBaseFile;
12
+ this.db = new sqlite(this.dataBaseFile, { verbose: verbose ? console.log : null });
14
13
  this.table();
15
14
  }
16
15
  table() {
17
- this.db.exec(tenantTableSQL);
16
+ const builder = new SqliteTableBuilder(this.dataBaseFile, 'tenants', tableFields, false);
17
+ builder.build('id');
18
18
  }
19
19
  async create(tenantData) {
20
20
  try {
21
21
  if (!tenantData.id) {
22
22
  tenantData.id = randomUUID();
23
23
  }
24
+ tenantData.createdAt = (new Date().toISOString());
24
25
  const fields = Object.keys(tenantData)
25
26
  .map(field => `${field}`)
26
27
  .join(', ');
@@ -46,6 +47,7 @@ class TenantSqliteRepository {
46
47
  }
47
48
  async update(id, tenantData) {
48
49
  try {
50
+ tenantData.updatedAt = (new Date().toISOString());
49
51
  const setClauses = Object.keys(tenantData)
50
52
  .map(field => `${field} = @${field}`)
51
53
  .join(', ');