Merge pull request #3221 from Infisical/feat/allowShareToAnyoneEdition

Feat/allow share to anyone edition
This commit is contained in:
carlosmonastyrski
2025-03-14 17:06:49 -03:00
committed by GitHub
33 changed files with 671 additions and 112 deletions
@@ -0,0 +1,32 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.Organization)) {
const hasSecretShareToAnyoneCol = await knex.schema.hasColumn(
TableName.Organization,
"allowSecretSharingOutsideOrganization"
);
if (!hasSecretShareToAnyoneCol) {
await knex.schema.alterTable(TableName.Organization, (t) => {
t.boolean("allowSecretSharingOutsideOrganization").defaultTo(true);
});
}
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.Organization)) {
const hasSecretShareToAnyoneCol = await knex.schema.hasColumn(
TableName.Organization,
"allowSecretSharingOutsideOrganization"
);
if (hasSecretShareToAnyoneCol) {
await knex.schema.alterTable(TableName.Organization, (t) => {
t.dropColumn("allowSecretSharingOutsideOrganization");
});
}
}
}
+2 -1
View File
@@ -22,7 +22,8 @@ export const OrganizationsSchema = z.object({
kmsEncryptedDataKey: zodBuffer.nullable().optional(),
defaultMembershipRole: z.string().default("member"),
enforceMfa: z.boolean().default(false),
selectedMfaMethod: z.string().nullable().optional()
selectedMfaMethod: z.string().nullable().optional(),
allowSecretSharingOutsideOrganization: z.boolean().default(true).nullable().optional()
});
export type TOrganizations = z.infer<typeof OrganizationsSchema>;
@@ -32,6 +32,10 @@ export enum OrgPermissionAdminConsoleAction {
AccessAllProjects = "access-all-projects"
}
export enum OrgPermissionSecretShareAction {
ManageSettings = "manage-settings"
}
export enum OrgPermissionGatewayActions {
// is there a better word for this. This mean can an identity be a gateway
CreateGateways = "create-gateways",
@@ -59,7 +63,8 @@ export enum OrgPermissionSubjects {
ProjectTemplates = "project-templates",
AppConnections = "app-connections",
Kmip = "kmip",
Gateway = "gateway"
Gateway = "gateway",
SecretShare = "secret-share"
}
export type AppConnectionSubjectFields = {
@@ -91,7 +96,8 @@ export type OrgPermissionSet =
)
]
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole]
| [OrgPermissionKmipActions, OrgPermissionSubjects.Kmip];
| [OrgPermissionKmipActions, OrgPermissionSubjects.Kmip]
| [OrgPermissionSecretShareAction, OrgPermissionSubjects.SecretShare];
const AppConnectionConditionSchema = z
.object({
@@ -185,6 +191,12 @@ export const OrgPermissionSchema = z.discriminatedUnion("subject", [
"Describe what action an entity can take."
)
}),
z.object({
subject: z.literal(OrgPermissionSubjects.SecretShare).describe("The entity this permission pertains to."),
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionSecretShareAction).describe(
"Describe what action an entity can take."
)
}),
z.object({
subject: z.literal(OrgPermissionSubjects.Kmip).describe("The entity this permission pertains to."),
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionKmipActions).describe(
@@ -292,6 +304,8 @@ const buildAdminPermission = () => {
// the proxy assignment is temporary in order to prevent "more privilege" error during role assignment to MI
can(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
can(OrgPermissionSecretShareAction.ManageSettings, OrgPermissionSubjects.SecretShare);
return rules;
};
@@ -257,7 +257,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
scimEnabled: z.boolean().optional(),
defaultMembershipRoleSlug: slugSchema({ max: 64, field: "Default Membership Role" }).optional(),
enforceMfa: z.boolean().optional(),
selectedMfaMethod: z.nativeEnum(MfaMethod).optional()
selectedMfaMethod: z.nativeEnum(MfaMethod).optional(),
allowSecretSharingOutsideOrganization: z.boolean().optional()
}),
response: {
200: z.object({
+23 -3
View File
@@ -19,7 +19,11 @@ import {
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TOidcConfigDALFactory } from "@app/ee/services/oidc/oidc-config-dal";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import {
OrgPermissionActions,
OrgPermissionSecretShareAction,
OrgPermissionSubjects
} from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { TProjectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal";
@@ -286,12 +290,27 @@ export const orgServiceFactory = ({
actorOrgId,
actorAuthMethod,
orgId,
data: { name, slug, authEnforced, scimEnabled, defaultMembershipRoleSlug, enforceMfa, selectedMfaMethod }
data: {
name,
slug,
authEnforced,
scimEnabled,
defaultMembershipRoleSlug,
enforceMfa,
selectedMfaMethod,
allowSecretSharingOutsideOrganization
}
}: TUpdateOrgDTO) => {
const appCfg = getConfig();
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
if (allowSecretSharingOutsideOrganization !== undefined) {
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionSecretShareAction.ManageSettings,
OrgPermissionSubjects.SecretShare
);
}
const plan = await licenseService.getPlan(orgId);
const currentOrg = await orgDAL.findOrgById(actorOrgId);
@@ -358,7 +377,8 @@ export const orgServiceFactory = ({
scimEnabled,
defaultMembershipRole,
enforceMfa,
selectedMfaMethod
selectedMfaMethod,
allowSecretSharingOutsideOrganization
});
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
return org;
+1
View File
@@ -72,6 +72,7 @@ export type TUpdateOrgDTO = {
defaultMembershipRoleSlug: string;
enforceMfa: boolean;
selectedMfaMethod: MfaMethod;
allowSecretSharingOutsideOrganization: boolean;
}>;
} & TOrgPermission;
@@ -82,6 +82,13 @@ export const secretSharingServiceFactory = ({
if (!permission) throw new ForbiddenRequestError({ name: "User is not a part of the specified organization" });
$validateSharedSecretExpiry(expiresAt);
const org = await orgDAL.findOrgById(orgId);
if (!org.allowSecretSharingOutsideOrganization && accessType === SecretSharingAccessType.Anyone) {
throw new BadRequestError({
message: "Organization does not allow sharing secrets to members outside of this organization"
});
}
if (secretValue.length > 10_000) {
throw new BadRequestError({ message: "Shared secret value too long" });
}