mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Add login/logout logs
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
};
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 });
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user