Skip to content

Commit

Permalink
feat(api): user logs table (#1777)
Browse files Browse the repository at this point in the history
* feat(api): user logs table

* cascade
  • Loading branch information
arnaudambro authored Nov 17, 2023
1 parent d310093 commit 742f26a
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 35 deletions.
8 changes: 8 additions & 0 deletions api/src/controllers/encrypt.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const {
Report,
TerritoryObservation,
sequelize,
UserLog,
} = require("../db/sequelize");

const { capture } = require("../sentry");
Expand Down Expand Up @@ -90,6 +91,13 @@ router.post(
organisation.set({ encrypting: true });
await organisation.save();

UserLog.create({
organisation: req.user.organisation,
user: req.user._id,
platform: req.headers.platform === "android" ? "app" : req.headers.platform === "dashboard" ? "dashboard" : "unknown",
action: "change-encryption-key",
});

try {
await sequelize.transaction(async (tx) => {
const {
Expand Down
96 changes: 61 additions & 35 deletions api/src/controllers/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const { validatePassword, looseUuidRegex, jwtRegex, sanitizeAll, headerJwtRegex
const mailservice = require("../utils/mailservice");
const config = require("../config");
const { comparePassword } = require("../utils");
const { User, RelUserTeam, Team, Organisation } = require("../db/sequelize");
const { User, RelUserTeam, Team, Organisation, UserLog } = require("../db/sequelize");
const validateUser = require("../middleware/validateUser");
const { capture } = require("../sentry");
const { ExtractJwt } = require("passport-jwt");
Expand Down Expand Up @@ -43,7 +43,7 @@ function logoutCookieOptions() {
}
}

function updateUserDebugInfos(req, user) {
function createUserLog(req, user) {
if (req.headers.platform === "android") {
try {
z.object({
Expand Down Expand Up @@ -72,30 +72,35 @@ function updateUserDebugInfos(req, user) {
capture(e, { extra: { body: req.body }, user });
return;
}
user.debugApp = {
version: req.headers.version,
apilevel: req.body.apilevel,
brand: req.body.brand,
carrier: req.body.carrier,
device: req.body.device,
deviceid: req.body.deviceid,
freediskstorage: req.body.freediskstorage,
hardware: req.body.hardware,
manufacturer: req.body.manufacturer,
maxmemory: req.body.maxmemory,
model: req.body.model,
product: req.body.product,
readableversion: req.body.readableversion,
systemname: req.body.systemname,
systemversion: req.body.systemversion,
buildid: req.body.buildid,
totaldiskcapacity: req.body.totaldiskcapacity,
totalmemory: req.body.totalmemory,
useragent: req.body.useragent,
tablet: req.body.tablet,
};
}
if (req.headers.platform === "dashboard") {
UserLog.create({
user: user._id,
organisation: user.organisation,
platform: "app",
action: "login",
debugApp: {
version: req.headers.version,
apilevel: req.body.apilevel,
brand: req.body.brand,
carrier: req.body.carrier,
device: req.body.device,
deviceid: req.body.deviceid,
freediskstorage: req.body.freediskstorage,
hardware: req.body.hardware,
manufacturer: req.body.manufacturer,
maxmemory: req.body.maxmemory,
model: req.body.model,
product: req.body.product,
readableversion: req.body.readableversion,
systemname: req.body.systemname,
systemversion: req.body.systemversion,
buildid: req.body.buildid,
totaldiskcapacity: req.body.totaldiskcapacity,
totalmemory: req.body.totalmemory,
useragent: req.body.useragent,
tablet: req.body.tablet,
},
});
} else if (req.headers.platform === "dashboard") {
try {
z.object({
body: z.object({
Expand All @@ -112,13 +117,26 @@ function updateUserDebugInfos(req, user) {
capture(e, { extra: { body: req.body }, user });
return;
}
user.debugDashboard = {
browserType: req.body.browsertype,
browserName: req.body.browsername,
browserVersion: req.body.browserversion,
browserOs: req.body.browseros,
version: req.headers.version,
};
UserLog.create({
user: user._id,
organisation: user.organisation,
platform: "dashboard",
action: "login",
debugDashboard: {
browserType: req.body.browsertype,
browserName: req.body.browsername,
browserVersion: req.body.browserversion,
browserOs: req.body.browseros,
version: req.headers.version,
},
});
} else {
UserLog.create({
user: user._id,
organisation: user.organisation,
platform: "unknown",
action: "login",
});
}
}

Expand All @@ -141,7 +159,13 @@ router.post(
"/logout",
passport.authenticate("user", { session: false }),
validateUser(["admin", "normal", "superadmin", "restricted-access"]),
catchErrors(async (_req, res) => {
catchErrors(async (req, res) => {
UserLog.create({
organisation: req.user.organisation,
user: req.user._id,
platform: req.headers.platform === "android" ? "app" : req.headers.platform === "dashboard" ? "dashboard" : "unknown",
action: "logout",
});
res.clearCookie("jwt", logoutCookieOptions());
return res.status(200).send({ ok: true });
})
Expand Down Expand Up @@ -174,7 +198,7 @@ router.post(
if (!match) return res.status(403).send({ ok: false, error: "E-mail ou mot de passe incorrect", code: EMAIL_OR_PASSWORD_INVALID });
user.lastLoginAt = new Date();

updateUserDebugInfos(req, user);
createUserLog(req, user);

await user.save();
// restricted-access users cannot acces the app
Expand Down Expand Up @@ -225,6 +249,8 @@ router.get(
const userTeams = await RelUserTeam.findAll({ where: { user: user._id, team: { [Op.in]: orgTeams.map((t) => t._id) } } });
const teams = userTeams.map((rel) => orgTeams.find((t) => t._id === rel.team));

createUserLog(req, user);

return res.status(200).send({ ok: true, token, user: serializeUserWithTeamsAndOrganisation(user, teams, organisation) });
})
);
Expand Down
29 changes: 29 additions & 0 deletions api/src/db/migrations/20231117084743-new-table-userLog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use strict";

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.sequelize.query(`
CREATE TABLE IF NOT EXISTS mano."UserLog" (
_id uuid NOT NULL,
"createdAt" timestamp with time zone NOT NULL,
"updatedAt" timestamp with time zone NOT NULL,
organisation uuid,
"user" uuid,
platform text,
action text,
"debugApp" jsonb,
"debugDashboard" jsonb,
PRIMARY KEY (_id),
CONSTRAINT "UserLog_organisation_fkey" FOREIGN KEY (organisation) REFERENCES mano."Organisation"(_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
CONSTRAINT "UserLog_user_fkey" FOREIGN KEY ("user") REFERENCES mano."User"(_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE
);
CREATE INDEX "UserLog_organisation_idx" ON mano."UserLog" USING btree (organisation);
CREATE INDEX "UserLog_user_idx" ON mano."UserLog" USING btree ("user");
`);
},

async down() {
// Qui fait des down, et pourquoi ?
},
};
27 changes: 27 additions & 0 deletions api/src/db/migrations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Pour ajouter une migration, faites

```bash
npx sequelize-cli migration:generate --name mon-fichier
```

Puis modifiez le fichier créé dans `api/src/db/migrations/` pour ajouter les instructions SQL nécessaires.

Par exemple

```
"use strict";
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.sequelize.query(`
ALTER TABLE "mano"."User"
ADD COLUMN IF NOT EXISTS "phone" text;
`);
},
async down() {
// Qui fait des down, et pourquoi ?
},
};
```
1 change: 1 addition & 0 deletions api/src/db/sequelize.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ db.TerritoryObservation = require("../models/territoryObservation")(sequelize, S
db.Treatment = require("../models/treatment")(sequelize, Sequelize);
db.User = require("../models/user")(sequelize, Sequelize);
db.PersonBackup = require("../models/personBackup")(sequelize, Sequelize);
db.UserLog = require("../models/userLog")(sequelize, Sequelize);

Object.keys(db).forEach((modelName) => {
if (db[modelName].associate) {
Expand Down
24 changes: 24 additions & 0 deletions api/src/models/userLog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const { Model, Deferrable } = require("sequelize");

module.exports = (sequelize, DataTypes) => {
const schema = {
_id: { type: DataTypes.UUID, allowNull: false, defaultValue: DataTypes.UUIDV4, primaryKey: true },
organisation: { type: DataTypes.UUID, references: { model: "Organisation", key: "_id", deferrable: Deferrable.INITIALLY_IMMEDIATE } },
user: { type: DataTypes.UUID, references: { model: "User", key: "_id", deferrable: Deferrable.INITIALLY_IMMEDIATE } },
platform: DataTypes.TEXT, // dashboard, app
action: DataTypes.TEXT, // login, logout, change-encryption-key
debugApp: DataTypes.JSONB,
debugDashboard: DataTypes.JSONB,
};

class UserLog extends Model {
static associate({ Organisation, User }) {
Organisation.hasMany(UserLog, { foreignKey: { type: DataTypes.UUID, name: "organisation", field: "organisation" } });
User.hasMany(UserLog, { foreignKey: { type: DataTypes.UUID, name: "organisation", field: "organisation" } });
}
}

UserLog.init(schema, { sequelize, modelName: "UserLog", freezeTableName: true, timestamps: true });

return UserLog;
};

0 comments on commit 742f26a

Please sign in to comment.