mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 14:27:30 +00:00
Patch service account UI in audit logs, add lastUsed for API keys and service accounts/tokens
This commit is contained in:
@@ -50,6 +50,7 @@ export const createAPIKeyData = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
apiKeyData = await new APIKeyData({
|
apiKeyData = await new APIKeyData({
|
||||||
name,
|
name,
|
||||||
|
lastUsed: new Date(),
|
||||||
expiresAt,
|
expiresAt,
|
||||||
user: req.user._id,
|
user: req.user._id,
|
||||||
secretHash
|
secretHash
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ export const createServiceAccount = async (req: Request, res: Response) => {
|
|||||||
organization: new Types.ObjectId(organizationId),
|
organization: new Types.ObjectId(organizationId),
|
||||||
user: req.user,
|
user: req.user,
|
||||||
publicKey,
|
publicKey,
|
||||||
|
lastUsed: new Date(),
|
||||||
expiresAt,
|
expiresAt,
|
||||||
secretHash
|
secretHash
|
||||||
}).save();
|
}).save();
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
environment,
|
environment,
|
||||||
user,
|
user,
|
||||||
serviceAccount,
|
serviceAccount,
|
||||||
|
lastUsed: new Date(),
|
||||||
expiresAt,
|
expiresAt,
|
||||||
secretHash,
|
secretHash,
|
||||||
encryptedKey,
|
encryptedKey,
|
||||||
@@ -102,7 +103,6 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
permissions
|
permissions
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
|
|
||||||
// return service token data without sensitive data
|
// return service token data without sensitive data
|
||||||
serviceTokenData = await ServiceTokenData.findById(serviceTokenData._id);
|
serviceTokenData = await ServiceTokenData.findById(serviceTokenData._id);
|
||||||
|
|
||||||
@@ -123,25 +123,11 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const deleteServiceTokenData = async (req: Request, res: Response) => {
|
export const deleteServiceTokenData = async (req: Request, res: Response) => {
|
||||||
let serviceTokenData;
|
const { serviceTokenDataId } = req.params;
|
||||||
try {
|
|
||||||
const { serviceTokenDataId } = req.params;
|
|
||||||
|
|
||||||
serviceTokenData = await ServiceTokenData.findByIdAndDelete(serviceTokenDataId);
|
const serviceTokenData = await ServiceTokenData.findByIdAndDelete(serviceTokenDataId);
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
Sentry.setUser({ email: req.user.email });
|
|
||||||
Sentry.captureException(err);
|
|
||||||
return res.status(400).send({
|
|
||||||
message: 'Failed to delete service token data'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceTokenData
|
serviceTokenData
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
function UnauthorizedRequestError(arg0: { message: string; }) {
|
|
||||||
throw new Error('Function not implemented.');
|
|
||||||
}
|
}
|
||||||
@@ -418,7 +418,7 @@ export const getWorkspaceLogs = async (req: Request, res: Response) => {
|
|||||||
.skip(offset)
|
.skip(offset)
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
.populate('actions')
|
.populate('actions')
|
||||||
.populate('user');
|
.populate('user serviceAccount serviceTokenData');
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser({ email: req.user.email });
|
Sentry.setUser({ email: req.user.email });
|
||||||
|
|||||||
+97
-115
@@ -1,4 +1,5 @@
|
|||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from '@sentry/node';
|
||||||
|
import { Types } from 'mongoose';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import bcrypt from 'bcrypt';
|
import bcrypt from 'bcrypt';
|
||||||
import {
|
import {
|
||||||
@@ -102,25 +103,17 @@ const getAuthUserPayload = async ({
|
|||||||
}: {
|
}: {
|
||||||
authTokenValue: string;
|
authTokenValue: string;
|
||||||
}) => {
|
}) => {
|
||||||
let user;
|
const decodedToken = <jwt.UserIDJwtPayload>(
|
||||||
try {
|
jwt.verify(authTokenValue, getJwtAuthSecret())
|
||||||
const decodedToken = <jwt.UserIDJwtPayload>(
|
);
|
||||||
jwt.verify(authTokenValue, getJwtAuthSecret())
|
|
||||||
);
|
|
||||||
|
|
||||||
user = await User.findOne({
|
const user = await User.findOne({
|
||||||
_id: decodedToken.userId
|
_id: decodedToken.userId
|
||||||
}).select('+publicKey');
|
}).select('+publicKey');
|
||||||
|
|
||||||
if (!user) throw AccountNotFoundError({ message: 'Failed to find User' });
|
if (!user) throw AccountNotFoundError({ message: 'Failed to find User' });
|
||||||
|
|
||||||
if (!user?.publicKey) throw UnauthorizedRequestError({ message: 'Failed to authenticate User with partially set up account' });
|
if (!user?.publicKey) throw UnauthorizedRequestError({ message: 'Failed to authenticate User with partially set up account' });
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
throw UnauthorizedRequestError({
|
|
||||||
message: 'Failed to authenticate JWT token'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
@@ -136,41 +129,38 @@ const getAuthSTDPayload = async ({
|
|||||||
}: {
|
}: {
|
||||||
authTokenValue: string;
|
authTokenValue: string;
|
||||||
}) => {
|
}) => {
|
||||||
let serviceTokenData;
|
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
|
||||||
try {
|
|
||||||
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
|
|
||||||
|
|
||||||
// TODO: optimize double query
|
let serviceTokenData = await ServiceTokenData
|
||||||
serviceTokenData = await ServiceTokenData
|
.findById(TOKEN_IDENTIFIER, '+secretHash +expiresAt');
|
||||||
.findById(TOKEN_IDENTIFIER, '+secretHash +expiresAt');
|
|
||||||
|
|
||||||
if (!serviceTokenData) {
|
if (!serviceTokenData) {
|
||||||
throw ServiceTokenDataNotFoundError({ message: 'Failed to find service token data' });
|
throw ServiceTokenDataNotFoundError({ message: 'Failed to find service token data' });
|
||||||
} else if (serviceTokenData?.expiresAt && new Date(serviceTokenData.expiresAt) < new Date()) {
|
} else if (serviceTokenData?.expiresAt && new Date(serviceTokenData.expiresAt) < new Date()) {
|
||||||
// case: service token expired
|
// case: service token expired
|
||||||
await ServiceTokenData.findByIdAndDelete(serviceTokenData._id);
|
await ServiceTokenData.findByIdAndDelete(serviceTokenData._id);
|
||||||
throw UnauthorizedRequestError({
|
|
||||||
message: 'Failed to authenticate expired service token'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const isMatch = await bcrypt.compare(TOKEN_SECRET, serviceTokenData.secretHash);
|
|
||||||
if (!isMatch) throw UnauthorizedRequestError({
|
|
||||||
message: 'Failed to authenticate service token'
|
|
||||||
});
|
|
||||||
|
|
||||||
serviceTokenData = await ServiceTokenData
|
|
||||||
.findById(TOKEN_IDENTIFIER)
|
|
||||||
.select('+encryptedKey +iv +tag');
|
|
||||||
|
|
||||||
if (!serviceTokenData) throw ServiceTokenDataNotFoundError({ message: 'Failed to find service token data' });
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
throw UnauthorizedRequestError({
|
throw UnauthorizedRequestError({
|
||||||
message: 'Failed to authenticate service token'
|
message: 'Failed to authenticate expired service token'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isMatch = await bcrypt.compare(TOKEN_SECRET, serviceTokenData.secretHash);
|
||||||
|
if (!isMatch) throw UnauthorizedRequestError({
|
||||||
|
message: 'Failed to authenticate service token'
|
||||||
|
});
|
||||||
|
|
||||||
|
serviceTokenData = await ServiceTokenData
|
||||||
|
.findOneAndUpdate({
|
||||||
|
_id: new Types.ObjectId(TOKEN_IDENTIFIER)
|
||||||
|
}, {
|
||||||
|
lastUsed: new Date()
|
||||||
|
}, {
|
||||||
|
new: true
|
||||||
|
})
|
||||||
|
.select('+encryptedKey +iv +tag');
|
||||||
|
|
||||||
|
if (!serviceTokenData) throw ServiceTokenDataNotFoundError({ message: 'Failed to find service token data' });
|
||||||
|
|
||||||
return serviceTokenData;
|
return serviceTokenData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +194,6 @@ const getAuthSAAKPayload = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: deprecate API keys
|
|
||||||
* Return API key data payload corresponding to API key [authTokenValue]
|
* Return API key data payload corresponding to API key [authTokenValue]
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
* @param {String} obj.authTokenValue - API key value
|
* @param {String} obj.authTokenValue - API key value
|
||||||
@@ -215,33 +204,44 @@ const getAuthAPIKeyPayload = async ({
|
|||||||
}: {
|
}: {
|
||||||
authTokenValue: string;
|
authTokenValue: string;
|
||||||
}) => {
|
}) => {
|
||||||
let user;
|
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
|
||||||
try {
|
|
||||||
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
|
|
||||||
|
|
||||||
const apiKeyData = await APIKeyData
|
let apiKeyData = await APIKeyData
|
||||||
.findById(TOKEN_IDENTIFIER, '+secretHash +expiresAt')
|
.findById(TOKEN_IDENTIFIER, '+secretHash +expiresAt')
|
||||||
.populate<{user: IUser}>('user', '+publicKey');
|
.populate<{user: IUser}>('user', '+publicKey');
|
||||||
|
|
||||||
if (!apiKeyData) {
|
if (!apiKeyData) {
|
||||||
throw APIKeyDataNotFoundError({ message: 'Failed to find API key data' });
|
throw APIKeyDataNotFoundError({ message: 'Failed to find API key data' });
|
||||||
} else if (apiKeyData?.expiresAt && new Date(apiKeyData.expiresAt) < new Date()) {
|
} else if (apiKeyData?.expiresAt && new Date(apiKeyData.expiresAt) < new Date()) {
|
||||||
// case: API key expired
|
// case: API key expired
|
||||||
await APIKeyData.findByIdAndDelete(apiKeyData._id);
|
await APIKeyData.findByIdAndDelete(apiKeyData._id);
|
||||||
throw UnauthorizedRequestError({
|
|
||||||
message: 'Failed to authenticate expired API key'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const isMatch = await bcrypt.compare(TOKEN_SECRET, apiKeyData.secretHash);
|
|
||||||
if (!isMatch) throw UnauthorizedRequestError({
|
|
||||||
message: 'Failed to authenticate API key'
|
|
||||||
});
|
|
||||||
|
|
||||||
user = apiKeyData.user;
|
|
||||||
} catch (err) {
|
|
||||||
throw UnauthorizedRequestError({
|
throw UnauthorizedRequestError({
|
||||||
message: 'Failed to authenticate API key'
|
message: 'Failed to authenticate expired API key'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const isMatch = await bcrypt.compare(TOKEN_SECRET, apiKeyData.secretHash);
|
||||||
|
if (!isMatch) throw UnauthorizedRequestError({
|
||||||
|
message: 'Failed to authenticate API key'
|
||||||
|
});
|
||||||
|
|
||||||
|
apiKeyData = await APIKeyData.findOneAndUpdate({
|
||||||
|
_id: new Types.ObjectId(TOKEN_IDENTIFIER)
|
||||||
|
}, {
|
||||||
|
lastUsed: new Date()
|
||||||
|
}, {
|
||||||
|
new: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!apiKeyData) {
|
||||||
|
throw APIKeyDataNotFoundError({ message: 'Failed to find API key data' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await User.findById(apiKeyData.user).select('+publicKey');
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw AccountNotFoundError({
|
||||||
|
message: 'Failed to find user'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,30 +257,23 @@ const getAuthAPIKeyPayload = async ({
|
|||||||
* @return {String} obj.refreshToken - issued refresh token
|
* @return {String} obj.refreshToken - issued refresh token
|
||||||
*/
|
*/
|
||||||
const issueAuthTokens = async ({ userId }: { userId: string }) => {
|
const issueAuthTokens = async ({ userId }: { userId: string }) => {
|
||||||
let token: string;
|
|
||||||
let refreshToken: string;
|
|
||||||
try {
|
|
||||||
// issue tokens
|
|
||||||
token = createToken({
|
|
||||||
payload: {
|
|
||||||
userId
|
|
||||||
},
|
|
||||||
expiresIn: getJwtAuthLifetime(),
|
|
||||||
secret: getJwtAuthSecret()
|
|
||||||
});
|
|
||||||
|
|
||||||
refreshToken = createToken({
|
// issue tokens
|
||||||
payload: {
|
const token = createToken({
|
||||||
userId
|
payload: {
|
||||||
},
|
userId
|
||||||
expiresIn: getJwtRefreshLifetime(),
|
},
|
||||||
secret: getJwtRefreshSecret()
|
expiresIn: getJwtAuthLifetime(),
|
||||||
});
|
secret: getJwtAuthSecret()
|
||||||
} catch (err) {
|
});
|
||||||
Sentry.setUser(null);
|
|
||||||
Sentry.captureException(err);
|
const refreshToken = createToken({
|
||||||
throw new Error('Failed to issue tokens');
|
payload: {
|
||||||
}
|
userId
|
||||||
|
},
|
||||||
|
expiresIn: getJwtRefreshLifetime(),
|
||||||
|
secret: getJwtRefreshSecret()
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token,
|
token,
|
||||||
@@ -294,19 +287,14 @@ const issueAuthTokens = async ({ userId }: { userId: string }) => {
|
|||||||
* @param {String} obj.userId - id of user whose tokens are cleared.
|
* @param {String} obj.userId - id of user whose tokens are cleared.
|
||||||
*/
|
*/
|
||||||
const clearTokens = async ({ userId }: { userId: string }): Promise<void> => {
|
const clearTokens = async ({ userId }: { userId: string }): Promise<void> => {
|
||||||
try {
|
// increment refreshVersion on user by 1
|
||||||
// increment refreshVersion on user by 1
|
User.findOneAndUpdate({
|
||||||
User.findOneAndUpdate({
|
_id: userId
|
||||||
_id: userId
|
}, {
|
||||||
}, {
|
$inc: {
|
||||||
$inc: {
|
refreshVersion: 1
|
||||||
refreshVersion: 1
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
Sentry.setUser(null);
|
|
||||||
Sentry.captureException(err);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -326,15 +314,9 @@ const createToken = ({
|
|||||||
expiresIn: string | number;
|
expiresIn: string | number;
|
||||||
secret: string;
|
secret: string;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
return jwt.sign(payload, secret, {
|
||||||
return jwt.sign(payload, secret, {
|
expiresIn
|
||||||
expiresIn
|
});
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
Sentry.setUser(null);
|
|
||||||
Sentry.captureException(err);
|
|
||||||
throw new Error('Failed to create a token');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Schema, model, Types } from 'mongoose';
|
|||||||
export interface IAPIKeyData {
|
export interface IAPIKeyData {
|
||||||
name: string;
|
name: string;
|
||||||
user: Types.ObjectId;
|
user: Types.ObjectId;
|
||||||
|
lastUsed: Date;
|
||||||
expiresAt: Date;
|
expiresAt: Date;
|
||||||
secretHash: string;
|
secretHash: string;
|
||||||
}
|
}
|
||||||
@@ -18,6 +19,9 @@ const apiKeyDataSchema = new Schema<IAPIKeyData>(
|
|||||||
ref: 'User',
|
ref: 'User',
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
lastUsed: {
|
||||||
|
type: Date
|
||||||
|
},
|
||||||
expiresAt: {
|
expiresAt: {
|
||||||
type: Date
|
type: Date
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export interface IServiceAccount extends Document {
|
|||||||
organization: Types.ObjectId;
|
organization: Types.ObjectId;
|
||||||
user: Types.ObjectId;
|
user: Types.ObjectId;
|
||||||
publicKey: string;
|
publicKey: string;
|
||||||
|
lastUsed: Date;
|
||||||
expiresAt: Date;
|
expiresAt: Date;
|
||||||
secretHash: string;
|
secretHash: string;
|
||||||
}
|
}
|
||||||
@@ -30,6 +31,9 @@ const serviceAccountSchema = new Schema<IServiceAccount>(
|
|||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
lastUsed: {
|
||||||
|
type: Date
|
||||||
|
},
|
||||||
expiresAt: {
|
expiresAt: {
|
||||||
type: Date
|
type: Date
|
||||||
},
|
},
|
||||||
@@ -44,6 +48,6 @@ const serviceAccountSchema = new Schema<IServiceAccount>(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const ServiceAccount = model<IServiceAccount>('ServiceAcount', serviceAccountSchema);
|
const ServiceAccount = model<IServiceAccount>('ServiceAccount', serviceAccountSchema);
|
||||||
|
|
||||||
export default ServiceAccount;
|
export default ServiceAccount;
|
||||||
@@ -7,6 +7,7 @@ export interface IServiceTokenData extends Document {
|
|||||||
environment: string;
|
environment: string;
|
||||||
user: Types.ObjectId;
|
user: Types.ObjectId;
|
||||||
serviceAccount: Types.ObjectId;
|
serviceAccount: Types.ObjectId;
|
||||||
|
lastUsed: Date;
|
||||||
expiresAt: Date;
|
expiresAt: Date;
|
||||||
secretHash: string;
|
secretHash: string;
|
||||||
encryptedKey: string;
|
encryptedKey: string;
|
||||||
@@ -26,7 +27,7 @@ const serviceTokenDataSchema = new Schema<IServiceTokenData>(
|
|||||||
ref: 'Workspace',
|
ref: 'Workspace',
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
environment: { // TODO: adapt to upcoming environment id
|
environment: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
@@ -38,6 +39,9 @@ const serviceTokenDataSchema = new Schema<IServiceTokenData>(
|
|||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'ServiceAccount'
|
ref: 'ServiceAccount'
|
||||||
},
|
},
|
||||||
|
lastUsed: {
|
||||||
|
type: Date
|
||||||
|
},
|
||||||
expiresAt: {
|
expiresAt: {
|
||||||
type: Date
|
type: Date
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,6 +21,12 @@ interface LogData {
|
|||||||
createdAt: string;
|
createdAt: string;
|
||||||
ipAddress: string;
|
ipAddress: string;
|
||||||
user: string;
|
user: string;
|
||||||
|
serviceAccount: {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
serviceTokenData: {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
payload: PayloadProps[];
|
payload: PayloadProps[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +47,14 @@ const ActivityLogsRow = ({
|
|||||||
const [payloadOpened, setPayloadOpened] = useState(false);
|
const [payloadOpened, setPayloadOpened] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const renderUser = () => {
|
||||||
|
if (row?.user) return `User: ${row.user}`;
|
||||||
|
if (row?.serviceAccount) return `Service Account: ${row.serviceAccount.name}`;
|
||||||
|
if (row?.serviceTokenData.name) return `Service Token: ${row.serviceTokenData.name}`;
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<tr key={guidGenerator()} className="bg-bunker-800 duration-100 w-full text-sm">
|
<tr key={guidGenerator()} className="bg-bunker-800 duration-100 w-full text-sm">
|
||||||
@@ -64,7 +78,7 @@ const ActivityLogsRow = ({
|
|||||||
)
|
)
|
||||||
.join(' and ')}
|
.join(' and ')}
|
||||||
</td>
|
</td>
|
||||||
<td className="pl-6 py-3 border-mineshaft-700 border-t text-gray-300">{row.user}</td>
|
<td className="pl-6 py-3 border-mineshaft-700 border-t text-gray-300">{renderUser()}</td>
|
||||||
<td className="pl-6 py-3 border-mineshaft-700 border-t text-gray-300">{row.channel}</td>
|
<td className="pl-6 py-3 border-mineshaft-700 border-t text-gray-300">{row.channel}</td>
|
||||||
<td className="pl-6 py-3 border-mineshaft-700 border-t text-gray-300">
|
<td className="pl-6 py-3 border-mineshaft-700 border-t text-gray-300">
|
||||||
{timeSince(new Date(row.createdAt))}
|
{timeSince(new Date(row.createdAt))}
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ interface LogData {
|
|||||||
user: {
|
user: {
|
||||||
email: string;
|
email: string;
|
||||||
};
|
};
|
||||||
|
serviceAccount?: {
|
||||||
|
string: string;
|
||||||
|
},
|
||||||
|
serviceTokenData?: {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
actions: {
|
actions: {
|
||||||
_id: string;
|
_id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -69,13 +75,16 @@ export default function Activity() {
|
|||||||
userId: '',
|
userId: '',
|
||||||
actionNames: eventChosen
|
actionNames: eventChosen
|
||||||
});
|
});
|
||||||
|
|
||||||
setLogsData(
|
setLogsData(
|
||||||
tempLogsData.map((log: LogData) => ({
|
tempLogsData.map((log: LogData) => ({
|
||||||
_id: log._id,
|
_id: log._id,
|
||||||
channel: log.channel,
|
channel: log.channel,
|
||||||
createdAt: log.createdAt,
|
createdAt: log.createdAt,
|
||||||
ipAddress: log.ipAddress,
|
ipAddress: log.ipAddress,
|
||||||
user: log.user.email,
|
user: log?.user?.email,
|
||||||
|
serviceAccount: log?.serviceAccount,
|
||||||
|
serviceTokenData: log?.serviceTokenData,
|
||||||
payload: log.actions.map((action) => ({
|
payload: log.actions.map((action) => ({
|
||||||
_id: action._id,
|
_id: action._id,
|
||||||
name: action.name,
|
name: action.name,
|
||||||
@@ -106,7 +115,9 @@ export default function Activity() {
|
|||||||
channel: log.channel,
|
channel: log.channel,
|
||||||
createdAt: log.createdAt,
|
createdAt: log.createdAt,
|
||||||
ipAddress: log.ipAddress,
|
ipAddress: log.ipAddress,
|
||||||
user: log.user.email,
|
user: log?.user?.email,
|
||||||
|
serviceAccount: log?.serviceAccount,
|
||||||
|
serviceTokenData: log?.serviceTokenData,
|
||||||
payload: log.actions.map((action) => ({
|
payload: log.actions.map((action) => ({
|
||||||
_id: action._id,
|
_id: action._id,
|
||||||
name: action.name,
|
name: action.name,
|
||||||
|
|||||||
Reference in New Issue
Block a user