mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 13:27:46 +00:00
Update audit log service actor v3 filter, status toggle permissions, add JWT service token secret, for ST V3
This commit is contained in:
@@ -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 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 getJwtProviderAuthLifetime = async () => (await client.getSecret("JWT_PROVIDER_AUTH_LIFETIME")).secretValue || "15m";
|
||||||
export const getJwtSignupSecret = async () => (await client.getSecret("JWT_SIGNUP_SECRET")).secretValue;
|
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 getMongoURL = async () => (await client.getSecret("MONGO_URL")).secretValue;
|
||||||
export const getNodeEnv = async () => (await client.getSecret("NODE_ENV")).secretValue || "production";
|
export const getNodeEnv = async () => (await client.getSecret("NODE_ENV")).secretValue || "production";
|
||||||
export const getVerboseErrorOutput = async () => (await client.getSecret("VERBOSE_ERROR_OUTPUT")).secretValue === "true" && true;
|
export const getVerboseErrorOutput = async () => (await client.getSecret("VERBOSE_ERROR_OUTPUT")).secretValue === "true" && true;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { BadRequestError, ResourceNotFoundError } from "../../utils/errors";
|
import { BadRequestError, ResourceNotFoundError } from "../../utils/errors";
|
||||||
import { EEAuditLogService } from "../../ee/services";
|
import { EEAuditLogService } from "../../ee/services";
|
||||||
|
import { getJwtServiceTokenSecret } from "../../config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create service token data
|
* Create service token data
|
||||||
@@ -82,7 +83,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
_id: serviceTokenData._id.toString()
|
_id: serviceTokenData._id.toString()
|
||||||
},
|
},
|
||||||
expiresIn,
|
expiresIn,
|
||||||
secret: "hello" // TODO: replace with real secret
|
secret: await getJwtServiceTokenSecret()
|
||||||
});
|
});
|
||||||
|
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
Membership,
|
Membership,
|
||||||
Secret,
|
Secret,
|
||||||
ServiceTokenData,
|
ServiceTokenData,
|
||||||
|
ServiceTokenDataV3,
|
||||||
TFolderSchema,
|
TFolderSchema,
|
||||||
User,
|
User,
|
||||||
Workspace
|
Workspace
|
||||||
@@ -20,6 +21,7 @@ import {
|
|||||||
SecretSnapshot,
|
SecretSnapshot,
|
||||||
SecretVersion,
|
SecretVersion,
|
||||||
ServiceActor,
|
ServiceActor,
|
||||||
|
ServiceActorV3,
|
||||||
TFolderRootVersionSchema,
|
TFolderRootVersionSchema,
|
||||||
TrustedIP,
|
TrustedIP,
|
||||||
UserActor
|
UserActor
|
||||||
@@ -653,7 +655,7 @@ export const getWorkspaceAuditLogs = async (req: Request, res: Response) => {
|
|||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
ProjectPermissionSub.AuditLogs
|
ProjectPermissionSub.AuditLogs
|
||||||
);
|
);
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
...(eventType
|
...(eventType
|
||||||
@@ -668,13 +670,13 @@ export const getWorkspaceAuditLogs = async (req: Request, res: Response) => {
|
|||||||
: {}),
|
: {}),
|
||||||
...(actor
|
...(actor
|
||||||
? {
|
? {
|
||||||
"actor.type": actor.split("-", 2)[0],
|
"actor.type": actor.substring(0, actor.lastIndexOf("-")),
|
||||||
...(actor.split("-", 2)[0] === ActorType.USER
|
...(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
|
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({
|
return res.status(200).send({
|
||||||
actors: [...userActors, ...serviceActors]
|
actors
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
getJwtProviderAuthSecret,
|
getJwtProviderAuthSecret,
|
||||||
getJwtRefreshLifetime,
|
getJwtRefreshLifetime,
|
||||||
getJwtRefreshSecret,
|
getJwtRefreshSecret,
|
||||||
|
getJwtServiceTokenSecret
|
||||||
} from "../config";
|
} from "../config";
|
||||||
import {
|
import {
|
||||||
AuthMode
|
AuthMode
|
||||||
@@ -87,7 +88,7 @@ export const validateAuthMode = ({
|
|||||||
break;
|
break;
|
||||||
case "proj_token":
|
case "proj_token":
|
||||||
authMode = AuthMode.SERVICE_TOKEN_V3;
|
authMode = AuthMode.SERVICE_TOKEN_V3;
|
||||||
authTokenValue = parts.slice(1).join('.');
|
authTokenValue = parts.slice(1).join(".");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
authMode = AuthMode.JWT;
|
authMode = AuthMode.JWT;
|
||||||
@@ -239,21 +240,28 @@ export const getAuthSTDPayload = async ({
|
|||||||
authTokenValue: string;
|
authTokenValue: string;
|
||||||
}): Promise<ServiceTokenV3AuthData> => {
|
}): Promise<ServiceTokenV3AuthData> => {
|
||||||
const decodedToken = <jwt.UserIDJwtPayload>(
|
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) {
|
if (!serviceTokenData) {
|
||||||
throw UnauthorizedRequestError({
|
throw UnauthorizedRequestError({
|
||||||
message: "Failed to authenticate" // standardize auth error messages
|
message: "Failed to authenticate"
|
||||||
});
|
});
|
||||||
} 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
|
||||||
// TODO: test expired token
|
|
||||||
await ServiceTokenDataV3.findByIdAndUpdate(
|
await ServiceTokenDataV3.findByIdAndUpdate(
|
||||||
serviceTokenData._id,
|
serviceTokenData._id,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export enum ActorType {
|
export enum ActorType {
|
||||||
USER = "user",
|
USER = "user",
|
||||||
SERVICE = "service"
|
SERVICE = "service",
|
||||||
|
SERVICE_V3 = "service-v3"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum UserAgentType {
|
export enum UserAgentType {
|
||||||
|
|||||||
@@ -35,9 +35,15 @@ export interface ServiceActor {
|
|||||||
metadata: ServiceActorMetadata;
|
metadata: ServiceActorMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ServiceActorV3 {
|
||||||
|
type: ActorType.SERVICE_V3;
|
||||||
|
metadata: ServiceActorMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
export type Actor =
|
export type Actor =
|
||||||
| UserActor
|
| UserActor
|
||||||
| ServiceActor;
|
| ServiceActor
|
||||||
|
| ServiceActorV3;
|
||||||
|
|
||||||
interface GetSecretsEvent {
|
interface GetSecretsEvent {
|
||||||
type: EventType.GET_SECRETS;
|
type: EventType.GET_SECRETS;
|
||||||
|
|||||||
@@ -50,6 +50,12 @@ export const LogsFilter = ({
|
|||||||
{actor.metadata.name}
|
{actor.metadata.name}
|
||||||
</SelectItem>
|
</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:
|
default:
|
||||||
return (
|
return (
|
||||||
<SelectItem value="actor-none" key="actor-none">
|
<SelectItem value="actor-none" key="actor-none">
|
||||||
|
|||||||
@@ -29,6 +29,13 @@ export const LogsTableRow = ({
|
|||||||
<p>Service token</p>
|
<p>Service token</p>
|
||||||
</Td>
|
</Td>
|
||||||
);
|
);
|
||||||
|
case ActorType.SERVICE_V3:
|
||||||
|
return (
|
||||||
|
<Td>
|
||||||
|
<p>{`${actor.metadata.name}`}</p>
|
||||||
|
<p>Service token V3</p>
|
||||||
|
</Td>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Td />
|
<Td />
|
||||||
|
|||||||
+1
-1
@@ -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="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
<div className="flex justify-between mb-8">
|
<div className="flex justify-between mb-8">
|
||||||
<p className="text-xl font-semibold text-mineshaft-100">
|
<p className="text-xl font-semibold text-mineshaft-100">
|
||||||
(New) Service Tokens
|
Service Tokens V3 (Beta)
|
||||||
</p>
|
</p>
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
I={ProjectPermissionActions.Create}
|
I={ProjectPermissionActions.Create}
|
||||||
|
|||||||
+17
-9
@@ -114,16 +114,24 @@ export const ServiceTokenV3Table = ({
|
|||||||
<Tr className="h-10" key={`st-v3-${_id}`}>
|
<Tr className="h-10" key={`st-v3-${_id}`}>
|
||||||
<Td>{name}</Td>
|
<Td>{name}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<Switch
|
<ProjectPermissionCan
|
||||||
id={`enable-service-token-${_id}`}
|
I={ProjectPermissionActions.Edit}
|
||||||
onCheckedChange={(value) => handleToggleServiceTokenDataStatus({
|
a={ProjectPermissionSub.ServiceTokens}
|
||||||
serviceTokenDataId: _id,
|
|
||||||
isActive: value
|
|
||||||
})}
|
|
||||||
isChecked={isActive}
|
|
||||||
>
|
>
|
||||||
<p className="w-12 mr-4">{isActive ? "Active" : "Inactive"}</p>
|
{(isAllowed) => (
|
||||||
</Switch>
|
<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>
|
||||||
<Td>
|
<Td>
|
||||||
{scopes.map((scope) => {
|
{scopes.map((scope) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user