@mshima/generator-jhipster-nodejs 0.0.0-stage → 0.0.1

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 (209) hide show
  1. package/LICENSE.txt +204 -0
  2. package/README.md +243 -2
  3. package/build-info.json +3 -0
  4. package/cli/cli-customizations.cjs +9 -0
  5. package/cli/cli.cjs +40 -0
  6. package/cli/logo.js +31 -0
  7. package/cli/version.cjs +12 -0
  8. package/generators/ci-cd/USAGE +5 -0
  9. package/generators/ci-cd/command.js +5 -0
  10. package/generators/ci-cd/generator.js +158 -0
  11. package/generators/ci-cd/index.js +2 -0
  12. package/generators/ci-cd/templates/.gitlab-ci.yml.ejs +137 -0
  13. package/generators/ci-cd/templates/azure-pipelines.yml.ejs +68 -0
  14. package/generators/ci-cd/templates/circle.yml.ejs +59 -0
  15. package/generators/ci-cd/templates/docker-registry.yml.ejs +58 -0
  16. package/generators/ci-cd/templates/github-ci.yml.ejs +90 -0
  17. package/generators/ci-cd/templates/jenkins/Jenkinsfile.ejs +104 -0
  18. package/generators/ci-cd/templates/jenkins/idea.gdsl +90 -0
  19. package/generators/ci-cd/templates/jenkins/jenkins.yml.ejs +33 -0
  20. package/generators/ci-cd/templates/travis.yml.ejs +75 -0
  21. package/generators/client/command.js +5 -0
  22. package/generators/client/generator.js +21 -0
  23. package/generators/client/generators/bootstrap/command.js +5 -0
  24. package/generators/client/generators/bootstrap/generator.js +28 -0
  25. package/generators/client/generators/bootstrap/index.js +2 -0
  26. package/generators/client/index.js +2 -0
  27. package/generators/common/generators/bootstrap/command.js +5 -0
  28. package/generators/common/generators/bootstrap/generator.js +37 -0
  29. package/generators/common/generators/bootstrap/index.js +2 -0
  30. package/generators/docker/command.js +5 -0
  31. package/generators/docker/generator.js +16 -0
  32. package/generators/docker/generators/bootstrap/command.js +5 -0
  33. package/generators/docker/generators/bootstrap/generator.js +56 -0
  34. package/generators/docker/generators/bootstrap/index.js +2 -0
  35. package/generators/docker/index.js +2 -0
  36. package/generators/docker/templates/docker/app.yml.ejs +25 -0
  37. package/generators/generator-nodejs-constants.js +32 -0
  38. package/generators/languages/command.js +5 -0
  39. package/generators/languages/generator.js +22 -0
  40. package/generators/languages/index.js +2 -0
  41. package/generators/languages/templates/src/main/webapp/i18n/en/home.json +19 -0
  42. package/generators/languages/templates/src/main/webapp/i18n/es/home.json +19 -0
  43. package/generators/languages/templates/src/main/webapp/i18n/it/home.json +19 -0
  44. package/generators/node-server/command.js +96 -0
  45. package/generators/node-server/entity-files.js +41 -0
  46. package/generators/node-server/files.js +254 -0
  47. package/generators/node-server/generator.js +702 -0
  48. package/generators/node-server/generators/bootstrap/command.js +5 -0
  49. package/generators/node-server/generators/bootstrap/generator.js +83 -0
  50. package/generators/node-server/generators/bootstrap/index.js +2 -0
  51. package/generators/node-server/index.js +2 -0
  52. package/generators/node-server/resources/package.json +76 -0
  53. package/generators/node-server/support/api-fake-data.js +54 -0
  54. package/generators/node-server/support/columns.js +73 -0
  55. package/generators/node-server/support/database.js +24 -0
  56. package/generators/node-server/support/entity-authorities.js +32 -0
  57. package/generators/node-server/support/entity-paths.js +13 -0
  58. package/generators/node-server/support/enums.js +11 -0
  59. package/generators/node-server/support/index.js +9 -0
  60. package/generators/node-server/support/mail-templates.js +82 -0
  61. package/generators/node-server/support/migrations.js +197 -0
  62. package/generators/node-server/support/validators.js +25 -0
  63. package/generators/node-server/templates/.dockerignore.ejs +16 -0
  64. package/generators/node-server/templates/.gitignore.jhi.nodejs.ejs +24 -0
  65. package/generators/node-server/templates/Dockerfile.ejs +54 -0
  66. package/generators/node-server/templates/README.md.jhi.nodejs.ejs +220 -0
  67. package/generators/node-server/templates/package.json.ejs +84 -0
  68. package/generators/node-server/templates/server/README.md.ejs +269 -0
  69. package/generators/node-server/templates/server/e2e/_entityFileName_.database.e2e-spec.ts.ejs +192 -0
  70. package/generators/node-server/templates/server/e2e/_entityFileName_.e2e-spec.ts.ejs +402 -0
  71. package/generators/node-server/templates/server/e2e/account.e2e-spec.ts.ejs +349 -0
  72. package/generators/node-server/templates/server/e2e/app.e2e-spec.ts.ejs +188 -0
  73. package/generators/node-server/templates/server/e2e/authority.e2e-spec.ts.ejs +50 -0
  74. package/generators/node-server/templates/server/e2e/cors.e2e-spec.ts.ejs +110 -0
  75. package/generators/node-server/templates/server/e2e/management.e2e-spec.ts.ejs +180 -0
  76. package/generators/node-server/templates/server/e2e/oauth2-bearer.e2e-spec.ts.ejs +71 -0
  77. package/generators/node-server/templates/server/e2e/session.e2e-spec.ts.ejs +219 -0
  78. package/generators/node-server/templates/server/e2e/setup.test.js.ejs +1 -0
  79. package/generators/node-server/templates/server/e2e/user.e2e-spec.ts.ejs +318 -0
  80. package/generators/node-server/templates/server/env.ejs +1 -0
  81. package/generators/node-server/templates/server/eslint.config.mjs.ejs +48 -0
  82. package/generators/node-server/templates/server/nest-cli.json.ejs +11 -0
  83. package/generators/node-server/templates/server/ormconfig.ts.ejs +35 -0
  84. package/generators/node-server/templates/server/package.json.ejs +143 -0
  85. package/generators/node-server/templates/server/sonar-project.properties.ejs +10 -0
  86. package/generators/node-server/templates/server/src/app.config.spec.ts.ejs +13 -0
  87. package/generators/node-server/templates/server/src/app.config.ts.ejs +179 -0
  88. package/generators/node-server/templates/server/src/app.module.ts.ejs +80 -0
  89. package/generators/node-server/templates/server/src/application-config.ts.ejs +153 -0
  90. package/generators/node-server/templates/server/src/auth/account.controller.ts.ejs +183 -0
  91. package/generators/node-server/templates/server/src/auth/auth.module.ts.ejs +104 -0
  92. package/generators/node-server/templates/server/src/auth/auth.service.ts.ejs +294 -0
  93. package/generators/node-server/templates/server/src/auth/csrf.middleware.spec.ts.ejs +65 -0
  94. package/generators/node-server/templates/server/src/auth/csrf.middleware.ts.ejs +76 -0
  95. package/generators/node-server/templates/server/src/auth/decorators/auth-user.decorator.ts.ejs +8 -0
  96. package/generators/node-server/templates/server/src/auth/decorators/roles.decorator.ts.ejs +4 -0
  97. package/generators/node-server/templates/server/src/auth/dto/key-and-password.dto.ts.ejs +17 -0
  98. package/generators/node-server/templates/server/src/auth/dto/password-change.dto.ts.ejs +17 -0
  99. package/generators/node-server/templates/server/src/auth/dto/user-login.dto.ts.ejs +21 -0
  100. package/generators/node-server/templates/server/src/auth/guards/auth.guard.ts.ejs +50 -0
  101. package/generators/node-server/templates/server/src/auth/guards/oauth2-login.guard.ts.ejs +6 -0
  102. package/generators/node-server/templates/server/src/auth/guards/roles.guard.ts.ejs +25 -0
  103. package/generators/node-server/templates/server/src/auth/index.ts.ejs +8 -0
  104. package/generators/node-server/templates/server/src/auth/jwt.config.spec.ts.ejs +22 -0
  105. package/generators/node-server/templates/server/src/auth/jwt.config.ts.ejs +29 -0
  106. package/generators/node-server/templates/server/src/auth/oauth2.authorities.spec.ts.ejs +24 -0
  107. package/generators/node-server/templates/server/src/auth/oauth2.authorities.ts.ejs +33 -0
  108. package/generators/node-server/templates/server/src/auth/oauth2.config.ts.ejs +51 -0
  109. package/generators/node-server/templates/server/src/auth/passport.jwt.strategy.ts.ejs +45 -0
  110. package/generators/node-server/templates/server/src/auth/passport.oauth2-bearer.strategy.ts.ejs +32 -0
  111. package/generators/node-server/templates/server/src/auth/passport.oauth2.strategy.spec.ts.ejs +27 -0
  112. package/generators/node-server/templates/server/src/auth/passport.oauth2.strategy.ts.ejs +44 -0
  113. package/generators/node-server/templates/server/src/auth/password-util.ts.ejs +10 -0
  114. package/generators/node-server/templates/server/src/auth/password.service.ts.ejs +34 -0
  115. package/generators/node-server/templates/server/src/auth/payload.interface.ts.ejs +14 -0
  116. package/generators/node-server/templates/server/src/auth/role-type.ts.ejs +7 -0
  117. package/generators/node-server/templates/server/src/auth/session-user.middleware.ts.ejs +33 -0
  118. package/generators/node-server/templates/server/src/auth/session.entity.ts.ejs +36 -0
  119. package/generators/node-server/templates/server/src/auth/session.store.spec.ts.ejs +99 -0
  120. package/generators/node-server/templates/server/src/auth/session.store.ts.ejs +116 -0
  121. package/generators/node-server/templates/server/src/auth/user.jwt.controller.ts.ejs +26 -0
  122. package/generators/node-server/templates/server/src/auth/user.oauth2.controller.ts.ejs +78 -0
  123. package/generators/node-server/templates/server/src/auth/user.session.controller.ts.ejs +103 -0
  124. package/generators/node-server/templates/server/src/common/common.module.ts.ejs +19 -0
  125. package/generators/node-server/templates/server/src/common/database/base.entity.ts.ejs +13 -0
  126. package/generators/node-server/templates/server/src/common/database/column-transformers.spec.ts.ejs +46 -0
  127. package/generators/node-server/templates/server/src/common/database/column-transformers.ts.ejs +69 -0
  128. package/generators/node-server/templates/server/src/common/database/column-types.spec.ts.ejs +21 -0
  129. package/generators/node-server/templates/server/src/common/database/column-types.ts.ejs +29 -0
  130. package/generators/node-server/templates/server/src/common/database/pagination.entity.spec.ts.ejs +66 -0
  131. package/generators/node-server/templates/server/src/common/database/pagination.entity.ts.ejs +108 -0
  132. package/generators/node-server/templates/server/src/common/dto/base.dto.ts.ejs +19 -0
  133. package/generators/node-server/templates/server/src/common/errors/error-constants.ts.ejs +10 -0
  134. package/generators/node-server/templates/server/src/common/errors/problem-details.filter.ts.ejs +63 -0
  135. package/generators/node-server/templates/server/src/common/errors/problem.exception.ts.ejs +92 -0
  136. package/generators/node-server/templates/server/src/common/field_validators.ejs +77 -0
  137. package/generators/node-server/templates/server/src/common/header-util.ts.ejs +105 -0
  138. package/generators/node-server/templates/server/src/common/interceptors/logging.interceptor.spec.ts.ejs +93 -0
  139. package/generators/node-server/templates/server/src/common/interceptors/logging.interceptor.ts.ejs +48 -0
  140. package/generators/node-server/templates/server/src/common/logging.spec.ts.ejs +83 -0
  141. package/generators/node-server/templates/server/src/common/logging.ts.ejs +129 -0
  142. package/generators/node-server/templates/server/src/common/metrics.spec.ts.ejs +43 -0
  143. package/generators/node-server/templates/server/src/common/metrics.ts.ejs +198 -0
  144. package/generators/node-server/templates/server/src/common/request.ts.ejs +8 -0
  145. package/generators/node-server/templates/server/src/common/sortable-properties.ts.ejs +43 -0
  146. package/generators/node-server/templates/server/src/common/updatable-properties.ts.ejs +14 -0
  147. package/generators/node-server/templates/server/src/config/application-dev.yml.ejs +118 -0
  148. package/generators/node-server/templates/server/src/config/application-prod.yml.ejs +97 -0
  149. package/generators/node-server/templates/server/src/config/application-secret-samples.yml.ejs +42 -0
  150. package/generators/node-server/templates/server/src/config/application-test.yml.ejs +68 -0
  151. package/generators/node-server/templates/server/src/config/application-tls-samples.yml.ejs +15 -0
  152. package/generators/node-server/templates/server/src/config/application-tls.yml.ejs +15 -0
  153. package/generators/node-server/templates/server/src/config/application.yml.ejs +149 -0
  154. package/generators/node-server/templates/server/src/config.spec.ts.ejs +81 -0
  155. package/generators/node-server/templates/server/src/config.ts.ejs +255 -0
  156. package/generators/node-server/templates/server/src/entities/_entityFileName_/_entityFileName_.controller.ts.ejs +178 -0
  157. package/generators/node-server/templates/server/src/entities/_entityFileName_/_entityFileName_.entity.ts.ejs +110 -0
  158. package/generators/node-server/templates/server/src/entities/_entityFileName_/_entityFileName_.mapper.ts.ejs +37 -0
  159. package/generators/node-server/templates/server/src/entities/_entityFileName_/_entityFileName_.module.ts.ejs +14 -0
  160. package/generators/node-server/templates/server/src/entities/_entityFileName_/_entityFileName_.service.ts.ejs +123 -0
  161. package/generators/node-server/templates/server/src/entities/_entityFileName_/dto/_entityFileName_.dto.ts.ejs +99 -0
  162. package/generators/node-server/templates/server/src/mail/mail.module.ts.ejs +49 -0
  163. package/generators/node-server/templates/server/src/mail/mail.service.spec.ts.ejs +105 -0
  164. package/generators/node-server/templates/server/src/mail/mail.service.ts.ejs +120 -0
  165. package/generators/node-server/templates/server/src/main.ts.ejs +114 -0
  166. package/generators/node-server/templates/server/src/management/api-docs.controller.ts.ejs +27 -0
  167. package/generators/node-server/templates/server/src/management/api-docs.service.ts.ejs +8 -0
  168. package/generators/node-server/templates/server/src/management/configuration.controller.ts.ejs +80 -0
  169. package/generators/node-server/templates/server/src/management/health.controller.spec.ts.ejs +79 -0
  170. package/generators/node-server/templates/server/src/management/health.controller.ts.ejs +87 -0
  171. package/generators/node-server/templates/server/src/management/health.module.ts.ejs +9 -0
  172. package/generators/node-server/templates/server/src/management/loggers.controller.ts.ejs +40 -0
  173. package/generators/node-server/templates/server/src/management/management.controller.spec.ts.ejs +26 -0
  174. package/generators/node-server/templates/server/src/management/management.controller.ts.ejs +34 -0
  175. package/generators/node-server/templates/server/src/management/management.module.ts.ejs +21 -0
  176. package/generators/node-server/templates/server/src/management/metrics.controller.ts.ejs +85 -0
  177. package/generators/node-server/templates/server/src/migrations/1570200270081-CreateTables.ts.ejs +64 -0
  178. package/generators/node-server/templates/server/src/migrations/1570200270082-CreateSessions.ts.ejs +26 -0
  179. package/generators/node-server/templates/server/src/migrations/1570200490072-SeedUsersRoles.ts.ejs +74 -0
  180. package/generators/node-server/templates/server/src/migrations/_entityConstraintsMigration_.ts.ejs +40 -0
  181. package/generators/node-server/templates/server/src/migrations/_entityMigration_.ts.ejs +26 -0
  182. package/generators/node-server/templates/server/src/migrations/support/schema.ts.ejs +202 -0
  183. package/generators/node-server/templates/server/src/orm.config.ts.ejs +147 -0
  184. package/generators/node-server/templates/server/src/registry/consul.service.ts.ejs +70 -0
  185. package/generators/node-server/templates/server/src/registry/eureka.service.ts.ejs +129 -0
  186. package/generators/node-server/templates/server/src/swagger.ts.ejs +24 -0
  187. package/generators/node-server/templates/server/src/tls.ts.ejs +27 -0
  188. package/generators/node-server/templates/server/src/users/authority.controller.ts.ejs +85 -0
  189. package/generators/node-server/templates/server/src/users/authority.entity.ts.ejs +22 -0
  190. package/generators/node-server/templates/server/src/users/dto/authority.dto.ts.ejs +10 -0
  191. package/generators/node-server/templates/server/src/users/dto/user.dto.ts.ejs +116 -0
  192. package/generators/node-server/templates/server/src/users/public.user.controller.ts.ejs +51 -0
  193. package/generators/node-server/templates/server/src/users/user.controller.ts.ejs +191 -0
  194. package/generators/node-server/templates/server/src/users/user.entity.ts.ejs +63 -0
  195. package/generators/node-server/templates/server/src/users/user.mapper.ts.ejs +26 -0
  196. package/generators/node-server/templates/server/src/users/user.module.ts.ejs +19 -0
  197. package/generators/node-server/templates/server/src/users/user.service.ts.ejs +156 -0
  198. package/generators/node-server/templates/server/tsconfig.build.json.ejs +9 -0
  199. package/generators/node-server/templates/server/tsconfig.json.ejs +23 -0
  200. package/generators/node-server/templates/server/typeorm-migration.ts.ejs +18 -0
  201. package/generators/node-server/templates/server/vitest.config.e2e.ts.ejs +16 -0
  202. package/generators/node-server/templates/server/vitest.config.ts.ejs +16 -0
  203. package/generators/server/command.js +7 -0
  204. package/generators/server/generator.js +20 -0
  205. package/generators/server/generators/bootstrap/command.js +5 -0
  206. package/generators/server/generators/bootstrap/generator.js +15 -0
  207. package/generators/server/generators/bootstrap/index.js +2 -0
  208. package/generators/server/index.js +2 -0
  209. package/package.json +75 -4
