@tstdl/base 0.93.80 → 0.93.82

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 (28) hide show
  1. package/api/server/gateway.js +1 -1
  2. package/authentication/authentication.api.d.ts +9 -0
  3. package/authentication/authentication.api.js +3 -0
  4. package/authentication/client/authentication.service.d.ts +23 -15
  5. package/authentication/client/authentication.service.js +30 -21
  6. package/authentication/index.d.ts +1 -0
  7. package/authentication/index.js +1 -0
  8. package/authentication/models/authentication-credentials.model.d.ts +2 -2
  9. package/authentication/models/authentication-credentials.model.js +5 -5
  10. package/authentication/models/authentication-session.model.d.ts +2 -2
  11. package/authentication/models/authentication-session.model.js +3 -3
  12. package/authentication/models/subject.model.js +5 -3
  13. package/authentication/models/token-payload-base.model.d.ts +5 -1
  14. package/authentication/models/token-payload-base.model.js +10 -2
  15. package/authentication/models/token.model.d.ts +9 -1
  16. package/authentication/server/authentication-ancillary.service.d.ts +12 -15
  17. package/authentication/server/authentication-ancillary.service.js +3 -0
  18. package/authentication/server/authentication.api-controller.js +5 -5
  19. package/authentication/server/authentication.audit.d.ts +7 -5
  20. package/authentication/server/authentication.service.d.ts +32 -22
  21. package/authentication/server/authentication.service.js +116 -65
  22. package/authentication/server/subject.service.d.ts +19 -3
  23. package/authentication/server/subject.service.js +126 -9
  24. package/authentication/types.d.ts +6 -0
  25. package/authentication/types.js +1 -0
  26. package/examples/api/authentication.js +3 -2
  27. package/examples/api/custom-authentication.js +11 -9
  28. package/package.json +1 -1
@@ -4,25 +4,36 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
+ import { BadRequestError } from '../../errors/bad-request.error.js';
8
+ import { formatPersonName } from '../../formats/formats.js';
7
9
  import { Singleton } from '../../injector/index.js';
8
10
  import { injectRepository } from '../../orm/server/index.js';
