@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.
- package/README.md +7 -2
- package/__tests__/unit/components/login-history/login-history.repository.spec.ts +95 -0
- package/__tests__/unit/components/login-user/login-user.spec.ts +223 -0
- package/__tests__/unit/components/login-user/user.repository.spec.ts +81 -0
- package/__tests__/unit/components/password-hash/password-hash.service.spec.ts +33 -0
- package/__tests__/unit/components/system/system.repository.spec.ts +88 -0
- package/__tests__/unit/components/system-access/system-access.repository.spec.ts +78 -0
- package/__tests__/unit/redis-client/redis.service.spec.ts +24 -0
- package/__tests__/unit/session/session.service.spec.ts +27 -0
- package/create-sso-user.sql +1 -0
- package/dist/__tests__/unit/components/login-user/login-user.spec.d.ts +1 -0
- package/dist/__tests__/unit/components/login-user/login-user.spec.js +208 -0
- package/dist/__tests__/unit/components/login-user/login-user.spec.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/jest.config.js +15 -0
- package/package.json +21 -3
- package/prisma/migrations/0_init/migration.sql +41 -42
- package/prisma/migrations/20230528161352_create_user_user_group_and_add_new_column/migration.sql +2 -2
- package/prisma/migrations/20230606091407_create_login_histories_table/migration.sql +17 -0
- package/prisma/schema.prisma +46 -33
- package/sampledotenv +8 -0
- package/src/components/index.ts +7 -0
- package/src/components/login-history/index.ts +1 -0
- package/src/components/login-history/login-history.repository.ts +33 -0
- package/src/components/login-user/index.ts +4 -0
- package/src/components/login-user/interfaces/index.ts +1 -0
- package/src/components/login-user/interfaces/user-info.interface.ts +9 -0
- package/src/components/login-user/login-user.ts +427 -0
- package/src/components/login-user/user.repository.ts +33 -0
- package/src/components/password-hash/index.ts +2 -0
- package/src/components/password-hash/interfaces/index.ts +1 -0
- package/src/components/password-hash/interfaces/password-hash-service.interface.ts +4 -0
- package/src/components/password-hash/password-hash.service.ts +14 -0
- package/src/components/system/index.ts +1 -0
- package/src/components/system/system.repository.ts +33 -0
- package/src/components/system-access/index.ts +1 -0
- package/src/components/system-access/system-access.repository.ts +33 -0
- package/src/components/user-group/index.ts +1 -0
- package/src/components/user-group/user-group.repository.ts +33 -0
- package/src/components/user-user-group/index.ts +1 -0
- package/src/components/user-user-group/user-user-group.repository.ts +33 -0
- package/src/index.ts +7 -0
- package/src/interfaces/index.ts +2 -0
- package/src/interfaces/system-login.interface.ts +6 -0
- package/src/interfaces/user-session.interface.ts +5 -0
- package/src/mail/index.ts +2 -0
- package/src/mail/interfaces/index.ts +2 -0
- package/src/mail/interfaces/send-mail.interface.ts +8 -0
- package/src/mail/interfaces/send-new-login-alert.interface.ts +6 -0
- package/src/mail/mail.service.ts +33 -0
- package/src/mail/mail.ts +40 -0
- package/src/prisma-client/__mocks__/prisma.ts +15 -0
- package/src/prisma-client/client.ts +3 -0
- package/src/prisma-client/index.ts +1 -0
- package/src/redis-client/__mocks__/jest-initial-setup.ts +2 -0
- package/src/redis-client/__mocks__/redis-mock.ts +28 -0
- package/src/redis-client/index.ts +1 -0
- package/src/redis-client/redis.service.ts +48 -0
- package/src/session/index.ts +2 -0
- package/src/session/interfaces/index.ts +1 -0
- package/src/session/interfaces/session-service.interface.ts +6 -0
- package/src/session/session.service.ts +45 -0
- package/tsconfig.json +7 -3
- package/dist/index.d.ts +0 -0
- package/dist/index.js +0 -1
- 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
|
+
}
|
package/src/mail/mail.ts
ADDED
@@ -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 @@
|
|
1
|
+
export * from './client';
|
@@ -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 @@
|
|
1
|
+
export * from './session-service.interface';
|
@@ -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":""}
|