Add login/logout logs

This commit is contained in:
Tuan Dang
2023-02-01 11:56:38 +07:00
parent 55522404b4
commit ca688764a3
15 changed files with 297 additions and 90 deletions

View File

@@ -73,7 +73,7 @@ app.use(
}) })
); );
app.use(requestIp.mw()) // app.use(requestIp.mw())
if (NODE_ENV === 'production') { if (NODE_ENV === 'production') {
// enable app-wide rate-limiting + helmet security // enable app-wide rate-limiting + helmet security

View File

@@ -4,16 +4,21 @@ import jwt from 'jsonwebtoken';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import * as bigintConversion from 'bigint-conversion'; import * as bigintConversion from 'bigint-conversion';
const jsrp = require('jsrp'); const jsrp = require('jsrp');
import { User } from '../../models'; import { User, LoginSRPDetail } from '../../models';
import { createToken, issueTokens, clearTokens } from '../../helpers/auth'; import { createToken, issueTokens, clearTokens } from '../../helpers/auth';
import {
ACTION_LOGIN,
ACTION_LOGOUT
} from '../../variables';
import { import {
NODE_ENV, NODE_ENV,
JWT_AUTH_LIFETIME, JWT_AUTH_LIFETIME,
JWT_AUTH_SECRET, JWT_AUTH_SECRET,
JWT_REFRESH_SECRET JWT_REFRESH_SECRET
} from '../../config'; } from '../../config';
import LoginSRPDetail from '../../models/LoginSRPDetail';
import { BadRequestError } from '../../utils/errors'; import { BadRequestError } from '../../utils/errors';
import { EELogService } from '../../ee/services';
import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this
declare module 'jsonwebtoken' { declare module 'jsonwebtoken' {
export interface UserIDJwtPayload extends jwt.JwtPayload { export interface UserIDJwtPayload extends jwt.JwtPayload {
@@ -116,6 +121,18 @@ export const login2 = async (req: Request, res: Response) => {
secure: NODE_ENV === 'production' ? true : false secure: NODE_ENV === 'production' ? true : false
}); });
const loginAction = await EELogService.createAction({
name: ACTION_LOGIN,
userId: user._id
});
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers['user-agent']),
ipAddress: req.ip
});
// return (access) token in response // return (access) token in response
return res.status(200).send({ return res.status(200).send({
token: tokens.token, token: tokens.token,
@@ -159,6 +176,19 @@ export const logout = async (req: Request, res: Response) => {
sameSite: 'strict', sameSite: 'strict',
secure: NODE_ENV === 'production' ? true : false secure: NODE_ENV === 'production' ? true : false
}); });
const logoutAction = await EELogService.createAction({
name: ACTION_LOGOUT,
userId: req.user._id
});
logoutAction && await EELogService.createLog({
userId: req.user._id,
actions: [logoutAction],
channel: getChannelFromUserAgent(req.headers['user-agent']),
ipAddress: req.ip
});
} catch (err) { } catch (err) {
Sentry.setUser({ email: req.user.email }); Sentry.setUser({ email: req.user.email });
Sentry.captureException(err); Sentry.captureException(err);

View File

@@ -4,12 +4,11 @@ import crypto from 'crypto';
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const jsrp = require('jsrp'); const jsrp = require('jsrp');
import * as bigintConversion from 'bigint-conversion'; import * as bigintConversion from 'bigint-conversion';
import { User, Token, BackupPrivateKey } from '../../models'; import { User, Token, BackupPrivateKey, LoginSRPDetail } from '../../models';
import { checkEmailVerification } from '../../helpers/signup'; import { checkEmailVerification } from '../../helpers/signup';
import { createToken } from '../../helpers/auth'; import { createToken } from '../../helpers/auth';
import { sendMail } from '../../helpers/nodemailer'; import { sendMail } from '../../helpers/nodemailer';
import { JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET, SITE_URL } from '../../config'; import { JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET, SITE_URL } from '../../config';
import LoginSRPDetail from '../../models/LoginSRPDetail';
import { BadRequestError } from '../../utils/errors'; import { BadRequestError } from '../../utils/errors';
/** /**

View File

@@ -79,7 +79,7 @@ export const createSecrets = async (req: Request, res: Response) => {
*/ */
const channel = getChannelFromUserAgent(req.headers['user-agent']) const channel = getChannelFromUserAgent(req.headers['user-agent'])
const { workspaceId, environment } = req.body; const { workspaceId, environment }: { workspaceId: string, environment: string } = req.body;
const hasAccess = await userHasWorkspaceAccess(req.user, workspaceId, environment, ABILITY_WRITE) const hasAccess = await userHasWorkspaceAccess(req.user, workspaceId, environment, ABILITY_WRITE)
if (!hasAccess) { if (!hasAccess) {
@@ -175,17 +175,17 @@ export const createSecrets = async (req: Request, res: Response) => {
})) }))
}); });
const addAction = await EELogService.createActionSecret({ const addAction = await EELogService.createAction({
name: ACTION_ADD_SECRETS, name: ACTION_ADD_SECRETS,
userId: req.user._id.toString(), userId: req.user._id,
workspaceId, workspaceId: new Types.ObjectId(workspaceId),
secretIds: newSecrets.map((n) => n._id) secretIds: newSecrets.map((n) => n._id)
}); });
// (EE) create (audit) log // (EE) create (audit) log
addAction && await EELogService.createLog({ addAction && await EELogService.createLog({
userId: req.user._id.toString(), userId: req.user._id.toString(),
workspaceId, workspaceId: new Types.ObjectId(workspaceId),
actions: [addAction], actions: [addAction],
channel, channel,
ipAddress: req.ip ipAddress: req.ip
@@ -300,16 +300,16 @@ export const getSecrets = async (req: Request, res: Response) => {
const channel = getChannelFromUserAgent(req.headers['user-agent']) const channel = getChannelFromUserAgent(req.headers['user-agent'])
const readAction = await EELogService.createActionSecret({ const readAction = await EELogService.createAction({
name: ACTION_READ_SECRETS, name: ACTION_READ_SECRETS,
userId: userId, userId: new Types.ObjectId(userId),
workspaceId: workspaceId as string, workspaceId: new Types.ObjectId(workspaceId as string),
secretIds: secrets.map((n: any) => n._id) secretIds: secrets.map((n: any) => n._id)
}); });
readAction && await EELogService.createLog({ readAction && await EELogService.createLog({
userId: userId, userId: new Types.ObjectId(userId),
workspaceId: workspaceId as string, workspaceId: new Types.ObjectId(workspaceId as string),
actions: [readAction], actions: [readAction],
channel, channel,
ipAddress: req.ip ipAddress: req.ip
@@ -505,17 +505,17 @@ export const updateSecrets = async (req: Request, res: Response) => {
}); });
}, 10000); }, 10000);
const updateAction = await EELogService.createActionSecret({ const updateAction = await EELogService.createAction({
name: ACTION_UPDATE_SECRETS, name: ACTION_UPDATE_SECRETS,
userId: req.user._id.toString(), userId: req.user._id,
workspaceId: key, workspaceId: new Types.ObjectId(key),
secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id) secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id)
}); });
// (EE) create (audit) log // (EE) create (audit) log
updateAction && await EELogService.createLog({ updateAction && await EELogService.createLog({
userId: req.user._id.toString(), userId: req.user._id.toString(),
workspaceId: key, workspaceId: new Types.ObjectId(key),
actions: [updateAction], actions: [updateAction],
channel, channel,
ipAddress: req.ip ipAddress: req.ip
@@ -631,17 +631,17 @@ export const deleteSecrets = async (req: Request, res: Response) => {
workspaceId: key workspaceId: key
}) })
}); });
const deleteAction = await EELogService.createActionSecret({ const deleteAction = await EELogService.createAction({
name: ACTION_DELETE_SECRETS, name: ACTION_DELETE_SECRETS,
userId: req.user._id.toString(), userId: req.user._id,
workspaceId: key, workspaceId: new Types.ObjectId(key),
secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id) secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id)
}); });
// (EE) create (audit) log // (EE) create (audit) log
deleteAction && await EELogService.createLog({ deleteAction && await EELogService.createLog({
userId: req.user._id.toString(), userId: req.user._id.toString(),
workspaceId: key, workspaceId: new Types.ObjectId(key),
actions: [deleteAction], actions: [deleteAction],
channel, channel,
ipAddress: req.ip ipAddress: req.ip

View File

@@ -1,39 +1,40 @@
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { SecretVersion, Action } from '../models'; import { Action } from '../models';
import { import {
getLatestSecretVersionIds, getLatestSecretVersionIds,
getLatestNSecretSecretVersionIds getLatestNSecretSecretVersionIds
} from '../helpers/secretVersion'; } from '../helpers/secretVersion';
import { ACTION_UPDATE_SECRETS } from '../../variables'; import {
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS,
ACTION_READ_SECRETS,
ACTION_DELETE_SECRETS,
ACTION_UPDATE_SECRETS,
} from '../../variables';
/** /**
* Create an (audit) action for secrets including * Create an (audit) action for updating secrets
* add, delete, update, and read actions.
* @param {Object} obj * @param {Object} obj
* @param {String} obj.name - name of action * @param {String} obj.name - name of action
* @param {ObjectId[]} obj.secretIds - ids of relevant secrets * @param {Types.ObjectId} obj.secretIds - ids of relevant secrets
* @returns {Action} action - new action * @returns {Action} action - new action
*/ */
const createActionSecretHelper = async ({ const createActionUpdateSecret = async ({
name, name,
userId, userId,
workspaceId, workspaceId,
secretIds secretIds
}: { }: {
name: string; name: string;
userId: string; userId: Types.ObjectId;
workspaceId: string; workspaceId: Types.ObjectId;
secretIds: Types.ObjectId[]; secretIds: Types.ObjectId[];
}) => { }) => {
let action; let action;
let latestSecretVersions;
try { try {
if (name === ACTION_UPDATE_SECRETS) { const latestSecretVersions = (await getLatestNSecretSecretVersionIds({
// case: action is updating secrets
// -> add old and new secret versions
latestSecretVersions = (await getLatestNSecretSecretVersionIds({
secretIds, secretIds,
n: 2 n: 2
})) }))
@@ -41,17 +42,7 @@ const createActionSecretHelper = async ({
oldSecretVersion: s.versions[0]._id, oldSecretVersion: s.versions[0]._id,
newSecretVersion: s.versions[1]._id newSecretVersion: s.versions[1]._id
})); }));
} else {
// case: action is adding, deleting, or reading secrets
// -> add new secret versions
latestSecretVersions = (await getLatestSecretVersionIds({
secretIds
}))
.map((s) => ({
newSecretVersion: s.versionId
}));
}
action = await new Action({ action = await new Action({
name, name,
user: userId, user: userId,
@@ -64,10 +55,148 @@ const createActionSecretHelper = async ({
} catch (err) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
Sentry.captureException(err); Sentry.captureException(err);
throw new Error('Failed to create update secret action');
}
return action;
}
/**
* Create an (audit) action for creating, reading, and deleting
* secrets
* @param {Object} obj
* @param {String} obj.name - name of action
* @param {Types.ObjectId} obj.secretIds - ids of relevant secrets
* @returns {Action} action - new action
*/
const createActionSecret = async ({
name,
userId,
workspaceId,
secretIds
}: {
name: string;
userId: Types.ObjectId;
workspaceId: Types.ObjectId;
secretIds: Types.ObjectId[];
}) => {
let action;
try {
// case: action is adding, deleting, or reading secrets
// -> add new secret versions
const latestSecretVersions = (await getLatestSecretVersionIds({
secretIds
}))
.map((s) => ({
newSecretVersion: s.versionId
}));
action = await new Action({
name,
user: userId,
workspace: workspaceId,
payload: {
secretVersions: latestSecretVersions
}
}).save();
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to create action create/read/delete secret action');
}
return action;
}
/**
* Create an (audit) action for user with id [userId]
* @param {Object} obj
* @param {String} obj.name - name of action
* @param {String} obj.userId - id of user associated with action
* @returns
*/
const createActionUser = ({
name,
userId
}: {
name: string;
userId: Types.ObjectId;
}) => {
let action;
try {
action = new Action({
name,
user: userId
}).save();
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to create user action');
}
return action;
}
/**
* Create an (audit) action.
* @param {Object} obj
* @param {Object} obj.name - name of action
* @param {Types.ObjectId} obj.userId - id of user associated with action
* @param {Types.ObjectId} obj.workspaceId - id of workspace associated with action
* @param {Types.ObjectId[]} obj.secretIds - ids of secrets associated with action
*/
const createActionHelper = async ({
name,
userId,
workspaceId,
secretIds,
}: {
name: string;
userId: Types.ObjectId;
workspaceId?: Types.ObjectId;
secretIds?: Types.ObjectId[];
}) => {
let action;
try {
switch (name) {
case ACTION_LOGIN:
case ACTION_LOGOUT:
action = await createActionUser({
name,
userId
});
break;
case ACTION_ADD_SECRETS:
case ACTION_READ_SECRETS:
case ACTION_DELETE_SECRETS:
if (!workspaceId || !secretIds) throw new Error('Missing required params workspace id or secret ids to create action secret');
action = await createActionSecret({
name,
userId,
workspaceId,
secretIds
});
break;
case ACTION_UPDATE_SECRETS:
if (!workspaceId || !secretIds) throw new Error('Missing required params workspace id or secret ids to create action secret');
action = await createActionUpdateSecret({
name,
userId,
workspaceId,
secretIds
});
break;
}
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to create action'); throw new Error('Failed to create action');
} }
return action; return action;
} }
export { createActionSecretHelper }; export {
createActionHelper
};

View File

@@ -1,9 +1,19 @@
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { Types } from 'mongoose';
import { import {
Log, Log,
IAction IAction
} from '../models'; } from '../models';
/**
* Create an (audit) log
* @param {Object} obj
* @param {Types.ObjectId} obj.userId - id of user associated with the log
* @param {Types.ObjectId} obj.workspaceId - id of workspace associated with the log
* @param {IAction[]} obj.actions - actions to include in log
* @param {String} obj.channel - channel (web/cli/auto) associated with the log
* @param {String} obj.ipAddress - ip address associated with the log
* @returns {Log} log - new audit log
*/
const createLogHelper = async ({ const createLogHelper = async ({
userId, userId,
workspaceId, workspaceId,
@@ -11,8 +21,8 @@ const createLogHelper = async ({
channel, channel,
ipAddress ipAddress
}: { }: {
userId: string; userId: Types.ObjectId;
workspaceId: string; workspaceId?: Types.ObjectId;
actions: IAction[]; actions: IAction[];
channel: string; channel: string;
ipAddress: string; ipAddress: string;
@@ -21,7 +31,7 @@ const createLogHelper = async ({
try { try {
log = await new Log({ log = await new Log({
user: userId, user: userId,
workspace: workspaceId, workspace: workspaceId ?? undefined,
actionNames: actions.map((a) => a.name), actionNames: actions.map((a) => a.name),
actions, actions,
channel, channel,

View File

@@ -1,10 +1,18 @@
import { Schema, model, Types } from 'mongoose'; import { Schema, model, Types } from 'mongoose';
import {
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS,
ACTION_DELETE_SECRETS
} from '../../variables';
export interface IAction { export interface IAction {
name: string; name: string;
user?: Types.ObjectId, user?: Types.ObjectId,
workspace?: Types.ObjectId, workspace?: Types.ObjectId,
payload: { payload?: {
secretVersions?: Types.ObjectId[] secretVersions?: Types.ObjectId[]
} }
} }
@@ -13,7 +21,15 @@ const actionSchema = new Schema<IAction>(
{ {
name: { name: {
type: String, type: String,
required: true required: true,
enum: [
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS,
ACTION_DELETE_SECRETS
]
}, },
user: { user: {
type: Schema.Types.ObjectId, type: Schema.Types.ObjectId,

View File

@@ -1,5 +1,7 @@
import { Schema, model, Types } from 'mongoose'; import { Schema, model, Types } from 'mongoose';
import { import {
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS, ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS, ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS, ACTION_READ_SECRETS,
@@ -29,6 +31,8 @@ const logSchema = new Schema<ILog>(
actionNames: { actionNames: {
type: [String], type: [String],
enum: [ enum: [
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS, ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS, ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS, ACTION_READ_SECRETS,

View File

@@ -1,14 +1,12 @@
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { import {
Log,
Action,
IAction IAction
} from '../models'; } from '../models';
import { import {
createLogHelper createLogHelper
} from '../helpers/log'; } from '../helpers/log';
import { import {
createActionSecretHelper createActionHelper
} from '../helpers/action'; } from '../helpers/action';
import EELicenseService from './EELicenseService'; import EELicenseService from './EELicenseService';
@@ -33,8 +31,8 @@ class EELogService {
channel, channel,
ipAddress ipAddress
}: { }: {
userId: string; userId: Types.ObjectId;
workspaceId: string; workspaceId?: Types.ObjectId;
actions: IAction[]; actions: IAction[];
channel: string; channel: string;
ipAddress: string; ipAddress: string;
@@ -50,26 +48,26 @@ class EELogService {
} }
/** /**
* Create an (audit) action for secrets including * Create an (audit) action
* add, delete, update, and read actions.
* @param {Object} obj * @param {Object} obj
* @param {String} obj.name - name of action * @param {String} obj.name - name of action
* @param {ObjectId[]} obj.secretIds - secret ids * @param {Types.ObjectId} obj.userId - id of user associated with the action
* @param {Types.ObjectId} obj.workspaceId - id of workspace associated with the action
* @param {ObjectId[]} obj.secretIds - ids of secrets associated with the action
* @returns {Action} action - new action * @returns {Action} action - new action
*/ */
static async createActionSecret({ static async createAction({
name, name,
userId, userId,
workspaceId, workspaceId,
secretIds secretIds
}: { }: {
name: string; name: string;
userId: string; userId: Types.ObjectId;
workspaceId: string; workspaceId?: Types.ObjectId;
secretIds: Types.ObjectId[]; secretIds?: Types.ObjectId[];
}) { }) {
if (!EELicenseService.isLicenseValid) return null; return await createActionHelper({
return await createActionSecretHelper({
name, name,
userId, userId,
workspaceId, workspaceId,

View File

@@ -406,10 +406,10 @@ const v2PushSecrets = async ({
secretIds: toDelete secretIds: toDelete
}); });
const deleteAction = await EELogService.createActionSecret({ const deleteAction = await EELogService.createAction({
name: ACTION_DELETE_SECRETS, name: ACTION_DELETE_SECRETS,
userId, userId: new Types.ObjectId(userId),
workspaceId, workspaceId: new Types.ObjectId(userId),
secretIds: toDelete secretIds: toDelete
}); });
@@ -499,10 +499,10 @@ const v2PushSecrets = async ({
}) })
}); });
const updateAction = await EELogService.createActionSecret({ const updateAction = await EELogService.createAction({
name: ACTION_UPDATE_SECRETS, name: ACTION_UPDATE_SECRETS,
userId, userId: new Types.ObjectId(userId),
workspaceId, workspaceId: new Types.ObjectId(workspaceId),
secretIds: toUpdate.map((u) => u._id) secretIds: toUpdate.map((u) => u._id)
}); });
@@ -536,10 +536,10 @@ const v2PushSecrets = async ({
}) })
}); });
const addAction = await EELogService.createActionSecret({ const addAction = await EELogService.createAction({
name: ACTION_ADD_SECRETS, name: ACTION_ADD_SECRETS,
userId, userId: new Types.ObjectId(userId),
workspaceId, workspaceId: new Types.ObjectId(workspaceId),
secretIds: newSecrets.map((n) => n._id) secretIds: newSecrets.map((n) => n._id)
}); });
addAction && actions.push(addAction); addAction && actions.push(addAction);
@@ -553,8 +553,8 @@ const v2PushSecrets = async ({
// (EE) create (audit) log // (EE) create (audit) log
if (actions.length > 0) { if (actions.length > 0) {
await EELogService.createLog({ await EELogService.createLog({
userId, userId: new Types.ObjectId(userId),
workspaceId, workspaceId: new Types.ObjectId(workspaceId),
actions, actions,
channel, channel,
ipAddress ipAddress
@@ -645,16 +645,16 @@ const pullSecrets = async ({
environment environment
}) })
const readAction = await EELogService.createActionSecret({ const readAction = await EELogService.createAction({
name: ACTION_READ_SECRETS, name: ACTION_READ_SECRETS,
userId, userId: new Types.ObjectId(userId),
workspaceId, workspaceId: new Types.ObjectId(workspaceId),
secretIds: secrets.map((n: any) => n._id) secretIds: secrets.map((n: any) => n._id)
}); });
readAction && await EELogService.createLog({ readAction && await EELogService.createLog({
userId, userId: new Types.ObjectId(userId),
workspaceId, workspaceId: new Types.ObjectId(workspaceId),
actions: [readAction], actions: [readAction],
channel, channel,
ipAddress ipAddress

View File

@@ -1,6 +1,14 @@
import mongoose, { Schema, model } from 'mongoose'; import mongoose, { Schema, model, Types } from 'mongoose';
const LoginSRPDetailSchema = new Schema( export interface ILoginSRPDetail {
_id: Types.ObjectId;
clientPublicKey: string;
email: string;
serverBInt: mongoose.Schema.Types.Buffer;
expireAt: Date;
}
const loginSRPDetailSchema = new Schema<ILoginSRPDetail>(
{ {
clientPublicKey: { clientPublicKey: {
type: String, type: String,
@@ -16,7 +24,7 @@ const LoginSRPDetailSchema = new Schema(
} }
); );
const LoginSRPDetail = model('LoginSRPDetail', LoginSRPDetailSchema); const LoginSRPDetail = model('LoginSRPDetail', loginSRPDetailSchema);
// LoginSRPDetailSchema.index({ "expireAt": 1 }, { expireAfterSeconds: 0 }); // LoginSRPDetailSchema.index({ "expireAt": 1 }, { expireAfterSeconds: 0 });

View File

@@ -16,6 +16,7 @@ import UserAction, { IUserAction } from './userAction';
import Workspace, { IWorkspace } from './workspace'; import Workspace, { IWorkspace } from './workspace';
import ServiceTokenData, { IServiceTokenData } from './serviceTokenData'; import ServiceTokenData, { IServiceTokenData } from './serviceTokenData';
import APIKeyData, { IAPIKeyData } from './apiKeyData'; import APIKeyData, { IAPIKeyData } from './apiKeyData';
import LoginSRPDetail, { ILoginSRPDetail } from './loginSRPDetail';
export { export {
BackupPrivateKey, BackupPrivateKey,
@@ -53,5 +54,7 @@ export {
ServiceTokenData, ServiceTokenData,
IServiceTokenData, IServiceTokenData,
APIKeyData, APIKeyData,
IAPIKeyData IAPIKeyData,
LoginSRPDetail,
ILoginSRPDetail
}; };

View File

@@ -12,6 +12,7 @@ export interface IUser {
salt?: string; salt?: string;
verifier?: string; verifier?: string;
refreshVersion?: number; refreshVersion?: number;
seenIps: [string];
} }
const userSchema = new Schema<IUser>( const userSchema = new Schema<IUser>(
@@ -54,7 +55,8 @@ const userSchema = new Schema<IUser>(
type: Number, type: Number,
default: 0, default: 0,
select: false select: false
} },
seenIps: [String]
}, },
{ {
timestamps: true timestamps: true

View File

@@ -1,9 +1,13 @@
const ACTION_LOGIN = 'login';
const ACTION_LOGOUT = 'logout';
const ACTION_ADD_SECRETS = 'addSecrets'; const ACTION_ADD_SECRETS = 'addSecrets';
const ACTION_DELETE_SECRETS = 'deleteSecrets'; const ACTION_DELETE_SECRETS = 'deleteSecrets';
const ACTION_UPDATE_SECRETS = 'updateSecrets'; const ACTION_UPDATE_SECRETS = 'updateSecrets';
const ACTION_READ_SECRETS = 'readSecrets'; const ACTION_READ_SECRETS = 'readSecrets';
export { export {
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS, ACTION_ADD_SECRETS,
ACTION_DELETE_SECRETS, ACTION_DELETE_SECRETS,
ACTION_UPDATE_SECRETS, ACTION_UPDATE_SECRETS,

View File

@@ -35,6 +35,8 @@ import {
import { SECRET_SHARED, SECRET_PERSONAL } from './secret'; import { SECRET_SHARED, SECRET_PERSONAL } from './secret';
import { EVENT_PUSH_SECRETS, EVENT_PULL_SECRETS } from './event'; import { EVENT_PUSH_SECRETS, EVENT_PULL_SECRETS } from './event';
import { import {
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS, ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS, ACTION_UPDATE_SECRETS,
ACTION_DELETE_SECRETS, ACTION_DELETE_SECRETS,
@@ -75,6 +77,8 @@ export {
INTEGRATION_FLYIO_API_URL, INTEGRATION_FLYIO_API_URL,
EVENT_PUSH_SECRETS, EVENT_PUSH_SECRETS,
EVENT_PULL_SECRETS, EVENT_PULL_SECRETS,
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS, ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS, ACTION_UPDATE_SECRETS,
ACTION_DELETE_SECRETS, ACTION_DELETE_SECRETS,