9
- import { isUndefined } from '../../utils/type-guards.js';
10
- import { Subject, SubjectType, SystemAccount } from '../models/index.js';
11
+ import { mailPattern } from '../../utils/patterns.js';
12
+ import { assertNotNull, isDefined, isUndefined } from '../../utils/type-guards.js';
13
+ import { ServiceAccount, Subject, SubjectType, SystemAccount, User, UserStatus } from '../models/index.js';
11
14
  let SubjectService = class SubjectService {
12
- subjectRepository = injectRepository(Subject);
13
- systemAccountRepository = injectRepository(SystemAccount);
15
+ #subjectRepository = injectRepository(Subject);
16
+ #systemAccountRepository = injectRepository(SystemAccount);
17
+ #userRepository = injectRepository(User);
18
+ #serviceAccountRepository = injectRepository(ServiceAccount);
19
+ async getSubject(id) {
20
+ return await this.#subjectRepository.load(id);
21
+ }
22
+ async tryGetSubject(id) {
23
+ return await this.#subjectRepository.tryLoad(id);
24
+ }
14
25
  async getSystemAccountSubject(tenantId, identifier) {
15
- return await this.subjectRepository.transaction(async (tx) => {
16
- let systemAccount = await this.systemAccountRepository.withTransaction(tx).tryLoadByQuery({
26
+ return await this.#subjectRepository.transaction(async (tx) => {
27
+ let systemAccount = await this.#systemAccountRepository.withTransaction(tx).tryLoadByQuery({
17
28
  tenantId,
18
29
  identifier,
19
30
  });
20
31
  if (isUndefined(systemAccount)) {
21
- systemAccount = await this.systemAccountRepository.withTransaction(tx).insert({
32
+ systemAccount = await this.#systemAccountRepository.withTransaction(tx).insert({
22
33
  tenantId,
23
34
  identifier,
24
35
  });
25
- return await this.subjectRepository.withTransaction(tx).insert({
36
+ return await this.#subjectRepository.withTransaction(tx).insert({
26
37
  type: SubjectType.System,
27
38
  tenantId,
28
39
  systemAccountId: systemAccount.id,
@@ -31,12 +42,118 @@ let SubjectService = class SubjectService {
31
42
  serviceAccountId: null,
32
43
  });
33
44
  }
34
- return await this.subjectRepository.withTransaction(tx).loadByQuery({
45
+ return await this.#subjectRepository.withTransaction(tx).loadByQuery({
35
46
  tenantId,
36
47
  systemAccountId: systemAccount.id,
37
48
  });
38
49
  });
39
50
  }
51
+ async createUser(data) {
52
+ const { tenantId, email, firstName, lastName, status } = data;
53
+ return await this.#userRepository.transaction(async (tx) => {
54
+ const user = await this.#userRepository.withTransaction(tx).insert({
55
+ tenantId,
56
+ email,
57
+ firstName,
58
+ lastName,
59
+ status: status ?? UserStatus.Active,
60
+ });
61
+ await this.#subjectRepository.withTransaction(tx).insert({
62
+ tenantId,
63
+ type: SubjectType.User,
64
+ displayName: `${firstName} ${lastName}`,
65
+ systemAccountId: null,
66
+ userId: user.id,
67
+ serviceAccountId: null,
68
+ });
69
+ return user;
70
+ });
71
+ }
72
+ async updateUser(tenantId, userId, data) {
73
+ const { firstName, lastName, status } = data;
74
+ await this.#userRepository.transaction(async (tx) => {
75
+ const updateData = {};
76
+ if (isDefined(firstName)) {
77
+ updateData.firstName = firstName;
78
+ }
79
+ if (isDefined(lastName)) {
80
+ updateData.lastName = lastName;
81
+ }
82
+ if (isDefined(status)) {
83
+ updateData.status = status;
84
+ }
85
+ if (Object.keys(updateData).length > 0) {
86
+ const updatedUser = await this.#userRepository.withTransaction(tx).updateByQuery({ tenantId, userId }, updateData);
87
+ if (isDefined(firstName) || isDefined(lastName)) {
88
+ await this.#subjectRepository.withTransaction(tx).updateByQuery({ tenantId, userId }, { displayName: formatPersonName(updatedUser) });
89
+ }
90
+ }
91
+ });
92
+ }
93
+ async updateUserEmail(tenantId, userId, email) {
94
+ // TODO: future (out of scope right now): validate mail flow
95
+ if (!mailPattern.test(email)) {
96
+ throw new BadRequestError(`Invalid email format.`);
97
+ }
98
+ await this.#userRepository.updateByQuery({ tenantId, userId }, { email });
99
+ }
100
+ async getUserSubject(tenantId, userId) {
101
+ return await this.#subjectRepository.loadByQuery({ tenantId, userId });
102
+ }
103
+ async getUserByEmail(tenantId, email) {
104
+ return await this.#userRepository.loadByQuery({ tenantId, email });
105
+ }
106
+ async tryGetUserByEmail(tenantId, email) {
107
+ return await this.#userRepository.tryLoadByQuery({ tenantId, email });
108
+ }
109
+ async hasUserByEmail(tenantId, email) {
110
+ const user = await this.tryGetUserByEmail(tenantId, email);
111
+ return isDefined(user);
112
+ }
113
+ async getUserBySubject(subject) {
114
+ assertNotNull(subject.userId, 'Subject is not a user subject');
115
+ return await this.#userRepository.load(subject.userId);
116
+ }
117
+ async loadManyUsersByEmails(tenantId, emails) {
118
+ return await this.#userRepository.loadManyByQuery({ tenantId, email: { $in: emails } });
119
+ }
120
+ async createServiceAccount(data) {
121
+ const { tenantId, description, parent } = data;
122
+ return await this.#serviceAccountRepository.transaction(async (tx) => {
123
+ const serviceAccount = await this.#serviceAccountRepository.withTransaction(tx).insert({
124
+ tenantId,
125
+ description,
126
+ parent,
127
+ });
128
+ await this.#subjectRepository.withTransaction(tx).insert({
129
+ tenantId,
130
+ type: SubjectType.ServiceAccount,
131
+ displayName: description,
132
+ userId: null,
133
+ systemAccountId: null,
134
+ serviceAccountId: serviceAccount.id,
135
+ });
136
+ return serviceAccount;
137
+ });
138
+ }
139
+ async updateServiceAccount(tenantId, serviceAccountId, data) {
140
+ const { displayName, description } = data;
141
+ await this.#subjectRepository.transaction(async (tx) => {
142
+ if (isDefined(displayName)) {
143
+ await this.#subjectRepository.withTransaction(tx).updateByQuery({ tenantId, serviceAccountId }, { displayName });
144
+ }
145
+ if (isDefined(description)) {
146
+ await this.#serviceAccountRepository.withTransaction(tx).updateByQuery({ tenantId, serviceAccountId }, { description });
147
+ }
148
+ });
149
+ }
150
+ async getServiceAccountSubject(tenantId, serviceAccountId) {
151
+ return await this.#subjectRepository.loadByQuery({ tenantId, serviceAccountId });
152
+ }
153
+ async getServiceAccountBySubject(subject) {
154
+ assertNotNull(subject.serviceAccountId, 'Subject is not a service account subject');
155
+ return await this.#serviceAccountRepository.load(subject.serviceAccountId);
156
+ }
40
157
  };
