@tomei/sso 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +7 -2
  2. package/__tests__/unit/components/login-history/login-history.repository.spec.ts +95 -0
  3. package/__tests__/unit/components/login-user/login-user.spec.ts +223 -0
  4. package/__tests__/unit/components/login-user/user.repository.spec.ts +81 -0
  5. package/__tests__/unit/components/password-hash/password-hash.service.spec.ts +33 -0
  6. package/__tests__/unit/components/system/system.repository.spec.ts +88 -0
  7. package/__tests__/unit/components/system-access/system-access.repository.spec.ts +78 -0
  8. package/__tests__/unit/redis-client/redis.service.spec.ts +24 -0
  9. package/__tests__/unit/session/session.service.spec.ts +27 -0
  10. package/create-sso-user.sql +1 -0
  11. package/dist/__tests__/unit/components/login-user/login-user.spec.d.ts +1 -0
  12. package/dist/__tests__/unit/components/login-user/login-user.spec.js +208 -0
  13. package/dist/__tests__/unit/components/login-user/login-user.spec.js.map +1 -0
  14. package/dist/tsconfig.tsbuildinfo +1 -1
  15. package/jest.config.js +15 -0
  16. package/package.json +21 -3
  17. package/prisma/migrations/0_init/migration.sql +41 -42
  18. package/prisma/migrations/20230528161352_create_user_user_group_and_add_new_column/migration.sql +2 -2
  19. package/prisma/migrations/20230606091407_create_login_histories_table/migration.sql +17 -0
  20. package/prisma/schema.prisma +46 -33
  21. package/sampledotenv +8 -0
  22. package/src/components/index.ts +7 -0
  23. package/src/components/login-history/index.ts +1 -0
  24. package/src/components/login-history/login-history.repository.ts +33 -0
  25. package/src/components/login-user/index.ts +4 -0
  26. package/src/components/login-user/interfaces/index.ts +1 -0
  27. package/src/components/login-user/interfaces/user-info.interface.ts +9 -0
  28. package/src/components/login-user/login-user.ts +427 -0
  29. package/src/components/login-user/user.repository.ts +33 -0
  30. package/src/components/password-hash/index.ts +2 -0
  31. package/src/components/password-hash/interfaces/index.ts +1 -0
  32. package/src/components/password-hash/interfaces/password-hash-service.interface.ts +4 -0
  33. package/src/components/password-hash/password-hash.service.ts +14 -0
  34. package/src/components/system/index.ts +1 -0
  35. package/src/components/system/system.repository.ts +33 -0
  36. package/src/components/system-access/index.ts +1 -0
  37. package/src/components/system-access/system-access.repository.ts +33 -0
  38. package/src/components/user-group/index.ts +1 -0
  39. package/src/components/user-group/user-group.repository.ts +33 -0
  40. package/src/components/user-user-group/index.ts +1 -0
  41. package/src/components/user-user-group/user-user-group.repository.ts +33 -0
  42. package/src/index.ts +7 -0
  43. package/src/interfaces/index.ts +2 -0
  44. package/src/interfaces/system-login.interface.ts +6 -0
  45. package/src/interfaces/user-session.interface.ts +5 -0
  46. package/src/mail/index.ts +2 -0
  47. package/src/mail/interfaces/index.ts +2 -0
  48. package/src/mail/interfaces/send-mail.interface.ts +8 -0
  49. package/src/mail/interfaces/send-new-login-alert.interface.ts +6 -0
  50. package/src/mail/mail.service.ts +33 -0
  51. package/src/mail/mail.ts +40 -0
  52. package/src/prisma-client/__mocks__/prisma.ts +15 -0
  53. package/src/prisma-client/client.ts +3 -0
  54. package/src/prisma-client/index.ts +1 -0
  55. package/src/redis-client/__mocks__/jest-initial-setup.ts +2 -0
  56. package/src/redis-client/__mocks__/redis-mock.ts +28 -0
  57. package/src/redis-client/index.ts +1 -0
  58. package/src/redis-client/redis.service.ts +48 -0
  59. package/src/session/index.ts +2 -0
  60. package/src/session/interfaces/index.ts +1 -0
  61. package/src/session/interfaces/session-service.interface.ts +6 -0
  62. package/src/session/session.service.ts +45 -0
  63. package/tsconfig.json +7 -3
  64. package/dist/index.d.ts +0 -0
  65. package/dist/index.js +0 -1
  66. package/dist/index.js.map +0 -1
