Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions auth/routes/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { checkPassword } from '../ldap.controller';
import { AuthUsers } from '../schemas/authUsers';
import { Organizacion } from './../../core/tm/schemas/organizacion';
import { Auth } from './../auth.class';
import { logUsuarioIngreso } from '../usuarioIngresos';
import { SystemLog } from '../../core/log/system.log';


const sha1Hash = require('sha1');
Expand Down Expand Up @@ -96,6 +98,11 @@ router.post('/v2/organizaciones', Auth.authenticate(), async (req, res, next) =>
const dto = await generateTokenPayload(username, orgId, account_id);
updateOrganizacion(usuario, orgId);
if (dto) {
try {
await logUsuarioIngreso(req, dto.payload.usuario, dto.payload.organizacion);
} catch (logErr) {
await SystemLog.error('logUsuarioIngreso', { username, orgId }, logErr, req);
}
return res.send({
token: dto.token
});
Expand All @@ -118,6 +125,11 @@ router.post('/organizaciones', Auth.authenticate(), async (req, res, next) => {
const oldToken: string = String(req.headers.authorization).substring(4);
const nuevosPermisos = user.organizaciones.find(item => String(item._id) === String(org._id));
const refreshToken = Auth.refreshToken(oldToken, user, [...user.permisosGlobales, ...nuevosPermisos.permisos], org);
try {
await logUsuarioIngreso(req, { id: user._id, usuario: user.usuario }, org);
} catch (logErr) {
await SystemLog.error('logUsuarioIngreso:legacy', { username, orgId }, logErr, req);
}
return res.send({
token: refreshToken
});
Expand Down
51 changes: 51 additions & 0 deletions auth/schemas/usuarioIngresos.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { AuditPlugin } from '@andes/mongoose-plugin-audit';
import { Document, Model, model, Schema, SchemaTypes, Types } from 'mongoose';

export const UsuarioIngresoSchema = new Schema({
usuario: {
id: { type: SchemaTypes.ObjectId, required: true },
usuario: { type: String, required: true }
},
start: { type: Date, required: true },
cantidad: { type: Number, default: 0 },
bucketNumber: { type: Number, default: 0 },
ingresos: [{
fecha: { type: Date, required: true },
organizacion: {
id: { type: SchemaTypes.ObjectId, required: true },
nombre: { type: String, required: true }
},
device: {
ip: String,
tipo: String,
os: String
}
}]
});

export interface IUsuarioIngresos extends Document {
usuario: {
id: Types.ObjectId;
usuario: string;
};
start: Date;
cantidad: number;
bucketNumber: number;
ingresos: [{
fecha: Date;
organizacion: {
id: Types.ObjectId;
nombre: string;
};
device: {
ip: string;
tipo: string;
os: string;
};
}];
}

UsuarioIngresoSchema.index({ 'usuario.id': 1, start: -1, bucketNumber: 1 }, { unique: true });
UsuarioIngresoSchema.plugin(AuditPlugin);

export const UsuarioIngreso: Model<IUsuarioIngresos> = model<IUsuarioIngresos>('usuarioIngreso', UsuarioIngresoSchema, 'usuarioIngresos');
107 changes: 107 additions & 0 deletions auth/usuarioIngresos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { UsuarioIngreso } from './schemas/usuarioIngresos.schema';
import * as moment from 'moment';

function parseUserAgent(uaString: string): { tipo: string; os: string } {
if (!uaString) {
return { tipo: 'Unknown', os: 'Unknown' };
}
const ua = uaString.toLowerCase();

let tipo = 'Desktop';
if (/mobile|android.*mobile|iphone|ipod|blackberry|windows phone/i.test(uaString)) {
tipo = 'Mobile';
} else if (/tablet|ipad|android(?!.*mobile)/i.test(uaString)) {
tipo = 'Tablet';
}

let os = 'Unknown';
if (/windows nt 10/i.test(ua)) {
os = 'Windows 10';
} else if (/windows nt 6\.3/i.test(ua)) {
os = 'Windows 8.1';
} else if (/windows nt 6\.2/i.test(ua)) {
os = 'Windows 8';
} else if (/windows nt 6\.1/i.test(ua)) {
os = 'Windows 7';
} else if (/windows/i.test(ua)) {
os = 'Windows';
} else if (/android/i.test(ua)) {
const match = ua.match(/android\s([\d.]+)/);
os = match ? `Android ${match[1]}` : 'Android';
} else if (/iphone os|ipad/i.test(ua)) {
const match = ua.match(/os ([\d_]+)/);
os = match ? `iOS ${match[1].replace(/_/g, '.')}` : 'iOS';
} else if (/mac os x/i.test(ua)) {
os = 'macOS';
} else if (/linux/i.test(ua)) {
os = 'Linux';
}

return { tipo, os };
}

export async function logUsuarioIngreso(req, user, organizacion) {
let bucketNumber = 0;
let retry = true;
while (retry) {
try {
await execLogIngreso(req, user, organizacion, bucketNumber);
retry = false;
} catch (err) {
if (err.code === 17419 || err.code === 11000) {
bucketNumber++;
} else {
retry = false;
throw err;
}
}
}
}

async function execLogIngreso(req, user, organizacion, bucketNumber) {
const now = new Date();
const start = moment(now).startOf('quarter').toDate();

const uaString = req.headers['user-agent'] || '';
const { tipo: deviceType, os } = parseUserAgent(uaString);
const forwarded = req.headers['x-forwarded-for'];
const rawIp = (typeof forwarded === 'string' ? forwarded.split(',')[0].trim() : null)
|| req.ip
|| req.connection?.remoteAddress
|| '';
const ip = rawIp.startsWith('::ffff:') ? rawIp.substring(7) : rawIp;

return UsuarioIngreso.update(
{
'usuario.id': user.id || user._id,
start,
bucketNumber
},
{
$inc: { cantidad: 1 },
$setOnInsert: {
usuario: {
id: user.id || user._id,
usuario: user.usuario || user.username
},
start,
bucketNumber
},
$push: {
ingresos: {
fecha: now,
organizacion: {
id: organizacion.id || organizacion._id,
nombre: organizacion.nombre
},
device: {
ip,
tipo: deviceType,
os
}
}
}
},
{ upsert: true }
);
}
Loading