@social-mail/social-mail-web-server 1.9.31 → 1.9.33

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 (25) hide show
  1. package/dist/server/model/entities/User.d.ts +1 -0
  2. package/dist/server/model/entities/User.d.ts.map +1 -1
  3. package/dist/server/model/entities/User.js +4 -0
  4. package/dist/server/model/entities/User.js.map +1 -1
  5. package/dist/server/model/events/LoginSessionEvents.d.ts +1 -0
  6. package/dist/server/model/events/LoginSessionEvents.d.ts.map +1 -1
  7. package/dist/server/model/events/LoginSessionEvents.js +11 -0
  8. package/dist/server/model/events/LoginSessionEvents.js.map +1 -1
  9. package/dist/server/services/files/SnapshotService.d.ts.map +1 -1
  10. package/dist/server/services/files/SnapshotService.js +0 -1
  11. package/dist/server/services/files/SnapshotService.js.map +1 -1
  12. package/dist/server/workflows/SocialWorkflowContext.d.ts.map +1 -1
  13. package/dist/server/workflows/SocialWorkflowContext.js +2 -0
  14. package/dist/server/workflows/SocialWorkflowContext.js.map +1 -1
  15. package/dist/server/workflows/user/login/FailedLoginLockWorkflow.d.ts +13 -0
  16. package/dist/server/workflows/user/login/FailedLoginLockWorkflow.d.ts.map +1 -0
  17. package/dist/server/workflows/user/login/FailedLoginLockWorkflow.js +54 -0
  18. package/dist/server/workflows/user/login/FailedLoginLockWorkflow.js.map +1 -0
  19. package/dist/tsconfig.tsbuildinfo +1 -1
  20. package/package.json +1 -1
  21. package/src/server/model/entities/User.ts +3 -0
  22. package/src/server/model/events/LoginSessionEvents.ts +13 -0
  23. package/src/server/services/files/SnapshotService.ts +0 -1
  24. package/src/server/workflows/SocialWorkflowContext.ts +5 -0
  25. package/src/server/workflows/user/login/FailedLoginLockWorkflow.ts +53 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@social-mail/social-mail-web-server",
