mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: adds scope org id column and fixes inject identity middleware
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
await knex.schema.alterTable(TableName.IdentityAccessToken, (t) => {
|
||||
t.uuid("scopeOrgId").notNullable();
|
||||
t.foreign("scopeOrgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.alterTable(TableName.IdentityAccessToken, (t) => {
|
||||
t.dropColumn("scopeOrgId");
|
||||
});
|
||||
}
|
||||
@@ -22,7 +22,8 @@ export const IdentityAccessTokensSchema = z.object({
|
||||
updatedAt: z.date(),
|
||||
name: z.string().nullable().optional(),
|
||||
authMethod: z.string(),
|
||||
accessTokenPeriod: z.coerce.number().default(0)
|
||||
accessTokenPeriod: z.coerce.number().default(0),
|
||||
scopeOrgId: z.string().uuid().nullable().optional()
|
||||
});
|
||||
|
||||
export type TIdentityAccessTokens = z.infer<typeof IdentityAccessTokensSchema>;
|
||||
|
||||
@@ -8,7 +8,6 @@ import { TScimTokenJwtPayload } from "@app/ee/services/scim/scim-types";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { slugSchema } from "@app/server/lib/schemas";
|
||||
import { ActorType, AuthMethod, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
|
||||
import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types";
|
||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||
@@ -152,15 +151,10 @@ export const injectIdentity = fp(
|
||||
|
||||
if (!authMode) return;
|
||||
|
||||
const subOrganizationSelector = req.headers?.["x-infisical-org"] as string | undefined;
|
||||
if (subOrganizationSelector) {
|
||||
await slugSchema().parseAsync(subOrganizationSelector);
|
||||
}
|
||||
|
||||
switch (authMode) {
|
||||
case AuthMode.JWT: {
|
||||
const { user, tokenVersionId, orgId, orgName, rootOrgId, parentOrgId } =
|
||||
await server.services.authToken.fnValidateJwtIdentity(token, subOrganizationSelector);
|
||||
await server.services.authToken.fnValidateJwtIdentity(token);
|
||||
requestContext.set("orgId", orgId);
|
||||
requestContext.set("orgName", orgName);
|
||||
requestContext.set("userAuthInfo", { userId: user.id, email: user.email || "" });
|
||||
@@ -180,11 +174,7 @@ export const injectIdentity = fp(
|
||||
break;
|
||||
}
|
||||
case AuthMode.IDENTITY_ACCESS_TOKEN: {
|
||||
const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken(
|
||||
token,
|
||||
req.realIp,
|
||||
subOrganizationSelector
|
||||
);
|
||||
const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken(token, req.realIp);
|
||||
const serverCfg = await getServerCfg();
|
||||
requestContext.set("orgId", identity.orgId);
|
||||
requestContext.set("orgName", identity.orgName);
|
||||
@@ -223,9 +213,6 @@ export const injectIdentity = fp(
|
||||
const serviceToken = await server.services.serviceToken.fnValidateServiceToken(token);
|
||||
requestContext.set("orgId", serviceToken.orgId);
|
||||
|
||||
if (subOrganizationSelector)
|
||||
throw new BadRequestError({ message: `Service token doesn't support sub organization selector` });
|
||||
|
||||
req.auth = {
|
||||
orgId: serviceToken.orgId,
|
||||
rootOrgId: serviceToken.rootOrgId,
|
||||
@@ -248,9 +235,6 @@ export const injectIdentity = fp(
|
||||
const { orgId, scimTokenId } = await server.services.scim.fnValidateScimToken(token);
|
||||
requestContext.set("orgId", orgId);
|
||||
|
||||
if (subOrganizationSelector)
|
||||
throw new BadRequestError({ message: `SCIM token doesn't support sub organization selector` });
|
||||
|
||||
req.auth = {
|
||||
authMode: AuthMode.SCIM_TOKEN,
|
||||
actor,
|
||||
|
||||
@@ -18,7 +18,8 @@ export const identityAccessTokenDALFactory = (db: TDbClient) => {
|
||||
.where(filter)
|
||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.IdentityAccessToken}.identityId`)
|
||||
.select(selectAllTableCols(TableName.IdentityAccessToken))
|
||||
.select(db.ref("orgId").withSchema(TableName.Identity).as("identityScopeOrgId"))
|
||||
.select(db.ref("orgId").withSchema(TableName.Identity).as("identityOrgId"))
|
||||
.select(db.ref("scopeOrgId").withSchema(TableName.IdentityAccessToken).as("scopeOrgId"))
|
||||
.first();
|
||||
|
||||
return doc;
|
||||
|
||||
@@ -184,11 +184,7 @@ export const identityAccessTokenServiceFactory = ({
|
||||
return { revokedToken };
|
||||
};
|
||||
|
||||
const fnValidateIdentityAccessToken = async (
|
||||
token: TIdentityAccessTokenJwtPayload,
|
||||
ipAddress?: string,
|
||||
subOrganizationSelector?: string
|
||||
) => {
|
||||
const fnValidateIdentityAccessToken = async (token: TIdentityAccessTokenJwtPayload, ipAddress?: string) => {
|
||||
const identityAccessToken = await identityAccessTokenDAL.findOne({
|
||||
[`${TableName.IdentityAccessToken}.id` as "id"]: token.identityAccessTokenId,
|
||||
isAccessTokenRevoked: false
|
||||
@@ -209,46 +205,32 @@ export const identityAccessTokenServiceFactory = ({
|
||||
trustedIps: trustedIps as TIp[]
|
||||
});
|
||||
}
|
||||
let orgId = "";
|
||||
let orgName = "";
|
||||
let parentOrgId = "";
|
||||
const identityOrgDetails = await orgDAL.findOne({ id: identityAccessToken.identityScopeOrgId });
|
||||
const rootOrgId = identityOrgDetails.rootOrgId || identityOrgDetails.id;
|
||||
|
||||
if (subOrganizationSelector) {
|
||||
const subOrganization = await orgDAL.findOne({ rootOrgId, slug: subOrganizationSelector });
|
||||
if (!subOrganization)
|
||||
throw new BadRequestError({ message: `Sub organization ${subOrganizationSelector} not found` });
|
||||
const scopeOrgId = identityAccessToken.scopeOrgId || identityAccessToken.identityOrgId;
|
||||
|
||||
const identityOrgMembership = await membershipIdentityDAL.findOne({
|
||||
scope: AccessScope.Organization,
|
||||
actorIdentityId: identityAccessToken.identityId,
|
||||
scopeOrgId: subOrganization.id
|
||||
});
|
||||
const identityOrgDetails = await orgDAL.findOne({ id: scopeOrgId });
|
||||
|
||||
if (!identityOrgMembership) {
|
||||
throw new BadRequestError({ message: "Identity does not belong to this organization" });
|
||||
}
|
||||
orgId = subOrganization.id;
|
||||
orgName = subOrganization.name;
|
||||
const isSubOrg = !!(identityOrgDetails.rootOrgId || identityOrgDetails.parentOrgId);
|
||||
|
||||
parentOrgId = subOrganization.parentOrgId as string;
|
||||
} else {
|
||||
const identityOrgMembership = await membershipIdentityDAL.findOne({
|
||||
scope: AccessScope.Organization,
|
||||
actorIdentityId: identityAccessToken.identityId,
|
||||
scopeOrgId: identityOrgDetails.id
|
||||
});
|
||||
const rootOrgId = isSubOrg
|
||||
? identityOrgDetails.rootOrgId || identityOrgDetails.parentOrgId || identityOrgDetails.id
|
||||
: identityOrgDetails.id;
|
||||
|
||||
if (!identityOrgMembership) {
|
||||
throw new BadRequestError({ message: "Identity does not belong to this organization" });
|
||||
}
|
||||
// Verify identity membership in the organization
|
||||
const identityOrgMembership = await membershipIdentityDAL.findOne({
|
||||
scope: AccessScope.Organization,
|
||||
actorIdentityId: identityAccessToken.identityId,
|
||||
scopeOrgId: identityOrgDetails.id
|
||||
});
|
||||
|
||||
orgId = identityOrgDetails.id;
|
||||
orgName = identityOrgDetails.name;
|
||||
parentOrgId = rootOrgId;
|
||||
if (!identityOrgMembership) {
|
||||
throw new BadRequestError({ message: "Identity does not belong to this organization" });
|
||||
}
|
||||
|
||||
const orgId = identityOrgDetails.id;
|
||||
const orgName = identityOrgDetails.name;
|
||||
const parentOrgId = identityOrgDetails.parentOrgId || rootOrgId;
|
||||
|
||||
let { accessTokenNumUses } = identityAccessToken;
|
||||
const tokenStatusInCache = await accessTokenQueue.getIdentityTokenDetailsInCache(identityAccessToken.id);
|
||||
if (tokenStatusInCache) {
|
||||
|
||||
@@ -7,6 +7,7 @@ export type TIdentityAccessTokenJwtPayload = {
|
||||
clientSecretId: string;
|
||||
identityAccessTokenId: string;
|
||||
authTokenType: string;
|
||||
subOrganizationId?: string;
|
||||
identityAuth: {
|
||||
oidc?: {
|
||||
claims: Record<string, string>;
|
||||
|
||||
@@ -304,7 +304,7 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
|
||||
to: "/organizations/$orgId/identities/$identityId",
|
||||
params: {
|
||||
identityId: id,
|
||||
orgId
|
||||
orgId: currentOrg.id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user