From afd444cad684e20b4c406fc4ed0f2c0e1c3843fe Mon Sep 17 00:00:00 2001 From: = Date: Fri, 21 Feb 2025 14:29:01 +0530 Subject: [PATCH] feat: permission changes --- .../20250212191958_create-gateway.ts | 5 +- .../ee/services/gateway/gateway-service.ts | 47 ++++++++++++++----- .../src/ee/services/license/license-fns.ts | 4 +- .../ee/services/permission/org-permission.ts | 20 ++++---- .../src/context/OrgPermissionContext/types.ts | 8 ++-- frontend/src/hooks/api/gateways/types.ts | 2 +- .../GatewayListPage/GatewayListPage.tsx | 19 ++++++-- .../components/OrgRoleModifySection.utils.ts | 8 ++-- .../OrgPermissionGatewayRow.tsx | 34 +++++++------- .../SqlDatabaseInputForm.tsx | 8 +++- .../EditDynamicSecretSqlProviderForm.tsx | 9 +++- 11 files changed, 105 insertions(+), 59 deletions(-) diff --git a/backend/src/db/migrations/20250212191958_create-gateway.ts b/backend/src/db/migrations/20250212191958_create-gateway.ts index b91fc116c..3d9f37101 100644 --- a/backend/src/db/migrations/20250212191958_create-gateway.ts +++ b/backend/src/db/migrations/20250212191958_create-gateway.ts @@ -72,7 +72,10 @@ export async function up(knex: Knex): Promise { const doesGatewayColExist = await knex.schema.hasColumn(TableName.DynamicSecret, "gatewayId"); await knex.schema.alterTable(TableName.DynamicSecret, (t) => { // not setting a foreign constraint so that cascade effects are not triggered - if (!doesGatewayColExist) t.uuid("gatewayId"); + if (!doesGatewayColExist) { + t.uuid("gatewayId"); + t.foreign("gatewayId").references("id").inTable(TableName.Identity); + } }); } } diff --git a/backend/src/ee/services/gateway/gateway-service.ts b/backend/src/ee/services/gateway/gateway-service.ts index 44f48b574..d370035e4 100644 --- a/backend/src/ee/services/gateway/gateway-service.ts +++ b/backend/src/ee/services/gateway/gateway-service.ts @@ -79,7 +79,10 @@ export const gatewayServiceFactory = ({ actorAuthMethod, orgId ); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGatewayActions.Create, OrgPermissionSubjects.Gateway); + ForbiddenError.from(permission).throwUnlessCan( + OrgPermissionGatewayActions.CreateGateways, + OrgPermissionSubjects.Gateway + ); }; const getGatewayRelayDetails = async (actorId: string, actorOrgId: string, actorAuthMethod: ActorAuthMethod) => { @@ -152,7 +155,7 @@ export const gatewayServiceFactory = ({ const rootCaKeyAlgorithm = CertKeyAlgorithm.RSA_2048; const rootCaExpiration = new Date(new Date().setFullYear(2045)); const rootCaCert = await x509.X509CertificateGenerator.createSelfSigned({ - name: "CN=Infisical Gateway Root CA", + name: `O=${identityOrg},CN=Infisical Gateway Root CA`, serialNumber: rootCaSerialNumber, notBefore: rootCaIssuedAt, notAfter: rootCaExpiration, @@ -174,7 +177,7 @@ export const gatewayServiceFactory = ({ const clientCaCert = await x509.X509CertificateGenerator.create({ serialNumber: clientCaSerialNumber, - subject: "CN=Client Intermediate CA", + subject: `O=${identityOrg},CN=Client Intermediate CA`, issuer: rootCaCert.subject, notBefore: clientCaIssuedAt, notAfter: clientCaExpiration, @@ -227,12 +230,12 @@ export const gatewayServiceFactory = ({ // generate gateway ca const gatewayCaSerialNumber = createSerialNumber(); const gatewayCaIssuedAt = new Date(); - const gatewayCaExpiration = new Date(new Date().setFullYear(new Date().getFullYear() + 10)); + const gatewayCaExpiration = new Date(new Date().setFullYear(2045)); const gatewayCaKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]); const gatewayCaSkObj = crypto.KeyObject.from(gatewayCaKeys.privateKey); const gatewayCaCert = await x509.X509CertificateGenerator.create({ serialNumber: gatewayCaSerialNumber, - subject: "CN=KMIP Server Intermediate CA", + subject: `O=${identityOrg},CN=Gateway CA`, issuer: rootCaCert.subject, notBefore: gatewayCaIssuedAt, notAfter: gatewayCaExpiration, @@ -327,6 +330,12 @@ export const gatewayServiceFactory = ({ format: "der", type: "pkcs8" }); + const gatewayCaCert = new x509.X509Certificate( + orgKmsDecryptor({ + cipherTextBlob: orgGatewayConfig.encryptedGatewayCaCertificate + }) + ); + const gatewayCaPrivateKey = await crypto.subtle.importKey( "pkcs8", gatewayCaSkObj.export({ format: "der", type: "pkcs8" }), @@ -343,7 +352,7 @@ export const gatewayServiceFactory = ({ const extensions: x509.Extension[] = [ new x509.BasicConstraintsExtension(false), - await x509.AuthorityKeyIdentifierExtension.create(rootCaCert, false), + await x509.AuthorityKeyIdentifierExtension.create(gatewayCaCert, false), await x509.SubjectKeyIdentifierExtension.create(gatewayKeys.publicKey), new x509.CertificatePolicyExtension(["2.5.29.32.0"]), // anyPolicy new x509.KeyUsagesExtension( @@ -360,8 +369,8 @@ export const gatewayServiceFactory = ({ const privateKey = crypto.KeyObject.from(gatewayKeys.privateKey); const gatewayCertificate = await x509.X509CertificateGenerator.create({ serialNumber, - subject: `CN=${identityId},O=${identityOrg}`, - issuer: rootCaCert.subject, + subject: `CN=${identityId},O=${identityOrg},OU=Gateway`, + issuer: gatewayCaCert.subject, notBefore: certIssuedAt, notAfter: certExpireAt, signingKey: gatewayCaPrivateKey, @@ -401,7 +410,7 @@ export const gatewayServiceFactory = ({ }).cipherTextBlob, identityId, orgGatewayRootCaId: orgGatewayConfig.id, - name: alphaNumericNanoId(8) + name: `gateway-${alphaNumericNanoId(6)}` }); }); @@ -478,7 +487,10 @@ export const gatewayServiceFactory = ({ orgPermission.authMethod, orgPermission.orgId ); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGatewayActions.Read, OrgPermissionSubjects.Gateway); + ForbiddenError.from(permission).throwUnlessCan( + OrgPermissionGatewayActions.ListGateways, + OrgPermissionSubjects.Gateway + ); const orgGatewayConfig = await orgGatewayConfigDAL.findOne({ orgId: orgPermission.orgId }); if (!orgGatewayConfig) return []; @@ -496,7 +508,10 @@ export const gatewayServiceFactory = ({ orgPermission.authMethod, orgPermission.orgId ); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGatewayActions.Read, OrgPermissionSubjects.Gateway); + ForbiddenError.from(permission).throwUnlessCan( + OrgPermissionGatewayActions.ListGateways, + OrgPermissionSubjects.Gateway + ); const orgGatewayConfig = await orgGatewayConfigDAL.findOne({ orgId: orgPermission.orgId }); if (!orgGatewayConfig) throw new NotFoundError({ message: `Gateway with ID ${id} not found.` }); @@ -513,7 +528,10 @@ export const gatewayServiceFactory = ({ orgPermission.authMethod, orgPermission.orgId ); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGatewayActions.Edit, OrgPermissionSubjects.Gateway); + ForbiddenError.from(permission).throwUnlessCan( + OrgPermissionGatewayActions.EditGateways, + OrgPermissionSubjects.Gateway + ); const orgGatewayConfig = await orgGatewayConfigDAL.findOne({ orgId: orgPermission.orgId }); if (!orgGatewayConfig) throw new NotFoundError({ message: `Gateway with ID ${id} not found.` }); @@ -530,7 +548,10 @@ export const gatewayServiceFactory = ({ orgPermission.authMethod, orgPermission.orgId ); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGatewayActions.Delete, OrgPermissionSubjects.Gateway); + ForbiddenError.from(permission).throwUnlessCan( + OrgPermissionGatewayActions.DeleteGateways, + OrgPermissionSubjects.Gateway + ); const orgGatewayConfig = await orgGatewayConfigDAL.findOne({ orgId: orgPermission.orgId }); if (!orgGatewayConfig) throw new NotFoundError({ message: `Gateway with ID ${id} not found.` }); diff --git a/backend/src/ee/services/license/license-fns.ts b/backend/src/ee/services/license/license-fns.ts index 21d378802..85f5cf921 100644 --- a/backend/src/ee/services/license/license-fns.ts +++ b/backend/src/ee/services/license/license-fns.ts @@ -17,7 +17,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ environmentsUsed: 0, identityLimit: null, identitiesUsed: 0, - dynamicSecret: false, + dynamicSecret: true, secretVersioning: true, pitRecovery: false, ipAllowlisting: false, @@ -52,7 +52,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ enforceMfa: false, projectTemplates: false, kmip: false, - gateway: false + gateway: true }); export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => { diff --git a/backend/src/ee/services/permission/org-permission.ts b/backend/src/ee/services/permission/org-permission.ts index 696df74ff..dbabcc2c1 100644 --- a/backend/src/ee/services/permission/org-permission.ts +++ b/backend/src/ee/services/permission/org-permission.ts @@ -34,10 +34,10 @@ export enum OrgPermissionAdminConsoleAction { export enum OrgPermissionGatewayActions { // is there a better word for this. This mean can an identity be a gateway - Create = "create", - Read = "read", - Edit = "edit", - Delete = "delete" + CreateGateways = "create-gateways", + ListGateways = "list-gateways", + EditGateways = "edit-gateways", + DeleteGateways = "delete-gateways" } export enum OrgPermissionSubjects { @@ -280,10 +280,10 @@ const buildAdminPermission = () => { can(OrgPermissionAppConnectionActions.Delete, OrgPermissionSubjects.AppConnections); can(OrgPermissionAppConnectionActions.Connect, OrgPermissionSubjects.AppConnections); - can(OrgPermissionGatewayActions.Read, OrgPermissionSubjects.Gateway); - can(OrgPermissionGatewayActions.Create, OrgPermissionSubjects.Gateway); - can(OrgPermissionGatewayActions.Edit, OrgPermissionSubjects.Gateway); - can(OrgPermissionGatewayActions.Delete, OrgPermissionSubjects.Gateway); + can(OrgPermissionGatewayActions.ListGateways, OrgPermissionSubjects.Gateway); + can(OrgPermissionGatewayActions.CreateGateways, OrgPermissionSubjects.Gateway); + can(OrgPermissionGatewayActions.EditGateways, OrgPermissionSubjects.Gateway); + can(OrgPermissionGatewayActions.DeleteGateways, OrgPermissionSubjects.Gateway); can(OrgPermissionAdminConsoleAction.AccessAllProjects, OrgPermissionSubjects.AdminConsole); @@ -321,8 +321,8 @@ const buildMemberPermission = () => { can(OrgPermissionActions.Read, OrgPermissionSubjects.AuditLogs); can(OrgPermissionAppConnectionActions.Connect, OrgPermissionSubjects.AppConnections); - can(OrgPermissionGatewayActions.Read, OrgPermissionSubjects.Gateway); - can(OrgPermissionGatewayActions.Create, OrgPermissionSubjects.Gateway); + can(OrgPermissionGatewayActions.ListGateways, OrgPermissionSubjects.Gateway); + can(OrgPermissionGatewayActions.CreateGateways, OrgPermissionSubjects.Gateway); return rules; }; diff --git a/frontend/src/context/OrgPermissionContext/types.ts b/frontend/src/context/OrgPermissionContext/types.ts index 2eb5c19b3..2a31ed6a8 100644 --- a/frontend/src/context/OrgPermissionContext/types.ts +++ b/frontend/src/context/OrgPermissionContext/types.ts @@ -9,10 +9,10 @@ export enum OrgPermissionActions { export enum OrgGatewayPermissionActions { // is there a better word for this. This mean can an identity be a gateway - Create = "create", - Read = "read", - Edit = "edit", - Delete = "delete" + CreateGateways = "create-gateways", + ListGateways = "list-gateways", + EditGateways = "edit-gateways", + DeleteGateways = "delete-gateways" } export enum OrgPermissionSubjects { diff --git a/frontend/src/hooks/api/gateways/types.ts b/frontend/src/hooks/api/gateways/types.ts index 3c0971079..42ce1ebc2 100644 --- a/frontend/src/hooks/api/gateways/types.ts +++ b/frontend/src/hooks/api/gateways/types.ts @@ -6,7 +6,7 @@ export type TGateway = { updatedAt: string; issuedAt: string; serialNumber: string; - heartbeart: string; + heartbeat: string; identity: { name: string; id: string; diff --git a/frontend/src/pages/organization/Gateways/GatewayListPage/GatewayListPage.tsx b/frontend/src/pages/organization/Gateways/GatewayListPage/GatewayListPage.tsx index 9c9302b49..4a06534b6 100644 --- a/frontend/src/pages/organization/Gateways/GatewayListPage/GatewayListPage.tsx +++ b/frontend/src/pages/organization/Gateways/GatewayListPage/GatewayListPage.tsx @@ -5,6 +5,7 @@ import { faBookOpen, faEdit, faEllipsisV, + faInfoCircle, faMagnifyingGlass, faPlug, faSearch, @@ -125,8 +126,18 @@ export const GatewayListPage = withPermission( Name - Issued At + Cert Issued At Identity + + Health Check + + + + @@ -140,8 +151,8 @@ export const GatewayListPage = withPermission( {format(new Date(el.issuedAt), "yyyy-MM-dd hh:mm:ss aaa")} {el.identity.name} - {el.heartbeart - ? formatRelative(new Date(), new Date(el.heartbeart)) + {el.heartbeat + ? formatRelative(new Date(el.heartbeat), new Date()) : "-"} @@ -159,7 +170,7 @@ export const GatewayListPage = withPermission( {(isAllowed: boolean) => ( diff --git a/frontend/src/pages/organization/RoleByIDPage/components/OrgRoleModifySection.utils.ts b/frontend/src/pages/organization/RoleByIDPage/components/OrgRoleModifySection.utils.ts index 4b3393fa8..42dd31417 100644 --- a/frontend/src/pages/organization/RoleByIDPage/components/OrgRoleModifySection.utils.ts +++ b/frontend/src/pages/organization/RoleByIDPage/components/OrgRoleModifySection.utils.ts @@ -37,10 +37,10 @@ const kmipPermissionSchema = z const orgGatewayPermissionSchema = z .object({ - [OrgGatewayPermissionActions.Read]: z.boolean().optional(), - [OrgGatewayPermissionActions.Edit]: z.boolean().optional(), - [OrgGatewayPermissionActions.Delete]: z.boolean().optional(), - [OrgGatewayPermissionActions.Create]: z.boolean().optional() + [OrgGatewayPermissionActions.ListGateways]: z.boolean().optional(), + [OrgGatewayPermissionActions.EditGateways]: z.boolean().optional(), + [OrgGatewayPermissionActions.DeleteGateways]: z.boolean().optional(), + [OrgGatewayPermissionActions.CreateGateways]: z.boolean().optional() }) .optional(); diff --git a/frontend/src/pages/organization/RoleByIDPage/components/RolePermissionsSection/OrgPermissionGatewayRow.tsx b/frontend/src/pages/organization/RoleByIDPage/components/RolePermissionsSection/OrgPermissionGatewayRow.tsx index fe160bb31..46e88653a 100644 --- a/frontend/src/pages/organization/RoleByIDPage/components/RolePermissionsSection/OrgPermissionGatewayRow.tsx +++ b/frontend/src/pages/organization/RoleByIDPage/components/RolePermissionsSection/OrgPermissionGatewayRow.tsx @@ -24,10 +24,10 @@ enum Permission { } const PERMISSION_ACTIONS = [ - { action: OrgGatewayPermissionActions.Read, label: "Read" }, - { action: OrgGatewayPermissionActions.Create, label: "Create" }, - { action: OrgGatewayPermissionActions.Edit, label: "Modify" }, - { action: OrgGatewayPermissionActions.Delete, label: "Remove" } + { action: OrgGatewayPermissionActions.ListGateways, label: "List Gateways" }, + { action: OrgGatewayPermissionActions.CreateGateways, label: "Create Gateways" }, + { action: OrgGatewayPermissionActions.EditGateways, label: "Edit Gateways" }, + { action: OrgGatewayPermissionActions.DeleteGateways, label: "Delete Gateways" } ] as const; export const OrgGatewayPermissionRow = ({ isEditable, control, setValue }: Props) => { @@ -47,7 +47,7 @@ export const OrgGatewayPermissionRow = ({ isEditable, control, setValue }: Props if (isCustom) return Permission.Custom; if (score === 0) return Permission.NoAccess; if (score === totalActions) return Permission.FullAccess; - if (score === 1 && rule?.[OrgGatewayPermissionActions.Read]) return Permission.ReadOnly; + if (score === 1 && rule?.[OrgGatewayPermissionActions.ListGateways]) return Permission.ReadOnly; return Permission.Custom; }, [rule, isCustom]); @@ -78,10 +78,10 @@ export const OrgGatewayPermissionRow = ({ isEditable, control, setValue }: Props setValue( "permissions.gateway", { - [OrgGatewayPermissionActions.Read]: true, - [OrgGatewayPermissionActions.Edit]: true, - [OrgGatewayPermissionActions.Create]: true, - [OrgGatewayPermissionActions.Delete]: true + [OrgGatewayPermissionActions.ListGateways]: true, + [OrgGatewayPermissionActions.EditGateways]: true, + [OrgGatewayPermissionActions.CreateGateways]: true, + [OrgGatewayPermissionActions.DeleteGateways]: true }, { shouldDirty: true } ); @@ -90,10 +90,10 @@ export const OrgGatewayPermissionRow = ({ isEditable, control, setValue }: Props setValue( "permissions.gateway", { - [OrgGatewayPermissionActions.Read]: true, - [OrgGatewayPermissionActions.Edit]: false, - [OrgGatewayPermissionActions.Create]: false, - [OrgGatewayPermissionActions.Delete]: false + [OrgGatewayPermissionActions.ListGateways]: true, + [OrgGatewayPermissionActions.EditGateways]: false, + [OrgGatewayPermissionActions.CreateGateways]: false, + [OrgGatewayPermissionActions.DeleteGateways]: false }, { shouldDirty: true } ); @@ -104,10 +104,10 @@ export const OrgGatewayPermissionRow = ({ isEditable, control, setValue }: Props setValue( "permissions.gateway", { - [OrgGatewayPermissionActions.Read]: false, - [OrgGatewayPermissionActions.Edit]: false, - [OrgGatewayPermissionActions.Create]: false, - [OrgGatewayPermissionActions.Delete]: false + [OrgGatewayPermissionActions.ListGateways]: false, + [OrgGatewayPermissionActions.EditGateways]: false, + [OrgGatewayPermissionActions.CreateGateways]: false, + [OrgGatewayPermissionActions.DeleteGateways]: false }, { shouldDirty: true } ); diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/CreateDynamicSecretForm/SqlDatabaseInputForm.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/CreateDynamicSecretForm/SqlDatabaseInputForm.tsx index 7c877e150..68c16eee6 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/CreateDynamicSecretForm/SqlDatabaseInputForm.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/CreateDynamicSecretForm/SqlDatabaseInputForm.tsx @@ -247,9 +247,15 @@ export const SqlDatabaseInputForm = ({ className="w-full border border-mineshaft-500" dropdownContainerClassName="max-w-none" isLoading={isProjectGatewaysLoading} - placeholder="Select gateway" + placeholder="Internet gateway" position="popper" > + onChange(undefined)} + > + Internet Gateway + {projectGateways?.map((el) => ( {el.name} diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretSqlProviderForm.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretSqlProviderForm.tsx index 165b94314..572f7832e 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretSqlProviderForm.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretSqlProviderForm.tsx @@ -195,7 +195,9 @@ export const EditDynamicSecretSqlProviderForm = ({ + onChange(undefined)}> + Internet Gateway + {projectGateways?.map((el) => ( {el.name}