3
- "version": "1.9.31",
3
+ "version": "1.9.33",
4
4
  "description": "## Phase 1",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -135,6 +135,9 @@ export class User {
135
135
  @Column({ dataType: "Boolean", default: () => false })
136
136
  public isGroup: boolean;
137
137
 
138
+ @Column({ dataType: "DateTime", nullable: true})
139
+ public loginLockedTill: DateTime;
140
+
138
141
  @JsonProperty({ help: "Save this to change password"})
139
142
  public changePassword: ChangePassword;
140
143
 
@@ -18,6 +18,8 @@ import { AppStringHelper } from "../../../common/AppStringHelper.js";
18
18
  import { randomUUID } from "node:crypto";
19
19
  import RateLimiterService from "../../services/rate-limiter/RateLimiterService.js";
20
20
  import ExternalInvoke from "@entity-access/server-pages/dist/decorators/ExternalInvoke.js";
21
+ import WorkflowContext from "@entity-access/entity-access/dist/workflows/WorkflowContext.js";
22
+ import FailedLoginLockWorkflow from "../../workflows/user/login/FailedLoginLockWorkflow.js";
21
23
 
22
24
  export default class LoginSessionEvents extends EntityEvents<LoginSession> {
23
25
 
@@ -36,6 +38,9 @@ export default class LoginSessionEvents extends EntityEvents<LoginSession> {
36
38
  @Inject
37
39
  private rateLimiterService: RateLimiterService;
38
40
 
41
+ @Inject
42
+ private wc: WorkflowContext;
43
+
39
44
  @ExternalQuery
40
45
  currentUser() {
41
46
  const { sessionID = 0, userID = 0 } = this.sessionUser;
@@ -103,6 +108,9 @@ export default class LoginSessionEvents extends EntityEvents<LoginSession> {
103
108
  throw new EntityAccessError("Invalid username");
104
109
  }
105
110
 
111
+ if (user.loginLockedTill && DateTime.from(user.loginLockedTill).msSinceEpoch > Date.now()) {
112
+ throw new EntityAccessError(`Too many failed login attempts try after some time`);
113
+ }
106
114
 
107
115
  const { checkPassword, passwordIsAccessToken } = entity;
108
116
  const now = DateTime.now;
@@ -216,6 +224,9 @@ export default class LoginSessionEvents extends EntityEvents<LoginSession> {
216
224
  async afterInsert(entity: LoginSession, entry: ChangeEntry) {
217
225
 
218
226
  if (entity.status !== "created") {
227
+
228
+ await FailedLoginLockWorkflow.queue(this.wc, entity);
229
+
219
230
  await this.sessionUser.setAuthCookie({
220
231
  userID: entity.userID,
221
232
  sessionID: entity.sessionID,
@@ -350,6 +361,8 @@ export default class LoginSessionEvents extends EntityEvents<LoginSession> {
350
361
  return;
351
362
  }
352
363
 
364
+ await FailedLoginLockWorkflow.cancel(this.wc, entity);
365
+
353
366
  await this.sessionUser.setAuthCookie({
354
367
  userID: entity.userID,
355
368
  sessionID: entity.sessionID,
@@ -135,7 +135,6 @@ export default class SnapshotService
135
135
  .first();
136
136
  if (templateFile) {
137
137
  const templateFileContent = await this.wcs.getSnapshotFile(ws, ["template.html"]);
138
- console.log({ text: await templateFileContent.readAsText()});
139
138
  // we need to save the file content..
140
139
  await this.storage.updateFile(templateFile, templateFileContent);
141
140
  await db.appFiles.statements.update({
@@ -57,6 +57,7 @@ import { AppWorkflowContext } from "./AppWorkflowContext.js";
57
57
  import DeleteFileHistoryWorkflow from "./files/DeleteFileHistoryWorkflow.js";
58
58
  import IndexEmailTextContentWorkflow from "./email/index/IndexEmailTextContentWorkflow.js";
59
59
  import IndexEmailAddressWorkflow from "./email/index/IndexEmailAddressWorkflow.js";
60
+ import FailedLoginLockWorkflow from "./user/login/FailedLoginLockWorkflow.js";
60
61
 
61
62
  @RegisterSingleton
62
63
  export default class SocialWorkflowContext extends AppWorkflowContext {
@@ -100,6 +101,10 @@ export default class SocialWorkflowContext extends AppWorkflowContext {
100
101
  // this.register(MigrateEmailFilesWorkflow);
101
102
  this.register(CleanupDeletedFilesWorkflow);
102
103
 
104
+
105
+ this.register(FailedLoginLockWorkflow);
106
+
107
+
103
108
  this.register(PendingPostReceiveWorkflow);
104
109
  this.register(PendingReferencesWorkflow);
105
110
 
@@ -0,0 +1,53 @@
1
+ import TimeSpan from "@entity-access/entity-access/dist/types/TimeSpan.js";
2
+ import Workflow, { UniqueActivity } from "@entity-access/entity-access/dist/workflows/Workflow.js";
3
+ import SocialMailContext from "../../../model/SocialMailContext.js";
4
+ import Inject from "@entity-access/entity-access/dist/di/di.js";
5
+ import Sql from "@entity-access/entity-access/dist/sql/Sql.js";
6
+ import DateTime from "@entity-access/entity-access/dist/types/DateTime.js";
7
+ import WorkflowContext from "@entity-access/entity-access/dist/workflows/WorkflowContext.js";
8
+ import type LoginSession from "../../../model/entities/LoginSession.js";
9
+
10
+ export default class FailedLoginLockWorkflow extends Workflow<{ userID }> {
11
+
12
+ static async queue(wc: WorkflowContext, { userID, sessionID }: LoginSession) {
13
+ return wc.queue(FailedLoginLockWorkflow, { userID }, { id: `track-failed-login-${sessionID}`});
14
+ }
15
+
16
+ static async cancel(wc: WorkflowContext, { sessionID }: LoginSession) {
17
+ return wc.storage.delete(`track-failed-login-${sessionID}`);
18
+ }
19
+
20
+ async run() {
21
+ this.preserveTime = TimeSpan.fromMinutes(1);
22
+ await this.delay(TimeSpan.fromMinutes(5));
23
+
24
+ await this.lockUser();
25
+ }
26
+
27
+ @UniqueActivity
28
+ async lockUser(
29
+ @Inject
30
+ db?: SocialMailContext
31
+ ) {
32
+ const max = 5;
33
+
34
+ const all = await db.loginSessions.where(this.input, (p) => (x) => x.userID === p.userID
35
+ && x.status !== "created"
36
+ && x.dateUpdated > Sql.date.addMinutes(Sql.date.now(), -5)
37
+ )
38
+ .orderByDescending(void 0, (p) => (x) => x.sessionID)
39
+ .limit(max)
40
+ .count();
41
+
42
+ if (max !== all) {
43
+ return;
44
+ }
45
+
46
+ const loginLockedTill = DateTime.now.addHours(1);
47
+ const { userID } = this.input;
48
+
49
+ await db.users.statements.update({ loginLockedTill }, { userID });
50
+ }
51
+
52
+
53
+ }