147 lines
3.5 KiB
TypeScript
147 lines
3.5 KiB
TypeScript
import { Inject, Injectable } from '@nestjs/common';
|
|
import { Request } from 'express';
|
|
import { Repository } from 'typeorm';
|
|
import { User } from '../user/user.entity';
|
|
import { AuditLog } from './audit.entity';
|
|
import { AuditAction } from './audit.enum';
|
|
import { lookup, Lookup } from 'geoip-lite';
|
|
import { parse, Details } from 'express-useragent';
|
|
import { FormUtilityService } from 'src/modules/utility/services/form-utility.service';
|
|
|
|
export interface UserLoginEntry {
|
|
login_at: Date;
|
|
current: boolean;
|
|
location: Partial<Lookup>;
|
|
user_agent: Partial<Details>;
|
|
}
|
|
|
|
@Injectable()
|
|
export class AuditService {
|
|
constructor(
|
|
@Inject('AUDIT_REPOSITORY')
|
|
private readonly audit: Repository<AuditLog>,
|
|
private readonly form: FormUtilityService,
|
|
) {}
|
|
|
|
public async insertAudit(
|
|
action: AuditAction,
|
|
comment?: string,
|
|
user?: User,
|
|
ip?: string,
|
|
ua?: string,
|
|
) {
|
|
const audit = new AuditLog();
|
|
audit.action = action as string;
|
|
audit.content = comment;
|
|
audit.actor_ip = ip;
|
|
audit.actor_ua = ua;
|
|
audit.actor = user;
|
|
|
|
if (
|
|
action === AuditAction.MALICIOUS_REQUEST ||
|
|
action === AuditAction.THROTTLE
|
|
) {
|
|
audit.flagged = true;
|
|
// TODO: email administrator
|
|
}
|
|
|
|
await this.updateAudit(audit);
|
|
return audit;
|
|
}
|
|
|
|
public async auditRequest(
|
|
req: Request,
|
|
type: AuditAction,
|
|
comment?: string,
|
|
user?: User,
|
|
) {
|
|
return this.insertAudit(
|
|
type,
|
|
comment,
|
|
user || req.user || null,
|
|
req.ip,
|
|
req.header('user-agent'),
|
|
);
|
|
}
|
|
|
|
public getIPLocation(ip: string) {
|
|
return lookup(ip);
|
|
}
|
|
|
|
public getUserAgentInfo(ua: string) {
|
|
return parse(ua);
|
|
}
|
|
|
|
public async getUserLogins(
|
|
user: User,
|
|
sessid?: string,
|
|
): Promise<UserLoginEntry[]> {
|
|
const userLogins: UserLoginEntry[] = [];
|
|
const auditEntries = await this.audit.find({
|
|
where: { actor: { id: user.id }, action: AuditAction.LOGIN },
|
|
order: { created_at: 'DESC' },
|
|
take: 10,
|
|
});
|
|
|
|
auditEntries.forEach((entry) => {
|
|
userLogins.push({
|
|
login_at: entry.created_at,
|
|
current: sessid === entry.content,
|
|
location: entry.actor_ip
|
|
? this.form.pluckObject(this.getIPLocation(entry.actor_ip), [
|
|
'country',
|
|
'city',
|
|
'timezone',
|
|
'll',
|
|
])
|
|
: null,
|
|
user_agent: entry.actor_ua
|
|
? this.form.pluckObject(this.getUserAgentInfo(entry.actor_ua), [
|
|
'browser',
|
|
'version',
|
|
'os',
|
|
'platform',
|
|
])
|
|
: null,
|
|
});
|
|
});
|
|
|
|
return userLogins;
|
|
}
|
|
|
|
public async getUserAccountCreation(user: User) {
|
|
const auditEntry = await this.audit.findOne({
|
|
where: { actor: { id: user.id }, action: AuditAction.REGISTRATION },
|
|
});
|
|
|
|
if (!auditEntry) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
created_at: auditEntry.created_at,
|
|
ip: auditEntry.actor_ip,
|
|
location: auditEntry.actor_ip
|
|
? this.form.pluckObject(this.getIPLocation(auditEntry.actor_ip), [
|
|
'country',
|
|
'city',
|
|
'timezone',
|
|
'll',
|
|
])
|
|
: null,
|
|
user_agent: auditEntry.actor_ua
|
|
? this.form.pluckObject(this.getUserAgentInfo(auditEntry.actor_ua), [
|
|
'browser',
|
|
'version',
|
|
'os',
|
|
'platform',
|
|
])
|
|
: null,
|
|
};
|
|
}
|
|
|
|
public async updateAudit(audit: AuditLog): Promise<void> {
|
|
await this.audit.save(audit);
|
|
}
|
|
}
|