mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 15:27:46 +00:00
Add paywall to ST V3 ip allowlisting
This commit is contained in:
@@ -2,12 +2,10 @@ import * as secretsController from "./secretsController";
|
|||||||
import * as workspacesController from "./workspacesController";
|
import * as workspacesController from "./workspacesController";
|
||||||
import * as authController from "./authController";
|
import * as authController from "./authController";
|
||||||
import * as signupController from "./signupController";
|
import * as signupController from "./signupController";
|
||||||
import * as serviceTokenDataController from "./serviceTokenDataController";
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
authController,
|
authController,
|
||||||
secretsController,
|
secretsController,
|
||||||
signupController,
|
signupController,
|
||||||
workspacesController,
|
workspacesController
|
||||||
serviceTokenDataController
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import * as serviceTokenDataController from "./serviceTokenDataController";
|
||||||
|
|
||||||
|
export {
|
||||||
|
serviceTokenDataController
|
||||||
|
}
|
||||||
+31
-13
@@ -4,29 +4,30 @@ import {
|
|||||||
IServiceTokenDataV3,
|
IServiceTokenDataV3,
|
||||||
IUser,
|
IUser,
|
||||||
ServiceTokenDataV3,
|
ServiceTokenDataV3,
|
||||||
ServiceTokenDataV3Key
|
ServiceTokenDataV3Key,
|
||||||
} from "../../models";
|
Workspace
|
||||||
|
} from "../../../models";
|
||||||
import {
|
import {
|
||||||
IServiceTokenV3Scope,
|
IServiceTokenV3Scope,
|
||||||
IServiceTokenV3TrustedIp
|
IServiceTokenV3TrustedIp
|
||||||
} from "../../models/serviceTokenDataV3";
|
} from "../../../models/serviceTokenDataV3";
|
||||||
import {
|
import {
|
||||||
ActorType,
|
ActorType,
|
||||||
EventType
|
EventType
|
||||||
} from "../../ee/models";
|
} from "../../models";
|
||||||
import { validateRequest } from "../../helpers/validation";
|
import { validateRequest } from "../../../helpers/validation";
|
||||||
import * as reqValidator from "../../validation/serviceTokenDataV3";
|
import * as reqValidator from "../../../validation/serviceTokenDataV3";
|
||||||
import { createToken } from "../../helpers/auth";
|
import { createToken } from "../../../helpers/auth";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionActions,
|
ProjectPermissionActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
getUserProjectPermissions
|
getUserProjectPermissions
|
||||||
} from "../../ee/services/ProjectRoleService";
|
} from "../../services/ProjectRoleService";
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { BadRequestError, ResourceNotFoundError } from "../../utils/errors";
|
import { BadRequestError, ResourceNotFoundError } from "../../../utils/errors";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "../../utils/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "../../../utils/ip";
|
||||||
import { EEAuditLogService } from "../../ee/services";
|
import { EEAuditLogService, EELicenseService } from "../../services";
|
||||||
import { getJwtServiceTokenSecret } from "../../config";
|
import { getJwtServiceTokenSecret } from "../../../config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return project key for service token
|
* Return project key for service token
|
||||||
@@ -80,8 +81,17 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
ProjectPermissionSub.ServiceTokens
|
ProjectPermissionSub.ServiceTokens
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const workspace = await Workspace.findById(workspaceId);
|
||||||
|
if (!workspace) throw BadRequestError({ message: "Workspace not found" });
|
||||||
|
|
||||||
|
const plan = await EELicenseService.getPlan(workspace.organization);
|
||||||
|
|
||||||
// validate trusted ips
|
// validate trusted ips
|
||||||
const reformattedTrustedIps = trustedIps.map((trustedIp) => {
|
const reformattedTrustedIps = trustedIps.map((trustedIp) => {
|
||||||
|
if (!plan.ipAllowlisting && trustedIp.ipAddress !== "0.0.0.0/0") return res.status(400).send({
|
||||||
|
message: "Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range."
|
||||||
|
});
|
||||||
|
|
||||||
const isValidIPOrCidr = isValidIpOrCidr(trustedIp.ipAddress);
|
const isValidIPOrCidr = isValidIpOrCidr(trustedIp.ipAddress);
|
||||||
|
|
||||||
if (!isValidIPOrCidr) return res.status(400).send({
|
if (!isValidIPOrCidr) return res.status(400).send({
|
||||||
@@ -173,7 +183,6 @@ export const updateServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
} = await validateRequest(reqValidator.UpdateServiceTokenV3, req);
|
} = await validateRequest(reqValidator.UpdateServiceTokenV3, req);
|
||||||
|
|
||||||
let serviceTokenData = await ServiceTokenDataV3.findById(serviceTokenDataId);
|
let serviceTokenData = await ServiceTokenDataV3.findById(serviceTokenDataId);
|
||||||
|
|
||||||
if (!serviceTokenData) throw ResourceNotFoundError({
|
if (!serviceTokenData) throw ResourceNotFoundError({
|
||||||
message: "Service token not found"
|
message: "Service token not found"
|
||||||
});
|
});
|
||||||
@@ -188,10 +197,19 @@ export const updateServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
ProjectPermissionSub.ServiceTokens
|
ProjectPermissionSub.ServiceTokens
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const workspace = await Workspace.findById(serviceTokenData.workspace);
|
||||||
|
if (!workspace) throw BadRequestError({ message: "Workspace not found" });
|
||||||
|
|
||||||
|
const plan = await EELicenseService.getPlan(workspace.organization);
|
||||||
|
|
||||||
// validate trusted ips
|
// validate trusted ips
|
||||||
let reformattedTrustedIps;
|
let reformattedTrustedIps;
|
||||||
if (trustedIps) {
|
if (trustedIps) {
|
||||||
reformattedTrustedIps = trustedIps.map((trustedIp) => {
|
reformattedTrustedIps = trustedIps.map((trustedIp) => {
|
||||||
|
if (!plan.ipAllowlisting && trustedIp.ipAddress !== "0.0.0.0/0") return res.status(400).send({
|
||||||
|
message: "Failed to update IP access range to service token due to plan restriction. Upgrade plan to update IP access range."
|
||||||
|
});
|
||||||
|
|
||||||
const isValidIPOrCidr = isValidIpOrCidr(trustedIp.ipAddress);
|
const isValidIPOrCidr = isValidIpOrCidr(trustedIp.ipAddress);
|
||||||
|
|
||||||
if (!isValidIPOrCidr) return res.status(400).send({
|
if (!isValidIPOrCidr) return res.status(400).send({
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import serviceTokenData from "./serviceTokenData";
|
||||||
|
|
||||||
|
export {
|
||||||
|
serviceTokenData
|
||||||
|
}
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { requireAuth } from "../../middleware";
|
import { requireAuth } from "../../../middleware";
|
||||||
import { AuthMode } from "../../variables";
|
import { AuthMode } from "../../../variables";
|
||||||
import { serviceTokenDataController } from "../../controllers/v3";
|
import { serviceTokenDataController } from "../../controllers/v3";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
@@ -27,6 +27,9 @@ import {
|
|||||||
roles as v1RoleRouter,
|
roles as v1RoleRouter,
|
||||||
secretScanning as v1SecretScanningRouter
|
secretScanning as v1SecretScanningRouter
|
||||||
} from "./ee/routes/v1";
|
} from "./ee/routes/v1";
|
||||||
|
import {
|
||||||
|
serviceTokenData as v3ServiceTokenDataRouter
|
||||||
|
} from "./ee/routes/v3";
|
||||||
import {
|
import {
|
||||||
auth as v1AuthRouter,
|
auth as v1AuthRouter,
|
||||||
bot as v1BotRouter,
|
bot as v1BotRouter,
|
||||||
@@ -55,7 +58,6 @@ import {
|
|||||||
organizations as v2OrganizationsRouter,
|
organizations as v2OrganizationsRouter,
|
||||||
secret as v2SecretRouter, // begin to phase out
|
secret as v2SecretRouter, // begin to phase out
|
||||||
secrets as v2SecretsRouter,
|
secrets as v2SecretsRouter,
|
||||||
serviceAccounts as v2ServiceAccountsRouter,
|
|
||||||
serviceTokenData as v2ServiceTokenDataRouter,
|
serviceTokenData as v2ServiceTokenDataRouter,
|
||||||
signup as v2SignupRouter,
|
signup as v2SignupRouter,
|
||||||
tags as v2TagsRouter,
|
tags as v2TagsRouter,
|
||||||
@@ -66,8 +68,7 @@ import {
|
|||||||
auth as v3AuthRouter,
|
auth as v3AuthRouter,
|
||||||
secrets as v3SecretsRouter,
|
secrets as v3SecretsRouter,
|
||||||
signup as v3SignupRouter,
|
signup as v3SignupRouter,
|
||||||
workspaces as v3WorkspacesRouter,
|
workspaces as v3WorkspacesRouter
|
||||||
serviceTokenData as v3ServiceTokenDataRouter
|
|
||||||
} from "./routes/v3";
|
} from "./routes/v3";
|
||||||
import { healthCheck } from "./routes/status";
|
import { healthCheck } from "./routes/status";
|
||||||
import { getLogger } from "./utils/logger";
|
import { getLogger } from "./utils/logger";
|
||||||
@@ -155,6 +156,7 @@ const main = async () => {
|
|||||||
app.use("/api/v1/organizations", eeOrganizationsRouter);
|
app.use("/api/v1/organizations", eeOrganizationsRouter);
|
||||||
app.use("/api/v1/sso", eeSSORouter);
|
app.use("/api/v1/sso", eeSSORouter);
|
||||||
app.use("/api/v1/cloud-products", eeCloudProductsRouter);
|
app.use("/api/v1/cloud-products", eeCloudProductsRouter);
|
||||||
|
app.use("/api/v3/service-token", v3ServiceTokenDataRouter);
|
||||||
|
|
||||||
// v1 routes
|
// v1 routes
|
||||||
app.use("/api/v1/signup", v1SignupRouter);
|
app.use("/api/v1/signup", v1SignupRouter);
|
||||||
@@ -198,8 +200,6 @@ const main = async () => {
|
|||||||
app.use("/api/v3/secrets", v3SecretsRouter);
|
app.use("/api/v3/secrets", v3SecretsRouter);
|
||||||
app.use("/api/v3/workspaces", v3WorkspacesRouter);
|
app.use("/api/v3/workspaces", v3WorkspacesRouter);
|
||||||
app.use("/api/v3/signup", v3SignupRouter);
|
app.use("/api/v3/signup", v3SignupRouter);
|
||||||
app.use("/api/v3/service-token", v3ServiceTokenDataRouter);
|
|
||||||
|
|
||||||
|
|
||||||
// api docs
|
// api docs
|
||||||
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerFile));
|
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerFile));
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ import auth from "./auth";
|
|||||||
import secrets from "./secrets";
|
import secrets from "./secrets";
|
||||||
import workspaces from "./workspaces";
|
import workspaces from "./workspaces";
|
||||||
import signup from "./signup";
|
import signup from "./signup";
|
||||||
import serviceTokenData from "./serviceTokenData";
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
auth,
|
auth,
|
||||||
secrets,
|
secrets,
|
||||||
signup,
|
signup,
|
||||||
workspaces,
|
workspaces
|
||||||
serviceTokenData
|
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-8
@@ -20,9 +20,13 @@ import {
|
|||||||
Modal,
|
Modal,
|
||||||
ModalContent,
|
ModalContent,
|
||||||
Select,
|
Select,
|
||||||
SelectItem
|
SelectItem,
|
||||||
|
UpgradePlanModal
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import {
|
||||||
|
useSubscription,
|
||||||
|
useWorkspace
|
||||||
|
} from "@app/context";
|
||||||
import {
|
import {
|
||||||
useCreateServiceTokenV3,
|
useCreateServiceTokenV3,
|
||||||
useGetUserWsKey,
|
useGetUserWsKey,
|
||||||
@@ -88,14 +92,17 @@ const schema = yup.object({
|
|||||||
export type FormData = yup.InferType<typeof schema>;
|
export type FormData = yup.InferType<typeof schema>;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
popUp: UsePopUpState<["serviceTokenV3"]>;
|
popUp: UsePopUpState<["serviceTokenV3", "upgradePlan"]>;
|
||||||
|
handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void;
|
||||||
handlePopUpToggle: (popUpName: keyof UsePopUpState<["serviceTokenV3"]>, state?: boolean) => void;
|
handlePopUpToggle: (popUpName: keyof UsePopUpState<["serviceTokenV3"]>, state?: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AddServiceTokenV3Modal = ({
|
export const AddServiceTokenV3Modal = ({
|
||||||
popUp,
|
popUp,
|
||||||
|
handlePopUpOpen,
|
||||||
handlePopUpToggle
|
handlePopUpToggle
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
|
const { subscription } = useSubscription();
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
const { data: latestFileKey } = useGetUserWsKey(currentWorkspace?._id ?? "");
|
const { data: latestFileKey } = useGetUserWsKey(currentWorkspace?._id ?? "");
|
||||||
@@ -400,19 +407,39 @@ export const AddServiceTokenV3Modal = ({
|
|||||||
control={control}
|
control={control}
|
||||||
name={`trustedIps.${index}.ipAddress`}
|
name={`trustedIps.${index}.ipAddress`}
|
||||||
defaultValue="0.0.0.0/0"
|
defaultValue="0.0.0.0/0"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
<FormControl
|
<FormControl
|
||||||
className="mb-0 flex-grow"
|
className="mb-0 flex-grow"
|
||||||
label={index === 0 ? "Trusted IP" : undefined}
|
label={index === 0 ? "Trusted IP" : undefined}
|
||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
>
|
>
|
||||||
<Input {...field} placeholder="123.456.789.0" />
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
field.onChange(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
placeholder="123.456.789.0"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => removeTrustedIp(index)}
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
removeTrustedIp(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
size="lg"
|
size="lg"
|
||||||
colorSchema="danger"
|
colorSchema="danger"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
@@ -426,11 +453,16 @@ export const AddServiceTokenV3Modal = ({
|
|||||||
<div className="my-4 ml-1">
|
<div className="my-4 ml-1">
|
||||||
<Button
|
<Button
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
onClick={() =>
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
appendTrustedIp({
|
appendTrustedIp({
|
||||||
ipAddress: "0.0.0.0/0"
|
ipAddress: "0.0.0.0/0"
|
||||||
})
|
})
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
size="xs"
|
size="xs"
|
||||||
>
|
>
|
||||||
@@ -478,6 +510,11 @@ export const AddServiceTokenV3Modal = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<UpgradePlanModal
|
||||||
|
isOpen={popUp?.upgradePlan?.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||||
|
text="You can use IP allowlisting if you switch to Infisical's Pro plan."
|
||||||
|
/>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|||||||
+3
-1
@@ -23,7 +23,8 @@ export const ServiceTokenV3Section = withProjectPermission(
|
|||||||
const { mutateAsync: deleteMutateAsync } = useDeleteServiceTokenV3();
|
const { mutateAsync: deleteMutateAsync } = useDeleteServiceTokenV3();
|
||||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||||
"serviceTokenV3",
|
"serviceTokenV3",
|
||||||
"deleteServiceTokenV3"
|
"deleteServiceTokenV3",
|
||||||
|
"upgradePlan"
|
||||||
] as const);
|
] as const);
|
||||||
|
|
||||||
const onDeleteServiceTokenDataSubmit = async (serviceTokenDataId: string) => {
|
const onDeleteServiceTokenDataSubmit = async (serviceTokenDataId: string) => {
|
||||||
@@ -74,6 +75,7 @@ export const ServiceTokenV3Section = withProjectPermission(
|
|||||||
/>
|
/>
|
||||||
<AddServiceTokenV3Modal
|
<AddServiceTokenV3Modal
|
||||||
popUp={popUp}
|
popUp={popUp}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
/>
|
/>
|
||||||
<DeleteActionModal
|
<DeleteActionModal
|
||||||
|
|||||||
Reference in New Issue
Block a user