@@ -0,0 +1,33 @@
1
+ import { ISendNewLoginAlertOptions } from './interfaces/send-new-login-alert.interface';
2
+ import Mail from './mail';
3
+
4
+ export class MailService {
5
+ MailClient: Mail;
6
+ constructor() {
7
+ this.MailClient = Mail.init();
8
+ }
9
+
10
+ public async sendNewLoginAlertEmail(
11
+ options: ISendNewLoginAlertOptions,
12
+ ): Promise<void> {
13
+ try {
14
+ await this.MailClient.sendMail({
15
+ to: options.Email,
16
+ subject: 'New Login Alert',
17
+ html: `<p>Dear ${options.Name},</p>
18
+ <p>There was a new login to your account from ${
19
+ options.IpAddress
20
+ } on ${options.LoginDate.toLocaleString()}.</p>
21
+ <p>If this was you, you can safely ignore this email.</p>
22
+ <p>If you suspect that someone else is trying to access your account, please contact us immediately at itd-support@tomei.com.my.</p>
23
+ <p>Thank you!,</p>
24
+ <p>
25
+ Best Regards,
26
+ IT Department
27
+ </p>`,
28
+ });
29
+ } catch (error) {
30
+ throw error;
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,40 @@
1
+ import * as nodemailer from 'nodemailer';
2
+ import { ISendMailOptionsInterface } from './interfaces/send-mail.interface';
3
+
4
+ export default class Mail {
5
+ private static transporter: nodemailer.Transporter;
6
+
7
+ constructor() {}
8
+
9
+ static init() {
10
+ if (!Mail.transporter) {
11
+ Mail.transporter = nodemailer.createTransport({
12
+ host: process.env.SMTP_HOST,
13
+ port: Number(process.env.SMTP_PORT),
14
+ secure: Number(process.env.SMTP_PORT) === 465,
15
+ auth: {
16
+ user: process.env.EMAIL_SENDER,
17
+ pass: process.env.EMAIL_PASSWORD,
18
+ },
19
+ });
20
+ }
21
+ return new Mail();
22
+ }
23
+
24
+ //SEND MAIL
25
+ async sendMail(options: ISendMailOptionsInterface): Promise<void> {
26
+ try {
27
+ await Mail.transporter.sendMail({
28
+ from: process.env.EMAIL_SENDER,
29
+ to: options.to,
30
+ cc: options.cc,
31
+ bcc: options.bcc,
32
+ subject: options.subject,
33
+ text: options.text,
34
+ html: options.html,
35
+ });
36
+ } catch (error) {
37
+ throw error;
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,15 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+ import { mockDeep, mockReset, DeepMockProxy } from 'jest-mock-extended';
3
+
4
+ import prisma from '../client';
5
+
6
+ jest.mock('../client', () => ({
7
+ __esModule: true,
8
+ default: mockDeep<PrismaClient>(),
9
+ }));
10
+
11
+ beforeEach(() => {
12
+ mockReset(prismaMock);
13
+ });
14
+
15
+ export const prismaMock = prisma as unknown as DeepMockProxy<PrismaClient>;
@@ -0,0 +1,3 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+ const prisma = new PrismaClient();
3
+ export default prisma;
@@ -0,0 +1 @@
1
+ export * from './client';
@@ -0,0 +1,2 @@
1
+ import redis from './redis-mock';
2
+ jest.mock('redis', () => redis);
@@ -0,0 +1,28 @@
1
+ /* eslint-disable @typescript-eslint/explicit-function-return-type */
2
+ // redis-mock has not been updated for node-redis v4 yet, but the main changes
3
+ // in the API are camelCase names and promises instead of callback, so we can work around it.
4
+ // https://github.com/yeahoffline/redis-mock/issues/195
5
+ import redis, { createClient } from 'redis-mock';
6
+ // @ts-expect-error Work-around redis-mock types reporting incorrectly as v4 redis.
7
+ import { RedisClient } from '@types/redis';
8
+ import { promisify } from 'util';
9
+ const client = createClient() as unknown as RedisClient;
10
+ const setEx = promisify(client.setex).bind(client);
11
+ const v4Client = {
12
+ connect: () => undefined,
13
+ get: promisify(client.get).bind(client),
14
+ set: promisify(client.set).bind(client),
15
+ del: promisify(client.del).bind(client),
16
+ hSet: promisify(client.hset).bind(client),
17
+ hGet: promisify(client.hget).bind(client),
18
+ hDel: promisify(client.hdel).bind(client),
19
+ flushAll: promisify(client.flushall).bind(client),
20
+ setEx: promisify(client.setex).bind(client),
21
+ expire: promisify(client.expire).bind(client),
22
+ mGet: promisify(client.mget).bind(client),
23
+ pSetEx: (key: string, ms: number, value: string) =>
24
+ setEx(key, ms / 1000, value),
25
+ on: () => undefined,
26
+ // Add additional functions as needed...
27
+ };
28
+ export default { ...redis, createClient: () => v4Client };
@@ -0,0 +1 @@
1
+ export * from './redis.service';
@@ -0,0 +1,48 @@
1
+ import { createClient } from 'redis';
2
+ export class RedisService {
3
+ private static client;
4
+
5
+ private constructor() {}
6
+
7
+ static async init(): Promise<RedisService> {
8
+ if (!RedisService.client) {
9
+ // Create a new Redis client
10
+ RedisService.client = createClient({
11
+ url: process.env.REDIS_URL,
12
+ password: process.env.REDIS_PASSWORD,
13
+ });
14
+
15
+ RedisService.client.on('error', (error) => {
16
+ console.error('Redis error:', error);
17
+ });
18
+ RedisService.client.on('connect', () => console.log('Redis connected'));
19
+ RedisService.client.on('reconnecting', () =>
20
+ console.log('Redis reconnecting'),
21
+ );
22
+
23
+ await RedisService.client.connect();
24
+ }
25
+ return new RedisService();
26
+ }
27
+
28
+ public async set(
29
+ key: string,
30
+ value: any,
31
+ expire: number = 3600 * 24 * 1,
32
+ ): Promise<void> {
33
+ try {
34
+ const newList = typeof value === 'string' ? value : JSON.stringify(value);
35
+ await RedisService.client.setEx(key, expire, newList);
36
+ } catch (error) {
37
+ console.log(error);
38
+ }
39
+ }
40
+
41
+ public async get(key: string): Promise<null> {
42
+ try {
43
+ return await RedisService.client.get(key);
44
+ } catch (error) {
45
+ throw error;
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,2 @@
1
+ export * from './interfaces';
2
+ export * from './session.service';
@@ -0,0 +1 @@
1
+ export * from './session-service.interface';
@@ -0,0 +1,6 @@
1
+ import { IUserSession } from '../../interfaces/user-session.interface';
2
+
3
+ export interface ISessionService {
4
+ retrieveUserSession(userId: string): Promise<IUserSession>;
5
+ setUserSession(userId: string, sessionData: IUserSession): Promise<void>;
6
+ }
@@ -0,0 +1,45 @@
1
+ import { RedisService } from '../redis-client/redis.service';
2
+ import { ISessionService } from './interfaces/session-service.interface';
3
+ import { IUserSession } from '../interfaces/user-session.interface';
4
+
5
+ export class SessionService implements ISessionService {
6
+ private static _RedisService: RedisService;
7
+
8
+ private constructor() {}
9
+
10
+ static async init(): Promise<SessionService> {
11
+ const sessionService = new SessionService();
12
+ SessionService._RedisService = await RedisService.init();
13
+ return sessionService;
14
+ }
15
+
16
+ async setUserSession(
17
+ userId: string,
18
+ sessionData: IUserSession,
19
+ ): Promise<void> {
20
+ try {
21
+ const key = `tomei-sid:${userId}`;
22
+ await SessionService._RedisService.set(
23
+ key,
24
+ sessionData,
25
+ 60 * 60 * 24 * 1,
26
+ );
27
+ } catch (error) {
28
+ throw error;
29
+ }
30
+ }
31
+
32
+ async retrieveUserSession(userId: string): Promise<IUserSession> {
33
+ try {
34
+ const key = `tomei-sid:${userId}`;
35
+ const stringData = await SessionService._RedisService.get(key);
36
+ if (!stringData)
37
+ return {
38
+ systemLogins: [],
39
+ };
40
+ return JSON.parse(stringData);
41
+ } catch (error) {
42
+ throw error;
43
+ }
44
+ }
45
+ }
package/tsconfig.json CHANGED
@@ -10,13 +10,17 @@
10
10
  "target": "es6",
11
11
  "sourceMap": true,
12
12
  "outDir": "./dist",
13
- "baseUrl": "./",
13
+ "baseUrl": "./src",
14
14
  "incremental": true,
15
15
  "skipLibCheck": true,
16
- "strictNullChecks": false,
17
16
  "noImplicitAny": false,
18
17
  "strictBindCallApply": false,
19
18
  "forceConsistentCasingInFileNames": false,
20
19
  "noFallthroughCasesInSwitch": false,
21
- }
20
+ "strictNullChecks": true,
21
+ },
22
+ "exclude": [
23
+ "./dist/**/*",
24
+ "./dist"
25
+ ]
22
26
  }
package/dist/index.d.ts DELETED
File without changes
package/dist/index.js DELETED
@@ -1 +0,0 @@
1
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}