41
158
  SubjectService = __decorate([
42
159
  Singleton()
@@ -0,0 +1,6 @@
1
+ export type SubjectInput = {
2
+ /** The tenant id if available. */
3
+ tenantId?: string;
4
+ /** The subject provided by the user (like an email address). */
5
+ subject: string;
6
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -17,7 +17,8 @@ import { timeout } from '../../utils/timing.js';
17
17
  import { Agent } from 'undici';
18
18
  async function serverTest() {
19
19
  const authenticationService = await injectAsync(AuthenticationServerService);
20
- await authenticationService.setCredentials('foobar', 'mysuperdupersecret-fvhc54w');
20
+ const subject = await authenticationService.resolveSubject({ subject: 'foobar' });
21
+ await authenticationService.setCredentials(subject, 'mysuperdupersecret-fvhc54w');
21
22
  }
22
23
  async function clientTest() {
23
24
  const authenticationService = inject(AuthenticationClientService);
@@ -25,7 +26,7 @@ async function clientTest() {
25
26
  await timeout(250); // allow server to initialize
26
27
  const passwordCheckResult = await authenticationService.checkSecret('123456');
27
28
  console.log({ 'password check for "123456"': passwordCheckResult });
28
- await authenticationService.login('foobar', 'mysuperdupersecret-fvhc54w');
29
+ await authenticationService.login({ subject: 'foobar' }, 'mysuperdupersecret-fvhc54w');
29
30
  authenticationService.token$.subscribe((token) => console.log({ token }));
30
31
  }
31
32
  async function test() {
@@ -18,7 +18,7 @@ import { AuthenticationAncillaryService, AuthenticationApiController, Authentica
18
18
  import { configureUndiciHttpClientAdapter } from '../../http/client/adapters/undici.adapter.js';
19
19
  import { configureHttpClient } from '../../http/client/module.js';
20
20
  import { configureNodeHttpServer } from '../../http/server/node/module.js';
21
- import { Singleton } from '../../injector/decorators.js';
21
+ import { Singleton } from '../../injector/index.js';
22
22
  import { inject, injectAsync } from '../../injector/inject.js';
23
23
  import { configureLocalMessageBus } from '../../message-bus/local/module.js';
24
24
  import { WebServerModule } from '../../module/modules/index.js';
@@ -65,17 +65,18 @@ __decorate([
65
65
  ], AuthenticationData.prototype, "deviceId", void 0);
66
66
  const CustomAuthenticationApiClient = getAuthenticationApiClient(CustomTokenPaylod, AuthenticationData, emptyObjectSchema);
67
67
  let CustomAuthenticationAncillaryService = class CustomAuthenticationAncillaryService extends AuthenticationAncillaryService {
68
- getTokenPayload(_subject, authenticationData) {
68
+ getTokenPayload(_subject, authenticationData, _context) {
69
69
  return { deviceRegistrationId: `registration:${authenticationData.deviceId}` };
70
70
  }
71
- resolveSubject() {
72
- throw new Error('Method not implemented.');
71
+ async resolveSubjects(data) {
72
+ const subjects = await this.subjectRepository.loadManyByQuery({ id: data.subject });
73
+ return subjects;
73
74
  }
74
- handleInitSecretReset() {
75
+ canImpersonate(_token, _subject, _authenticationData) {
75
76
  throw new Error('Method not implemented.');
76
77
  }
77
- canImpersonate() {
78
- throw new Error('Method not implemented.');
78
+ handleInitSecretReset() {
79
+ // send mail
79
80
  }
80
81
  };
81
82
  CustomAuthenticationAncillaryService = __decorate([
@@ -83,13 +84,14 @@ CustomAuthenticationAncillaryService = __decorate([
83
84
  ], CustomAuthenticationAncillaryService);
84
85
  async function serverTest() {
85
86
  const authenticationService = await injectAsync(AuthenticationServerService);
86
- await authenticationService.setCredentials('foobar', 'supersecret-dupidupudoo9275');
87
+ const subject = await authenticationService.resolveSubject({ subject: 'foobar' });
88
+ await authenticationService.setCredentials(subject, 'supersecret-dupidupudoo9275');
87
89
  }
88
90
  async function clientTest(application) {
89
91
  const authenticationService = inject(AuthenticationClientService);
90
92
  await timeout(1500); // allow server to initialize
91
93
  authenticationService.initialize();
92
- await authenticationService.login('foobar', 'supersecret-dupidupudoo9275');
94
+ await authenticationService.login({ subject: 'foobar' }, 'supersecret-dupidupudoo9275');
93
95
  authenticationService.token$.subscribe((token) => console.log({ token }));
94
96
  application.requestShutdown();
95
97
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstdl/base",
3
- "version": "0.93.80",
3
+ "version": "0.93.82",
4
4
  "author": "Patrick Hein",
5
5
  "publishConfig": {
6
6
  "access": "public"