@@ -0,0 +1,110 @@
1
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
2
+ import { Test, TestingModule } from '@nestjs/testing';
3
+ import request from 'supertest';
4
+ import { INestApplication } from '@nestjs/common';
5
+ import { AppModule } from '../src/app.module.ts';
6
+ import { configureApp } from '../src/app.config.ts';
7
+ import { config } from '../src/config.ts';
8
+
9
+ describe('CORS', () => {
10
+ let app: INestApplication;
11
+
12
+ const createApp = async (properties: Record<string, any>): Promise<void> => {
13
+ config.addAll(properties);
14
+ const moduleFixture: TestingModule = await Test.createTestingModule({
15
+ imports: [AppModule],
16
+ }).compile();
17
+
18
+ app = moduleFixture.createNestApplication();
19
+ configureApp(app);
20
+ await app.init();
21
+ };
22
+
23
+ afterEach(async () => {
24
+ config.addAll({ 'jhipster.cors.allowed-origins': '', 'jhipster.cors.allowed-origin-patterns': '', 'jhipster.cors.allow-credentials': false });
25
+ await app?.close();
26
+ });
27
+
28
+ describe('without allowed origins, like the test and prod profiles', () => {
29
+ beforeEach(() => createApp({}));
30
+
31
+ it('does not allow other origins', async () => {
32
+ const response = await request(app.getHttpServer()).get('/management/health').set('Origin', 'http://localhost:9000').expect(200);
33
+ if (response.headers['access-control-allow-origin'] !== undefined) {
34
+ throw new Error(`Unexpected Access-Control-Allow-Origin: ${response.headers['access-control-allow-origin']}`);
35
+ }
36
+ });
37
+ });
38
+
39
+ describe('with the properties of the dev profile', () => {
40
+ beforeEach(() =>
41
+ createApp({
42
+ 'jhipster.cors.allowed-origins': 'http://localhost:9000',
43
+ 'jhipster.cors.allowed-origin-patterns': 'https://*.githubpreview.dev',
44
+ 'jhipster.cors.allowed-methods': '*',
45
+ 'jhipster.cors.allowed-headers': '*',
46
+ 'jhipster.cors.exposed-headers': 'Authorization,Link,X-Total-Count',
47
+ 'jhipster.cors.allow-credentials': true,
48
+ 'jhipster.cors.max-age': 1800,
49
+ }),
50
+ );
51
+
52
+ it('allows an allowed origin on /management', () =>
53
+ request(app.getHttpServer())
54
+ .get('/management/health')
55
+ .set('Origin', 'http://localhost:9000')
56
+ .expect(200)
57
+ .expect('Access-Control-Allow-Origin', 'http://localhost:9000')
58
+ .expect('Access-Control-Allow-Credentials', 'true')
59
+ .expect('Access-Control-Expose-Headers', 'Authorization,Link,X-Total-Count'));
60
+
61
+ it('allows an origin of a pattern on a preflight request of /api', () =>
62
+ request(app.getHttpServer())
63
+ .options('/api/account')
64
+ .set('Origin', 'https://my-codespace.githubpreview.dev')
65
+ .set('Access-Control-Request-Method', 'POST')
66
+ .set('Access-Control-Request-Headers', 'authorization,content-type')
67
+ .expect(204)
68
+ .expect('Access-Control-Allow-Origin', 'https://my-codespace.githubpreview.dev')
69
+ .expect('Access-Control-Allow-Headers', 'authorization,content-type')
70
+ .expect('Access-Control-Max-Age', '1800'));
71
+
72
+ it('does not allow another origin', async () => {
73
+ const response = await request(app.getHttpServer()).get('/management/health').set('Origin', 'http://other.example.com').expect(200);
74
+ if (response.headers['access-control-allow-origin'] !== undefined) {
75
+ throw new Error(`Unexpected Access-Control-Allow-Origin: ${response.headers['access-control-allow-origin']}`);
76
+ }
77
+ });
78
+ });
79
+
80
+ describe('with * as allowed origins, without credentials', () => {
81
+ beforeEach(() => createApp({ 'jhipster.cors.allowed-origins': '*', 'jhipster.cors.allow-credentials': false }));
82
+
83
+ it('allows any origin, like the CorsConfiguration of Spring', async () => {
84
+ const response = await request(app.getHttpServer())
85
+ .get('/management/health')
86
+ .set('Origin', 'http://any.example.com')
87
+ .expect(200)
88
+ .expect('Access-Control-Allow-Origin', '*');
89
+ expect(response.headers['access-control-allow-credentials']).toBeUndefined();
90
+ });
91
+ });
92
+
93
+ it('refuses * as allowed origins with credentials, which browsers refuse, like the CorsConfiguration of Spring', async () => {
94
+ await expect(createApp({ 'jhipster.cors.allowed-origins': '*', 'jhipster.cors.allow-credentials': true })).rejects.toThrow(
95
+ /allowed-origin-patterns/,
96
+ );
97
+ });
98
+
99
+ describe('with * as allowed origin patterns and credentials', () => {
100
+ beforeEach(() => createApp({ 'jhipster.cors.allowed-origin-patterns': '*', 'jhipster.cors.allow-credentials': true }));
101
+
102
+ it('allows any origin with credentials, the origin given back', () =>
103
+ request(app.getHttpServer())
104
+ .get('/management/health')
105
+ .set('Origin', 'http://any.example.com')
106
+ .expect(200)
107
+ .expect('Access-Control-Allow-Origin', 'http://any.example.com')
108
+ .expect('Access-Control-Allow-Credentials', 'true'));
109
+ });
110
+ });
@@ -0,0 +1,180 @@
1
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
2
+ import { Test, TestingModule } from '@nestjs/testing';
3
+ import request from 'supertest';
4
+ import { ExecutionContext, INestApplication } from '@nestjs/common';
5
+ import { AppModule } from '../src/app.module.ts';
6
+ import { configureApp } from '../src/app.config.ts';
7
+ import { loggerLevels } from '../src/common/logging.ts';
8
+ import { setupSwagger } from '../src/swagger.ts';
9
+ import { AuthGuard } from '../src/auth/guards/auth.guard.ts';
10
+ import { RolesGuard } from '../src/auth/guards/roles.guard.ts';
11
+
12
+ describe('Management', () => {
13
+ let app: INestApplication;
14
+
15
+ /** The application with its guards, without them (an administrator), or with an authenticated user of some roles. */
16
+ const createApp = async (guards: boolean | string[]): Promise<INestApplication> => {
17
+ let builder = Test.createTestingModule({ imports: [AppModule] });
18
+ if (guards === false) {
19
+ // An administrator.
20
+ builder = builder.overrideGuard(AuthGuard).useValue({ canActivate: () => true }).overrideGuard(RolesGuard).useValue({ canActivate: () => true });
21
+ } else if (Array.isArray(guards)) {
22
+ // A user of these roles, checked by the RolesGuard of the application.
23
+ builder = builder.overrideGuard(AuthGuard).useValue({
24
+ canActivate: (context: ExecutionContext) => {
25
+ context.switchToHttp().getRequest().user = { login: 'managementtest', authorities: guards };
26
+ return true;
27
+ },
28
+ });
29
+ }
30
+ const moduleFixture: TestingModule = await builder.compile();
31
+ const nestApp = moduleFixture.createNestApplication();
32
+ configureApp(nestApp);
33
+ await nestApp.init();
34
+ return nestApp;
35
+ };
36
+
37
+ afterEach(async () => {
38
+ loggerLevels.setLevel('ManagementTest', null);
39
+ await app?.close();
40
+ });
41
+
42
+ describe('as an administrator', () => {
43
+ beforeEach(async () => {
44
+ app = await createApp(false);
45
+ });
46
+
47
+ it('/GET loggers gives the levels and the ROOT logger', async () => {
48
+ const { body } = await request(app.getHttpServer()).get('/management/loggers').expect(200);
49
+
50
+ expect(body.levels).toEqual(['OFF', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE']);
51
+ expect(Object.keys(body.loggers)[0]).toBe('ROOT');
52
+ expect(body.loggers.ROOT.configuredLevel).toEqual(expect.any(String));
53
+ });
54
+
55
+ it('/POST loggers changes the level of a logger, and null gives it the ROOT level again', async () => {
56
+ await request(app.getHttpServer()).post('/management/loggers/ManagementTest').send({ configuredLevel: 'TRACE' }).expect(204);
57
+ await request(app.getHttpServer())
58
+ .get('/management/loggers/ManagementTest')
59
+ .expect(200)
60
+ .expect({ configuredLevel: 'TRACE', effectiveLevel: 'TRACE' });
61
+
62
+ await request(app.getHttpServer()).post('/management/loggers/ManagementTest').send({ configuredLevel: null }).expect(204);
63
+ const { body } = await request(app.getHttpServer()).get('/management/loggers/ManagementTest').expect(200);
64
+ expect(body).toEqual({ configuredLevel: null, effectiveLevel: loggerLevels.getLevel('ROOT').effectiveLevel });
65
+ });
66
+
67
+ it('/POST loggers with an unknown level is a bad request', () =>
68
+ request(app.getHttpServer()).post('/management/loggers/ManagementTest').send({ configuredLevel: 'LOUD' }).expect(400));
69
+
70
+ it('/GET jhimetrics gives the memory, the processor and the requests by endpoint', async () => {
71
+ await request(app.getHttpServer()).get('/management/info').expect(200);
72
+ const { body } = await request(app.getHttpServer()).get('/management/jhimetrics').expect(200);
73
+
74
+ expect(body.jvm.heap).toEqual({ used: expect.any(Number), committed: expect.any(Number), max: expect.any(Number) });
75
+ expect(body.processMetrics['system.cpu.count']).toBeGreaterThan(0);
76
+ expect(body['http.server.requests'].percode['200'].count).toBeGreaterThan(0);
77
+ expect(body.services['/management/info'].GET.count).toBe(1);
78
+ });
79
+
80
+ it('/GET env gives the sources of the configuration, their values hidden like Spring Boot', async () => {
81
+ const { body } = await request(app.getHttpServer()).get('/management/env').expect(200);
82
+
83
+ expect(body.activeProfiles).toContain('test');
84
+ expect(body.propertySources.map((source: { name: string }) => source.name)).toEqual([
85
+ 'systemEnvironment',
86
+ "Config resource 'config/application-secret-samples.yml'",
87
+ "Config resource 'config/application-test.yml'",
88
+ "Config resource 'config/application.yml'",
89
+ ]);
90
+ const applicationYml = body.propertySources[3];
91
+ expect(applicationYml.properties['management.endpoint.env.show-values']).toEqual({ value: '******' });
92
+ });
93
+
94
+ it('/GET configprops gives the properties of the application by prefix, their values hidden like Spring Boot', async () => {
95
+ const { body } = await request(app.getHttpServer()).get('/management/configprops').expect(200);
96
+
97
+ const [context] = Object.values<{ beans: Record<string, { prefix: string; properties: any }> }>(body.contexts);
98
+ expect(context.beans.jhipster.prefix).toBe('jhipster');
99
+ expect(context.beans.jhipster.properties.mail.from).toBe('******');
100
+ });
101
+
102
+ it('/GET threaddump gives the thread of the event loop', async () => {
103
+ const { body } = await request(app.getHttpServer()).get('/management/threaddump').expect(200);
104
+
105
+ expect(body.threads).toEqual([expect.objectContaining({ threadName: 'main', threadState: 'RUNNABLE' })]);
106
+ });
107
+ });
108
+
109
+ describe('as a user without the administrator role', () => {
110
+ beforeEach(async () => {
111
+ app = await createApp(['ROLE_USER']);
112
+ });
113
+
114
+ it('/GET and /POST loggers are forbidden, the role of the controller class applies to its routes', async () => {
115
+ await request(app.getHttpServer()).get('/management/loggers').expect(403);
116
+ await request(app.getHttpServer()).post('/management/loggers/ManagementTest').send({ configuredLevel: 'OFF' }).expect(403);
117
+ expect(loggerLevels.getLevel('ManagementTest').configuredLevel).toBeNull();
118
+ });
119
+
120
+ it('/GET jhimetrics is forbidden', () => request(app.getHttpServer()).get('/management/jhimetrics').expect(403));
121
+
122
+ it('/GET v3/api-docs is forbidden, like JHipster Spring Boot', async () => {
123
+ setupSwagger(app);
124
+ await request(app.getHttpServer()).get('/v3/api-docs').expect(403);
125
+ });
126
+ });
127
+
128
+ describe('as a user with the administrator role', () => {
129
+ beforeEach(async () => {
130
+ app = await createApp(['ROLE_ADMIN']);
131
+ });
132
+
133
+ it('/GET loggers is allowed', () => request(app.getHttpServer()).get('/management/loggers').expect(200));
134
+
135
+ it('/GET v3/api-docs is not found without the api-docs profile', () => request(app.getHttpServer()).get('/v3/api-docs').expect(404));
136
+
137
+ it('/GET v3/api-docs gives the OpenAPI document of the api-docs profile', async () => {
138
+ setupSwagger(app);
139
+ const { body } = await request(app.getHttpServer()).get('/v3/api-docs').expect(200);
140
+
141
+ expect(body.openapi).toEqual(expect.any(String));
142
+ expect(body.info.title).toBe('<%= baseName %> API');
143
+ expect(Object.keys(body.paths)).toContainEqual(expect.stringMatching(/^\/api\//));
144
+ // The controller of the document itself is left out.
145
+ expect(Object.keys(body.paths)).not.toContain('/v3/api-docs');
146
+ });
147
+ });
148
+
149
+ describe('Prometheus', () => {
150
+ beforeEach(async () => {
151
+ app = await createApp(true);
152
+ });
153
+
154
+ it('/GET prometheus gives the metrics without authentication', async () => {
155
+ await request(app.getHttpServer()).get('/management/info').expect(200);
156
+ const response = await request(app.getHttpServer()).get('/management/prometheus').expect(200).expect('Content-Type', /text\/plain/);
157
+
158
+ expect(response.text).toContain('http_server_requests_seconds_count{method="GET",uri="/management/info",status="200",outcome="SUCCESS"} 1');
159
+ expect(response.text).toContain('nodejs_heap_size_used_bytes');
160
+ });
161
+ });
162
+
163
+ <%_ if (authenticationTypeJwt || authenticationTypeSession) { _%>
164
+ describe('without authentication', () => {
165
+ beforeEach(async () => {
166
+ app = await createApp(true);
167
+ });
168
+
169
+ it('/GET loggers is refused', async () => {
170
+ const { status } = await request(app.getHttpServer()).get('/management/loggers');
171
+ expect([401, 403]).toContain(status);
172
+ });
173
+
174
+ it('/GET v3/api-docs is refused', async () => {
175
+ setupSwagger(app);
176
+ await request(app.getHttpServer()).get('/v3/api-docs').expect(401);
177
+ });
178
+ });
179
+ <%_ } _%>
180
+ });
@@ -0,0 +1,71 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
2
+ import { createServer, Server } from 'node:http';
3
+ import { AddressInfo } from 'node:net';
4
+ import { generateKeyPairSync, KeyObject, sign } from 'node:crypto';
5
+ import { INestApplication } from '@nestjs/common';
6
+ import { Test } from '@nestjs/testing';
7
+ import request from 'supertest';
8
+
9
+ // The identity provider: a JWKS server with a key, and the tokens it signs, like the access tokens of Keycloak.
10
+ const { publicKey, privateKey } = generateKeyPairSync('rsa', { modulusLength: 2048 });
11
+ const otherKey = generateKeyPairSync('rsa', { modulusLength: 2048 }).privateKey;
12
+ const kid = 'test-key';
13
+ const base64url = (value: object | Buffer): string => (Buffer.isBuffer(value) ? value : Buffer.from(JSON.stringify(value))).toString('base64url');
14
+
15
+ describe('OAuth2 bearer tokens', () => {
16
+ let jwks: Server;
17
+ let app: INestApplication;
18
+ let issuer: string;
19
+
20
+ const token = (claims: object, key: KeyObject = privateKey): string => {
21
+ const payload = { iss: issuer, aud: 'account', exp: Math.floor(Date.now() / 1000) + 300, sub: 'abc', ...claims };
22
+ const unsigned = `${base64url({ alg: 'RS256', typ: 'JWT', kid })}.${base64url(payload)}`;
23
+ return `${unsigned}.${base64url(sign('RSA-SHA256', Buffer.from(unsigned), key))}`;
24
+ };
25
+ const account = (authorization?: string): request.Test => {
26
+ const test = request(app.getHttpServer()).get('/api/account');
27
+ return authorization ? test.set('Authorization', authorization) : test;
28
+ };
29
+
30
+ beforeAll(async () => {
31
+ jwks = createServer((_req, res) => {
32
+ res.setHeader('content-type', 'application/json');
33
+ res.end(JSON.stringify({ keys: [{ ...publicKey.export({ format: 'jwk' }), kid, use: 'sig', alg: 'RS256' }] }));
34
+ });
35
+ await new Promise<void>(resolve => jwks.listen(0, '127.0.0.1', resolve));
36
+ issuer = `http://127.0.0.1:${(jwks.address() as AddressInfo).port}/realms/test`;
37
+ // The issuer of the configuration, before the application reads it.
38
+ process.env.JHIPSTER_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUERURI = issuer;
39
+ const { AppModule } = await import('../src/app.module.ts');
40
+ const { configureApp } = await import('../src/app.config.ts');
41
+ const moduleFixture = await Test.createTestingModule({ imports: [AppModule] }).compile();
42
+ app = moduleFixture.createNestApplication();
43
+ configureApp(app);
44
+ await app.init();
45
+ });
46
+
47
+ afterAll(async () => {
48
+ await app?.close();
49
+ jwks?.close();
50
+ delete process.env.JHIPSTER_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUERURI;
51
+ });
52
+
53
+ it('answers 401 without a session nor a bearer token, like JHipster Spring Boot', () => account().expect(401));
54
+
55
+ it('accepts an access token of the identity provider, its user from its claims', async () => {
56
+ const { body } = await account(`Bearer ${token({ preferred_username: 'Bearer-User', email: 'Bearer@Example.com', groups: ['ROLE_USER'] })}`).expect(200);
57
+ expect(body).toMatchObject({ login: 'bearer-user', email: 'bearer@example.com', authorities: ['ROLE_USER'] });
58
+ });
59
+
60
+ it('reads the authentication scheme case insensitively', () => account(`bearer ${token({ preferred_username: 'user' })}`).expect(200));
61
+
62
+ it('refuses a token signed by another key', () => account(`Bearer ${token({}, otherKey)}`).expect(401));
63
+
64
+ it('refuses an expired token', () => account(`Bearer ${token({ exp: 1 })}`).expect(401));
65
+
66
+ it('refuses a token of another issuer, like the issuer validator of JHipster Spring Boot', () =>
67
+ account(`Bearer ${token({ iss: 'http://127.0.0.1:1/realms/other' })}`).expect(401));
68
+
69
+ it('refuses a token for another audience, like the audience validator of JHipster Spring Boot', () =>
70
+ account(`Bearer ${token({ aud: 'another-api' })}`).expect(401));
71
+ });
@@ -0,0 +1,219 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { Test, TestingModule } from '@nestjs/testing';
3
+ import { INestApplication } from '@nestjs/common';
4
+ import request from 'supertest';
5
+ import { AppModule } from '../src/app.module.ts';
6
+ import { configureApp } from '../src/app.config.ts';
7
+ import { <%= user.dtoClass %> } from '../src/users/dto/user.dto.ts';
8
+ import { UserService } from '../src/users/user.service.ts';
9
+ import { PasswordService } from '../src/auth/password.service.ts';
10
+ import { getRepositoryToken } from '@nestjs/typeorm';
11
+ import { Repository } from 'typeorm';
12
+ import { Session } from '../src/auth/session.entity.ts';
13
+
14
+ /** A cookie of the Set-Cookie headers of a response, with its attributes. */
15
+ const setCookie = (response: request.Response, name: string): string | undefined =>
16
+ ([] as string[]).concat(response.headers['set-cookie'] ?? []).find(value => value.startsWith(`${name}=`));
17
+
18
+ /** The value of a cookie of the Set-Cookie headers of a response. */
19
+ const cookieValue = (response: request.Response, name: string): string | undefined => {
20
+ const cookie = setCookie(response, name);
21
+ return cookie ? decodeURIComponent(cookie.split(';')[0].slice(name.length + 1)) : undefined;
22
+ };
23
+
24
+ /** The expiry of a cookie of the Set-Cookie headers of a response. */
25
+ const cookieExpiry = (response: request.Response, name: string): number =>
26
+ Date.parse(/Expires=([^;]+)/.exec(setCookie(response, name) ?? '')?.[1] ?? '');
27
+
28
+ describe('Session authentication', () => {
29
+ let app: INestApplication;
30
+ let agent: ReturnType<typeof request.agent>;
31
+ let service: UserService;
32
+ let users: <%= user.dtoClass %>[];
33
+
34
+ const password = 'sessionPassword';
35
+
36
+ beforeEach(async () => {
37
+ const moduleFixture: TestingModule = await Test.createTestingModule({
38
+ imports: [AppModule],
39
+ }).compile();
40
+
41
+ app = moduleFixture.createNestApplication();
42
+ configureApp(app);
43
+ await app.init();
44
+ service = moduleFixture.get<UserService>(UserService);
45
+ users = [
46
+ await service.save({ login: 'session-user', email: 'session-user@localhost', password, activated: true }, 'session-user', true),
47
+ await service.save({ login: 'session-inactive', email: 'session-inactive@localhost', password, activated: false }, 'session-inactive', true),
48
+ ];
49
+ // An agent keeps the cookies of the responses, like a browser: the session and the XSRF-TOKEN cookie.
50
+ agent = request.agent(app.getHttpServer());
51
+ csrfToken = undefined;
52
+ });
53
+
54
+ afterEach(async () => {
55
+ for (const user of users ?? []) {
56
+ await service.delete(user);
57
+ }
58
+ await app?.close();
59
+ });
60
+
61
+ /** The XSRF-TOKEN cookie a client without a session gets, the token of its login, kept by the agent. */
62
+ let csrfToken: string | undefined;
63
+ const anonymousToken = async (): Promise<string> => {
64
+ csrfToken = cookieValue(await agent.get('/api/account').expect(401), 'XSRF-TOKEN') ?? csrfToken;
65
+ return csrfToken!;
66
+ };
67
+
68
+ /** The login of the form the JHipster clients post, with the token of the XSRF-TOKEN cookie. */
69
+ const login = async (form: Record<string, string>): Promise<request.Response> => {
70
+ const token = await anonymousToken();
71
+ return agent.post('/api/authentication').set('X-XSRF-TOKEN', token).type('form').send(form);
72
+ };
73
+
74
+ it('answers 401 without a session, like JHipster Spring Boot, giving the client a CSRF token cookie', async () => {
75
+ const response = await agent.get('/api/account').expect(401);
76
+ expect(cookieValue(response, 'XSRF-TOKEN')).toEqual(expect.any(String));
77
+ });
78
+
79
+ it('refuses a login without the CSRF token of the cookie of the client, like Spring Security, whatever its path', async () => {
80
+ await anonymousToken();
81
+ // Express routes the paths whatever their case and a trailing slash.
82
+ for (const path of ['/api/authentication', '/API/Authentication', '/api/authentication/']) {
83
+ await agent.post(path).type('form').send({ username: 'session-user', password }).expect(403);
84
+ }
85
+ await agent.get('/api/account').expect(401);
86
+ });
87
+
88
+ it('refuses a form of other values than texts with 401', async () => {
89
+ const token = await anonymousToken();
90
+ await agent.post('/api/authentication').set('X-XSRF-TOKEN', token).send({ username: ['session-user'], password: { value: password } }).expect(401);
91
+ });
92
+
93
+ it('checks the password of an unknown login too, answering in the time of a known one', async () => {
94
+ const matches = vi.spyOn(app.get(PasswordService), 'matches');
95
+ await login({ username: 'unknown', password });
96
+ expect(matches).toHaveBeenCalledWith(password, expect.any(String));
97
+ });
98
+
99
+ it('logs out a user deactivated or removed at its next request, like JHipster Spring Boot reads the user again', async () => {
100
+ await login({ username: 'session-user', password, 'remember-me': 'true' });
101
+ await agent.get('/api/account').expect(200);
102
+ const user = (await service.findByFields({ where: { login: 'session-user' } }))!;
103
+ await service.save({ ...user, activated: false }, 'admin');
104
+ await agent.get('/api/account').expect(401);
105
+ });
106
+
107
+ it('applies the authorities given to the user of a session at once', async () => {
108
+ await login({ username: 'session-user', password });
109
+ await agent.get('/api/admin/users').expect(403);
110
+ const user = (await service.findByFields({ where: { login: 'session-user' } }))!;
111
+ await service.save({ ...user, authorities: ['ROLE_USER', 'ROLE_ADMIN'] }, 'admin');
112
+ await agent.get('/api/admin/users').expect(200);
113
+ });
114
+
115
+ it('refuses a wrong password, an unknown login, a missing password and a user not activated with 401', async () => {
116
+ await login({ username: 'session-user', password: 'wrong' }).then(response => expect(response.status).toBe(401));
117
+ await login({ username: 'unknown', password }).then(response => expect(response.status).toBe(401));
118
+ await login({ username: 'session-user' }).then(response => expect(response.status).toBe(401));
119
+ await login({ username: 'session-inactive', password }).then(response => expect(response.status).toBe(401));
120
+ await agent.get('/api/account').expect(401);
121
+ });
122
+
123
+ it('logs in the user of the login, in lower case, in a session of 30 minutes without requests', async () => {
124
+ const response = await login({ username: 'Session-User', password, 'remember-me': 'false' });
125
+ expect(response.status).toBe(200);
126
+ expect(cookieExpiry(response, 'JSESSIONID')).toBeLessThanOrEqual(Date.now() + 30 * 60 * 1000);
127
+ const { body } = await agent.get('/api/account').expect(200);
128
+ expect(body).toMatchObject({ login: 'session-user' });
129
+ expect(body.password).toBeUndefined();
130
+ });
131
+
132
+ it('keeps the session in the database, which outlives a restart of the application, and removes it at the logout', async () => {
133
+ const sessions = app.get<Repository<Session>>(getRepositoryToken(Session));
134
+ const token = cookieValue(await login({ username: 'session-user', password }), 'XSRF-TOKEN')!;
135
+ const stored = await sessions.find({ where: { login: 'session-user' } });
136
+ expect(stored).toHaveLength(1);
137
+ expect(stored[0].expires.getTime()).toBeGreaterThan(Date.now());
138
+ expect(JSON.parse(stored[0].data)).toMatchObject({ user: { login: 'session-user' } });
139
+ expect(stored[0].data).not.toContain(password);
140
+
141
+ await agent.post('/api/logout').set('X-XSRF-TOKEN', token).expect(200);
142
+ expect(await sessions.find({ where: { login: 'session-user' } })).toHaveLength(0);
143
+ });
144
+
145
+ it('names the session cookie like JHipster Spring Boot, not secure behind an untrusted proxy (forward-headers-strategy none)', async () => {
146
+ const token = await anonymousToken();
147
+ const response = await agent
148
+ .post('/api/authentication')
149
+ .set('X-XSRF-TOKEN', token)
150
+ .set('X-Forwarded-Proto', 'https')
151
+ .type('form')
152
+ .send({ username: 'session-user', password });
153
+ expect(setCookie(response, 'JSESSIONID')).toMatch(/HttpOnly; SameSite=Lax$/);
154
+ expect(setCookie(response, 'JSESSIONID')).not.toContain('Secure');
155
+ });
156
+
157
+ it('keeps a remember-me login 31 days without requests, like the remember-me of JHipster Spring Boot', async () => {
158
+ const response = await login({ username: 'session-user', password, 'remember-me': 'true' });
159
+ expect(response.status).toBe(200);
160
+ expect(cookieExpiry(response, 'JSESSIONID')).toBeGreaterThan(Date.now() + 30 * 24 * 60 * 60 * 1000);
161
+ expect(cookieExpiry(response, 'XSRF-TOKEN')).toBeGreaterThan(Date.now() + 30 * 24 * 60 * 60 * 1000);
162
+ });
163
+
164
+ it('refuses the unsafe requests of a session without its CSRF token, the logout included', async () => {
165
+ const token = cookieValue(await login({ username: 'session-user', password }), 'XSRF-TOKEN')!;
166
+ await agent.post('/api/logout').expect(403);
167
+ await agent.post('/api/logout').set('X-XSRF-TOKEN', 'other').expect(403);
168
+ await agent.get('/api/account').expect(200);
169
+ await agent.post('/api/logout').set('X-XSRF-TOKEN', token).expect(200);
170
+ });
171
+
172
+ it('logs out, ending the session', async () => {
173
+ const token = cookieValue(await login({ username: 'session-user', password }), 'XSRF-TOKEN')!;
174
+ await agent.post('/api/logout').set('X-XSRF-TOKEN', token).expect(200);
175
+ await agent.get('/api/account').expect(401);
176
+ });
177
+
178
+ it('lists the sessions of the user and ends one of them, like the sessions page of JHipster Spring Boot', async () => {
179
+ await request(app.getHttpServer()).get('/api/account/sessions').expect(401);
180
+ const token = cookieValue(await login({ username: 'session-user', password }), 'XSRF-TOKEN')!;
181
+ // Another session of the same user, in another browser.
182
+ const other = request.agent(app.getHttpServer());
183
+ const otherToken = cookieValue(await other.get('/api/account').expect(401), 'XSRF-TOKEN')!;
184
+ await other.post('/api/authentication').set('X-XSRF-TOKEN', otherToken).set('User-Agent', 'other-browser').type('form').send({ username: 'session-user', password }).expect(200);
185
+
186
+ const { body: sessions } = await agent.get('/api/account/sessions').expect(200);
187
+ expect(sessions).toHaveLength(2);
188
+ expect(sessions).toEqual(
189
+ expect.arrayContaining([
190
+ { series: expect.any(String), tokenDate: new Date().toISOString().slice(0, 10), ipAddress: expect.any(String), userAgent: 'other-browser' },
191
+ ]),
192
+ );
193
+ const otherSeries = sessions.find((session: any) => session.userAgent === 'other-browser').series;
194
+
195
+ await agent.delete(`/api/account/sessions/${otherSeries}`).expect(403);
196
+ await agent.delete(`/api/account/sessions/${otherSeries}`).set('X-XSRF-TOKEN', token).expect(200);
197
+ await other.get('/api/account').expect(401);
198
+ await agent.get('/api/account').expect(200);
199
+ expect((await agent.get('/api/account/sessions').expect(200)).body).toHaveLength(1);
200
+ });
201
+
202
+ it('neither lists nor ends the sessions of another user', async () => {
203
+ await login({ username: 'session-user', password });
204
+ const admin = request.agent(app.getHttpServer());
205
+ users.push(await service.save({ login: 'session-other', email: 'session-other@localhost', password, activated: true }, 'session-other', true));
206
+ const adminToken = cookieValue(await admin.get('/api/account').expect(401), 'XSRF-TOKEN')!;
207
+ await admin.post('/api/authentication').set('X-XSRF-TOKEN', adminToken).type('form').send({ username: 'session-other', password }).expect(200);
208
+ const { body: otherSessions } = await admin.get('/api/account/sessions').expect(200);
209
+ const { body: sessions } = await agent.get('/api/account/sessions').expect(200);
210
+ expect(sessions).toHaveLength(1);
211
+ expect(otherSessions).toHaveLength(1);
212
+ expect(otherSessions[0].series).not.toEqual(sessions[0].series);
213
+ });
214
+
215
+ it('refuses the administration to a user without ROLE_ADMIN', async () => {
216
+ await login({ username: 'session-user', password });
217
+ await agent.get('/api/admin/users').expect(403);
218
+ });
219
+ });
@@ -0,0 +1 @@
1
+ process.env.BACKEND_ENV='test'