Update audit log service actor v3 filter, status toggle permissions, add JWT service token secret, for ST V3

This commit is contained in:
Tuan Dang
2023-09-26 12:03:00 +01:00
parent 4683dc7869
commit aa1e0b0f28
10 changed files with 85 additions and 27 deletions

View File

@@ -26,6 +26,7 @@ export const getJwtSignupLifetime = async () => (await client.getSecret("JWT_SIG
export const getJwtProviderAuthSecret = async () => (await client.getSecret("JWT_PROVIDER_AUTH_SECRET")).secretValue;
export const getJwtProviderAuthLifetime = async () => (await client.getSecret("JWT_PROVIDER_AUTH_LIFETIME")).secretValue || "15m";
export const getJwtSignupSecret = async () => (await client.getSecret("JWT_SIGNUP_SECRET")).secretValue;
export const getJwtServiceTokenSecret = async () => (await client.getSecret("JWT_SERVICE_TOKEN_SECRET")).secretValue;
export const getMongoURL = async () => (await client.getSecret("MONGO_URL")).secretValue;
export const getNodeEnv = async () => (await client.getSecret("NODE_ENV")).secretValue || "production";
export const getVerboseErrorOutput = async () => (await client.getSecret("VERBOSE_ERROR_OUTPUT")).secretValue === "true" && true;

View File

@@ -22,6 +22,7 @@ import {
import { ForbiddenError } from "@casl/ability";
import { BadRequestError, ResourceNotFoundError } from "../../utils/errors";
import { EEAuditLogService } from "../../ee/services";
import { getJwtServiceTokenSecret } from "../../config";
/**
* Create service token data
@@ -82,7 +83,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
_id: serviceTokenData._id.toString()
},
expiresIn,
secret: "hello" // TODO: replace with real secret
secret: await getJwtServiceTokenSecret()
});
await EEAuditLogService.createAuditLog(

View File

@@ -5,6 +5,7 @@ import {
Membership,
Secret,
ServiceTokenData,
ServiceTokenDataV3,
TFolderSchema,
User,
Workspace
@@ -20,6 +21,7 @@ import {
SecretSnapshot,
SecretVersion,
ServiceActor,
ServiceActorV3,
TFolderRootVersionSchema,
TrustedIP,
UserActor
@@ -653,7 +655,7 @@ export const getWorkspaceAuditLogs = async (req: Request, res: Response) => {
ProjectPermissionActions.Read,
ProjectPermissionSub.AuditLogs
);
const query = {
workspace: new Types.ObjectId(workspaceId),
...(eventType
@@ -668,13 +670,13 @@ export const getWorkspaceAuditLogs = async (req: Request, res: Response) => {
: {}),
...(actor
? {
"actor.type": actor.split("-", 2)[0],
"actor.type": actor.substring(0, actor.lastIndexOf("-")),
...(actor.split("-", 2)[0] === ActorType.USER
? {
"actor.metadata.userId": actor.split("-", 2)[1]
"actor.metadata.userId": actor.substring(actor.lastIndexOf("-") + 1)
}
: {
"actor.metadata.serviceId": actor.split("-", 2)[1]
"actor.metadata.serviceId": actor.substring(actor.lastIndexOf("-") + 1)
})
}
: {}),
@@ -742,9 +744,27 @@ export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Res
name: serviceTokenData.name
}
}));
const serviceV3Actors: ServiceActorV3[] = (
await ServiceTokenDataV3.find({
workspace: new Types.ObjectId(workspaceId)
})
).map((serviceTokenData) => ({
type: ActorType.SERVICE_V3,
metadata: {
serviceId: serviceTokenData._id.toString(),
name: serviceTokenData.name
}
}));
const actors = [
...userActors,
...serviceActors,
...serviceV3Actors
];
return res.status(200).send({
actors: [...userActors, ...serviceActors]
actors
});
};

View File

@@ -24,6 +24,7 @@ import {
getJwtProviderAuthSecret,
getJwtRefreshLifetime,
getJwtRefreshSecret,
getJwtServiceTokenSecret
} from "../config";
import {
AuthMode
@@ -87,7 +88,7 @@ export const validateAuthMode = ({
break;
case "proj_token":
authMode = AuthMode.SERVICE_TOKEN_V3;
authTokenValue = parts.slice(1).join('.');
authTokenValue = parts.slice(1).join(".");
break;
default:
authMode = AuthMode.JWT;
@@ -239,21 +240,28 @@ export const getAuthSTDPayload = async ({
authTokenValue: string;
}): Promise<ServiceTokenV3AuthData> => {
const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(authTokenValue, "hello") // TODO: replace with real secret
jwt.verify(authTokenValue, await getJwtServiceTokenSecret())
);
const serviceTokenData = await ServiceTokenDataV3.findOneAndUpdate(
{
_id: new Types.ObjectId(decodedToken._id),
isActive: true
},
{
lastUsed: new Date()
},
{
new: true
}
);
const serviceTokenData = await ServiceTokenDataV3.findOne({
_id: new Types.ObjectId(decodedToken._id),
isActive: true
});
if (!serviceTokenData) {
throw UnauthorizedRequestError({
message: "Failed to authenticate" // standardize auth error messages
message: "Failed to authenticate"
});
} else if (serviceTokenData?.expiresAt && new Date(serviceTokenData.expiresAt) < new Date()) {
// case: service token expired
// TODO: test expired token
await ServiceTokenDataV3.findByIdAndUpdate(
serviceTokenData._id,
{

View File

@@ -1,6 +1,7 @@
export enum ActorType {
USER = "user",
SERVICE = "service"
SERVICE = "service",
SERVICE_V3 = "service-v3"
}
export enum UserAgentType {

View File

@@ -35,9 +35,15 @@ export interface ServiceActor {
metadata: ServiceActorMetadata;
}
export interface ServiceActorV3 {
type: ActorType.SERVICE_V3;
metadata: ServiceActorMetadata;
}
export type Actor =
| UserActor
| ServiceActor;
| ServiceActor
| ServiceActorV3;
interface GetSecretsEvent {
type: EventType.GET_SECRETS;

View File

@@ -50,6 +50,12 @@ export const LogsFilter = ({
{actor.metadata.name}
</SelectItem>
);
case ActorType.SERVICE_V3:
return (
<SelectItem value={`${actor.type}-${actor.metadata.serviceId}`} key={`service-actor-v3-filter-${actor.metadata.serviceId}`}>
{actor.metadata.name}
</SelectItem>
);
default:
return (
<SelectItem value="actor-none" key="actor-none">

View File

@@ -29,6 +29,13 @@ export const LogsTableRow = ({
<p>Service token</p>
</Td>
);
case ActorType.SERVICE_V3:
return (
<Td>
<p>{`${actor.metadata.name}`}</p>
<p>Service token V3</p>
</Td>
);
default:
return (
<Td />

View File

@@ -50,7 +50,7 @@ export const ServiceTokenV3Section = withProjectPermission(
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="flex justify-between mb-8">
<p className="text-xl font-semibold text-mineshaft-100">
(New) Service Tokens
Service Tokens V3 (Beta)
</p>
<ProjectPermissionCan
I={ProjectPermissionActions.Create}

View File

@@ -114,16 +114,24 @@ export const ServiceTokenV3Table = ({
<Tr className="h-10" key={`st-v3-${_id}`}>
<Td>{name}</Td>
<Td>
<Switch
id={`enable-service-token-${_id}`}
onCheckedChange={(value) => handleToggleServiceTokenDataStatus({
serviceTokenDataId: _id,
isActive: value
})}
isChecked={isActive}
<ProjectPermissionCan
I={ProjectPermissionActions.Edit}
a={ProjectPermissionSub.ServiceTokens}
>
<p className="w-12 mr-4">{isActive ? "Active" : "Inactive"}</p>
</Switch>
{(isAllowed) => (
<Switch
id={`enable-service-token-${_id}`}
onCheckedChange={(value) => handleToggleServiceTokenDataStatus({
serviceTokenDataId: _id,
isActive: value
})}
isChecked={isActive}
isDisabled={!isAllowed}
>
<p className="w-12 mr-4">{isActive ? "Active" : "Inactive"}</p>
</Switch>
)}
</ProjectPermissionCan>
</Td>
<Td>
{scopes.map((scope) => {