feat: multiple auth methods for identities

This commit is contained in:
Daniel Hougaard
2024-10-26 23:26:22 +04:00
parent 2ddf75d2e6
commit 741138c4bd
35 changed files with 2200 additions and 2164 deletions
@@ -0,0 +1,55 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasAuthMethodColumnAccessToken = await knex.schema.hasColumn(TableName.IdentityAccessToken, "authMethod");
if (!hasAuthMethodColumnAccessToken) {
await knex.schema.alterTable(TableName.IdentityAccessToken, (t) => {
t.string("authMethod").nullable();
});
// Backfilling: Update the authMethod column in the IdentityAccessToken table to match the authMethod of the Identity
await knex(TableName.IdentityAccessToken).update({
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore because generate schema happens after this
authMethod: knex(TableName.Identity)
.select("authMethod")
.whereRaw(`${TableName.IdentityAccessToken}."identityId" = ${TableName.Identity}.id`)
.whereNotNull("authMethod")
.first()
});
// ! We delete all access tokens where the identity has no auth method set!
// ! Which means un-configured identities that for some reason have access tokens, will have their access tokens deleted.
await knex(TableName.IdentityAccessToken)
.whereNotExists((queryBuilder) => {
void queryBuilder
.select("id")
.from(TableName.Identity)
.whereRaw(`${TableName.IdentityAccessToken}."identityId" = ${TableName.Identity}.id`)
.whereNotNull("authMethod");
})
.delete();
// Finally we set the authMethod to notNullable after populating the column.
// This will fail if the data is not populated correctly, so it's safe.
await knex.schema.alterTable(TableName.IdentityAccessToken, (t) => {
t.string("authMethod").notNullable().alter();
});
}
// ! We aren't dropping the authMethod column from the Identity itself, because we wan't to be able to easily rollback for the time being.
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export async function down(knex: Knex): Promise<void> {
const hasAuthMethodColumnAccessToken = await knex.schema.hasColumn(TableName.IdentityAccessToken, "authMethod");
if (hasAuthMethodColumnAccessToken) {
await knex.schema.alterTable(TableName.IdentityAccessToken, (t) => {
t.dropColumn("authMethod");
});
}
}
+1 -1
View File
@@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models";
export const IdentitiesSchema = z.object({
id: z.string().uuid(),
name: z.string(),
authMethod: z.string().nullable().optional(),
// authMethod: z.string().nullable().optional(),
createdAt: z.date(),
updatedAt: z.date()
});
@@ -20,7 +20,8 @@ export const IdentityAccessTokensSchema = z.object({
identityId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
name: z.string().nullable().optional()
name: z.string().nullable().optional(),
authMethod: z.string()
});
export type TIdentityAccessTokens = z.infer<typeof IdentityAccessTokensSchema>;
+1 -1
View File
@@ -189,7 +189,7 @@ export enum ProjectUpgradeStatus {
export enum IdentityAuthMethod {
TOKEN_AUTH = "token-auth",
Univeral = "universal-auth",
UNIVERSAL_AUTH = "universal-auth",
KUBERNETES_AUTH = "kubernetes-auth",
GCP_AUTH = "gcp-auth",
AWS_AUTH = "aws-auth",
+1 -1
View File
@@ -16,7 +16,7 @@ export async function seed(knex: Knex): Promise<void> {
// @ts-ignore
id: seedData1.machineIdentity.id,
name: seedData1.machineIdentity.name,
authMethod: IdentityAuthMethod.Univeral
authMethod: IdentityAuthMethod.UNIVERSAL_AUTH
}
]);
const identityUa = await knex(TableName.IdentityUniversalAuth)
-7
View File
@@ -1087,7 +1087,6 @@ export const registerRoutes = async (
const identityTokenAuthService = identityTokenAuthServiceFactory({
identityTokenAuthDAL,
identityDAL,
identityOrgMembershipDAL,
identityAccessTokenDAL,
permissionService,
@@ -1096,7 +1095,6 @@ export const registerRoutes = async (
const identityUaService = identityUaServiceFactory({
identityOrgMembershipDAL,
permissionService,
identityDAL,
identityAccessTokenDAL,
identityUaClientSecretDAL,
identityUaDAL,
@@ -1106,7 +1104,6 @@ export const registerRoutes = async (
identityKubernetesAuthDAL,
identityOrgMembershipDAL,
identityAccessTokenDAL,
identityDAL,
orgBotDAL,
permissionService,
licenseService
@@ -1115,7 +1112,6 @@ export const registerRoutes = async (
identityGcpAuthDAL,
identityOrgMembershipDAL,
identityAccessTokenDAL,
identityDAL,
permissionService,
licenseService
});
@@ -1124,7 +1120,6 @@ export const registerRoutes = async (
identityAccessTokenDAL,
identityAwsAuthDAL,
identityOrgMembershipDAL,
identityDAL,
licenseService,
permissionService
});
@@ -1133,7 +1128,6 @@ export const registerRoutes = async (
identityAzureAuthDAL,
identityOrgMembershipDAL,
identityAccessTokenDAL,
identityDAL,
permissionService,
licenseService
});
@@ -1142,7 +1136,6 @@ export const registerRoutes = async (
identityOidcAuthDAL,
identityOrgMembershipDAL,
identityAccessTokenDAL,
identityDAL,
permissionService,
licenseService,
orgBotDAL
@@ -1,6 +1,12 @@
import { z } from "zod";
import { IdentitiesSchema, IdentityOrgMembershipsSchema, OrgMembershipRole, OrgRolesSchema } from "@app/db/schemas";
import {
IdentitiesSchema,
IdentityAuthMethod,
IdentityOrgMembershipsSchema,
OrgMembershipRole,
OrgRolesSchema
} from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { IDENTITIES } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
@@ -216,7 +222,9 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
permissions: true,
description: true
}).optional(),
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true })
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
authMethods: z.array(z.nativeEnum(IdentityAuthMethod))
})
})
})
}
@@ -1,6 +1,6 @@
import { z } from "zod";
import { IdentitiesSchema, IdentityOrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
import { IdentitiesSchema, IdentityAuthMethod, IdentityOrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
import { ORGANIZATIONS } from "@app/lib/api-docs";
import { OrderByDirection } from "@app/lib/types";
import { readLimit } from "@app/server/config/rateLimiter";
@@ -58,7 +58,9 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => {
permissions: true,
description: true
}).optional(),
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true })
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
authMethods: z.array(z.nativeEnum(IdentityAuthMethod))
})
})
).array(),
totalCount: z.number()
@@ -1,7 +1,7 @@
import { Knex } from "knex";
import { TDbClient } from "@app/db";
import { IdentityAuthMethod, TableName, TIdentityAccessTokens } from "@app/db/schemas";
import { TableName, TIdentityAccessTokens } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols } from "@app/lib/knex";
import { logger } from "@app/lib/logger";
@@ -17,54 +17,27 @@ export const identityAccessTokenDALFactory = (db: TDbClient) => {
const doc = await (tx || db.replicaNode())(TableName.IdentityAccessToken)
.where(filter)
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.IdentityAccessToken}.identityId`)
.leftJoin(TableName.IdentityUaClientSecret, (qb) => {
qb.on(`${TableName.Identity}.authMethod`, db.raw("?", [IdentityAuthMethod.Univeral])).andOn(
`${TableName.IdentityAccessToken}.identityUAClientSecretId`,
`${TableName.IdentityUaClientSecret}.id`
);
})
.leftJoin(TableName.IdentityUniversalAuth, (qb) => {
qb.on(`${TableName.Identity}.authMethod`, db.raw("?", [IdentityAuthMethod.Univeral])).andOn(
`${TableName.IdentityUaClientSecret}.identityUAId`,
`${TableName.IdentityUniversalAuth}.id`
);
})
.leftJoin(TableName.IdentityGcpAuth, (qb) => {
qb.on(`${TableName.Identity}.authMethod`, db.raw("?", [IdentityAuthMethod.GCP_AUTH])).andOn(
`${TableName.Identity}.id`,
`${TableName.IdentityGcpAuth}.identityId`
);
})
.leftJoin(TableName.IdentityAwsAuth, (qb) => {
qb.on(`${TableName.Identity}.authMethod`, db.raw("?", [IdentityAuthMethod.AWS_AUTH])).andOn(
`${TableName.Identity}.id`,
`${TableName.IdentityAwsAuth}.identityId`
);
})
.leftJoin(TableName.IdentityAzureAuth, (qb) => {
qb.on(`${TableName.Identity}.authMethod`, db.raw("?", [IdentityAuthMethod.AZURE_AUTH])).andOn(
`${TableName.Identity}.id`,
`${TableName.IdentityAzureAuth}.identityId`
);
})
.leftJoin(TableName.IdentityKubernetesAuth, (qb) => {
qb.on(`${TableName.Identity}.authMethod`, db.raw("?", [IdentityAuthMethod.KUBERNETES_AUTH])).andOn(
`${TableName.Identity}.id`,
`${TableName.IdentityKubernetesAuth}.identityId`
);
})
.leftJoin(TableName.IdentityOidcAuth, (qb) => {
qb.on(`${TableName.Identity}.authMethod`, db.raw("?", [IdentityAuthMethod.OIDC_AUTH])).andOn(
`${TableName.Identity}.id`,
`${TableName.IdentityOidcAuth}.identityId`
);
})
.leftJoin(TableName.IdentityTokenAuth, (qb) => {
qb.on(`${TableName.Identity}.authMethod`, db.raw("?", [IdentityAuthMethod.TOKEN_AUTH])).andOn(
`${TableName.Identity}.id`,
`${TableName.IdentityTokenAuth}.identityId`
);
})
.leftJoin(
TableName.IdentityUaClientSecret,
`${TableName.IdentityAccessToken}.identityUAClientSecretId`,
`${TableName.IdentityUaClientSecret}.id`
)
.leftJoin(
TableName.IdentityUniversalAuth,
`${TableName.IdentityUaClientSecret}.identityUAId`,
`${TableName.IdentityUniversalAuth}.id`
)
.leftJoin(TableName.IdentityGcpAuth, `${TableName.Identity}.id`, `${TableName.IdentityGcpAuth}.identityId`)
.leftJoin(TableName.IdentityAwsAuth, `${TableName.Identity}.id`, `${TableName.IdentityAwsAuth}.identityId`)
.leftJoin(TableName.IdentityAzureAuth, `${TableName.Identity}.id`, `${TableName.IdentityAzureAuth}.identityId`)
.leftJoin(
TableName.IdentityKubernetesAuth,
`${TableName.Identity}.id`,
`${TableName.IdentityKubernetesAuth}.identityId`
)
.leftJoin(TableName.IdentityOidcAuth, `${TableName.Identity}.id`, `${TableName.IdentityOidcAuth}.identityId`)
.leftJoin(TableName.IdentityTokenAuth, `${TableName.Identity}.id`, `${TableName.IdentityTokenAuth}.identityId`)
.select(selectAllTableCols(TableName.IdentityAccessToken))
.select(
db.ref("accessTokenTrustedIps").withSchema(TableName.IdentityUniversalAuth).as("accessTokenTrustedIpsUa"),
@@ -82,14 +55,13 @@ export const identityAccessTokenDALFactory = (db: TDbClient) => {
return {
...doc,
accessTokenTrustedIps:
doc.accessTokenTrustedIpsUa ||
doc.accessTokenTrustedIpsGcp ||
doc.accessTokenTrustedIpsAws ||
doc.accessTokenTrustedIpsAzure ||
doc.accessTokenTrustedIpsK8s ||
doc.accessTokenTrustedIpsOidc ||
doc.accessTokenTrustedIpsToken
trustedIpsUniversalAuth: doc.accessTokenTrustedIpsUa,
trustedIpsGcpAuth: doc.accessTokenTrustedIpsGcp,
trustedIpsAwsAuth: doc.accessTokenTrustedIpsAws,
trustedIpsAzureAuth: doc.accessTokenTrustedIpsAzure,
trustedIpsKubernetesAuth: doc.accessTokenTrustedIpsK8s,
trustedIpsOidcAuth: doc.accessTokenTrustedIpsOidc,
trustedIpsAccessTokenAuth: doc.accessTokenTrustedIpsToken
};
} catch (error) {
throw new DatabaseError({ error, name: "IdAccessTokenFindOne" });
@@ -1,6 +1,6 @@
import jwt, { JwtPayload } from "jsonwebtoken";
import { TableName, TIdentityAccessTokens } from "@app/db/schemas";
import { IdentityAuthMethod, TableName, TIdentityAccessTokens } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { checkIPAgainstBlocklist, TIp } from "@app/lib/ip";
@@ -164,10 +164,22 @@ export const identityAccessTokenServiceFactory = ({
message: "Failed to authorize revoked access token, access token is revoked"
});
if (ipAddress && identityAccessToken) {
const trustedIpsMap: Record<IdentityAuthMethod, unknown> = {
[IdentityAuthMethod.UNIVERSAL_AUTH]: identityAccessToken.trustedIpsUniversalAuth,
[IdentityAuthMethod.GCP_AUTH]: identityAccessToken.trustedIpsGcpAuth,
[IdentityAuthMethod.AWS_AUTH]: identityAccessToken.trustedIpsAwsAuth,
[IdentityAuthMethod.AZURE_AUTH]: identityAccessToken.trustedIpsAzureAuth,
[IdentityAuthMethod.KUBERNETES_AUTH]: identityAccessToken.trustedIpsKubernetesAuth,
[IdentityAuthMethod.OIDC_AUTH]: identityAccessToken.trustedIpsOidcAuth,
[IdentityAuthMethod.TOKEN_AUTH]: identityAccessToken.trustedIpsAccessTokenAuth
};
const trustedIps = trustedIpsMap[identityAccessToken.authMethod as IdentityAuthMethod];
if (ipAddress) {
checkIPAgainstBlocklist({
ipAddress,
trustedIps: identityAccessToken?.accessTokenTrustedIps as TIp[]
trustedIps: trustedIps as TIp[]
});
}
@@ -13,7 +13,7 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedErro
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityDALFactory } from "../identity/identity-dal";
// import { TIdentityDALFactory } from "../identity/identity-dal";
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
@@ -33,7 +33,7 @@ type TIdentityAwsAuthServiceFactoryDep = {
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
identityAwsAuthDAL: Pick<TIdentityAwsAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">;
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
identityDAL: Pick<TIdentityDALFactory, "updateById">;
// identityDAL: Pick<TIdentityDALFactory, "updateById">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
};
@@ -44,7 +44,7 @@ export const identityAwsAuthServiceFactory = ({
identityAccessTokenDAL,
identityAwsAuthDAL,
identityOrgMembershipDAL,
identityDAL,
// identityDAL,
licenseService,
permissionService
}: TIdentityAwsAuthServiceFactoryDep) => {
@@ -113,7 +113,8 @@ export const identityAwsAuthServiceFactory = ({
accessTokenTTL: identityAwsAuth.accessTokenTTL,
accessTokenMaxTTL: identityAwsAuth.accessTokenMaxTTL,
accessTokenNumUses: 0,
accessTokenNumUsesLimit: identityAwsAuth.accessTokenNumUsesLimit
accessTokenNumUsesLimit: identityAwsAuth.accessTokenNumUsesLimit,
authMethod: IdentityAuthMethod.AWS_AUTH
},
tx
);
@@ -155,10 +156,12 @@ export const identityAwsAuthServiceFactory = ({
}: TAttachAwsAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity.authMethod)
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AWS_AUTH)) {
throw new BadRequestError({
message: "Failed to add AWS Auth to already configured identity"
});
}
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
@@ -206,13 +209,6 @@ export const identityAwsAuthServiceFactory = ({
},
tx
);
await identityDAL.updateById(
identityMembershipOrg.identityId,
{
authMethod: IdentityAuthMethod.AWS_AUTH
},
tx
);
return doc;
});
return { ...identityAwsAuth, orgId: identityMembershipOrg.orgId };
@@ -234,10 +230,12 @@ export const identityAwsAuthServiceFactory = ({
}: TUpdateAwsAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.AWS_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AWS_AUTH)) {
throw new BadRequestError({
message: "Failed to update AWS Auth"
});
}
const identityAwsAuth = await identityAwsAuthDAL.findOne({ identityId });
@@ -293,10 +291,12 @@ export const identityAwsAuthServiceFactory = ({
const getAwsAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetAwsAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.AWS_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AWS_AUTH)) {
throw new BadRequestError({
message: "The identity does not have AWS Auth attached"
});
}
const awsIdentityAuth = await identityAwsAuthDAL.findOne({ identityId });
@@ -320,10 +320,11 @@ export const identityAwsAuthServiceFactory = ({
}: TRevokeAwsAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.AWS_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AWS_AUTH)) {
throw new BadRequestError({
message: "The identity does not have aws auth"
});
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
@@ -348,7 +349,7 @@ export const identityAwsAuthServiceFactory = ({
const revokedIdentityAwsAuth = await identityAwsAuthDAL.transaction(async (tx) => {
const deletedAwsAuth = await identityAwsAuthDAL.delete({ identityId }, tx);
await identityDAL.updateById(identityId, { authMethod: null }, tx);
// await identityDAL.updateById(identityId, { authMethod: null }, tx);
return { ...deletedAwsAuth?.[0], orgId: identityMembershipOrg.orgId };
});
return revokedIdentityAwsAuth;
@@ -11,7 +11,7 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedErro
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityDALFactory } from "../identity/identity-dal";
// import { TIdentityDALFactory } from "../identity/identity-dal";
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
@@ -32,7 +32,7 @@ type TIdentityAzureAuthServiceFactoryDep = {
>;
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
identityDAL: Pick<TIdentityDALFactory, "updateById">;
// identityDAL: Pick<TIdentityDALFactory, "updateById">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
};
@@ -43,7 +43,7 @@ export const identityAzureAuthServiceFactory = ({
identityAzureAuthDAL,
identityOrgMembershipDAL,
identityAccessTokenDAL,
identityDAL,
// identityDAL,
permissionService,
licenseService
}: TIdentityAzureAuthServiceFactoryDep) => {
@@ -84,7 +84,8 @@ export const identityAzureAuthServiceFactory = ({
accessTokenTTL: identityAzureAuth.accessTokenTTL,
accessTokenMaxTTL: identityAzureAuth.accessTokenMaxTTL,
accessTokenNumUses: 0,
accessTokenNumUsesLimit: identityAzureAuth.accessTokenNumUsesLimit
accessTokenNumUsesLimit: identityAzureAuth.accessTokenNumUsesLimit,
authMethod: IdentityAuthMethod.AZURE_AUTH
},
tx
);
@@ -126,11 +127,12 @@ export const identityAzureAuthServiceFactory = ({
}: TAttachAzureAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity.authMethod)
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AZURE_AUTH)) {
throw new BadRequestError({
message: "Failed to add Azure Auth to already configured identity"
});
}
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
}
@@ -176,13 +178,7 @@ export const identityAzureAuthServiceFactory = ({
},
tx
);
await identityDAL.updateById(
identityMembershipOrg.identityId,
{
authMethod: IdentityAuthMethod.AZURE_AUTH
},
tx
);
return doc;
});
return { ...identityAzureAuth, orgId: identityMembershipOrg.orgId };
@@ -204,10 +200,11 @@ export const identityAzureAuthServiceFactory = ({
}: TUpdateAzureAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.AZURE_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AZURE_AUTH)) {
throw new BadRequestError({
message: "Failed to update Azure Auth"
});
}
const identityGcpAuth = await identityAzureAuthDAL.findOne({ identityId });
@@ -266,10 +263,11 @@ export const identityAzureAuthServiceFactory = ({
const getAzureAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetAzureAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.AZURE_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AZURE_AUTH)) {
throw new BadRequestError({
message: "The identity does not have Azure Auth attached"
});
}
const identityAzureAuth = await identityAzureAuthDAL.findOne({ identityId });
@@ -294,10 +292,11 @@ export const identityAzureAuthServiceFactory = ({
}: TRevokeAzureAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.AZURE_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AZURE_AUTH)) {
throw new BadRequestError({
message: "The identity does not have azure auth"
});
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
@@ -321,7 +320,7 @@ export const identityAzureAuthServiceFactory = ({
const revokedIdentityAzureAuth = await identityAzureAuthDAL.transaction(async (tx) => {
const deletedAzureAuth = await identityAzureAuthDAL.delete({ identityId }, tx);
await identityDAL.updateById(identityId, { authMethod: null }, tx);
// await identityDAL.updateById(identityId, { authMethod: null }, tx);
return { ...deletedAzureAuth?.[0], orgId: identityMembershipOrg.orgId };
});
return revokedIdentityAzureAuth;
@@ -11,7 +11,7 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedErro
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityDALFactory } from "../identity/identity-dal";
// import { TIdentityDALFactory } from "../identity/identity-dal";
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
@@ -30,7 +30,7 @@ type TIdentityGcpAuthServiceFactoryDep = {
identityGcpAuthDAL: Pick<TIdentityGcpAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">;
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
identityDAL: Pick<TIdentityDALFactory, "updateById">;
// identityDAL: Pick<TIdentityDALFactory, "updateById">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
};
@@ -41,7 +41,7 @@ export const identityGcpAuthServiceFactory = ({
identityGcpAuthDAL,
identityOrgMembershipDAL,
identityAccessTokenDAL,
identityDAL,
// identityDAL,
permissionService,
licenseService
}: TIdentityGcpAuthServiceFactoryDep) => {
@@ -125,7 +125,8 @@ export const identityGcpAuthServiceFactory = ({
accessTokenTTL: identityGcpAuth.accessTokenTTL,
accessTokenMaxTTL: identityGcpAuth.accessTokenMaxTTL,
accessTokenNumUses: 0,
accessTokenNumUsesLimit: identityGcpAuth.accessTokenNumUsesLimit
accessTokenNumUsesLimit: identityGcpAuth.accessTokenNumUsesLimit,
authMethod: IdentityAuthMethod.GCP_AUTH
},
tx
);
@@ -168,10 +169,12 @@ export const identityGcpAuthServiceFactory = ({
}: TAttachGcpAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity.authMethod)
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.GCP_AUTH)) {
throw new BadRequestError({
message: "Failed to add GCP Auth to already configured identity"
});
}
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
@@ -219,13 +222,6 @@ export const identityGcpAuthServiceFactory = ({
},
tx
);
await identityDAL.updateById(
identityMembershipOrg.identityId,
{
authMethod: IdentityAuthMethod.GCP_AUTH
},
tx
);
return doc;
});
return { ...identityGcpAuth, orgId: identityMembershipOrg.orgId };
@@ -248,10 +244,12 @@ export const identityGcpAuthServiceFactory = ({
}: TUpdateGcpAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.GCP_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.GCP_AUTH)) {
throw new BadRequestError({
message: "Failed to update GCP Auth"
});
}
const identityGcpAuth = await identityGcpAuthDAL.findOne({ identityId });
@@ -311,10 +309,12 @@ export const identityGcpAuthServiceFactory = ({
const getGcpAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetGcpAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.GCP_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.GCP_AUTH)) {
throw new BadRequestError({
message: "The identity does not have GCP Auth attached"
});
}
const identityGcpAuth = await identityGcpAuthDAL.findOne({ identityId });
@@ -339,10 +339,12 @@ export const identityGcpAuthServiceFactory = ({
}: TRevokeGcpAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.GCP_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.GCP_AUTH)) {
throw new BadRequestError({
message: "The identity does not have gcp auth"
});
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
@@ -366,7 +368,7 @@ export const identityGcpAuthServiceFactory = ({
const revokedIdentityGcpAuth = await identityGcpAuthDAL.transaction(async (tx) => {
const deletedGcpAuth = await identityGcpAuthDAL.delete({ identityId }, tx);
await identityDAL.updateById(identityId, { authMethod: null }, tx);
// await identityDAL.updateById(identityId, { authMethod: null }, tx);
return { ...deletedGcpAuth?.[0], orgId: identityMembershipOrg.orgId };
});
return revokedIdentityGcpAuth;
@@ -22,7 +22,7 @@ import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityDALFactory } from "../identity/identity-dal";
// import { TIdentityDALFactory } from "../identity/identity-dal";
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
@@ -44,7 +44,7 @@ type TIdentityKubernetesAuthServiceFactoryDep = {
>;
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne" | "findById">;
identityDAL: Pick<TIdentityDALFactory, "updateById">;
// identityDAL: Pick<TIdentityDALFactory, "updateById">;
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "transaction" | "create">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
@@ -56,7 +56,7 @@ export const identityKubernetesAuthServiceFactory = ({
identityKubernetesAuthDAL,
identityOrgMembershipDAL,
identityAccessTokenDAL,
identityDAL,
// identityDAL,
orgBotDAL,
permissionService,
licenseService
@@ -215,7 +215,8 @@ export const identityKubernetesAuthServiceFactory = ({
accessTokenTTL: identityKubernetesAuth.accessTokenTTL,
accessTokenMaxTTL: identityKubernetesAuth.accessTokenMaxTTL,
accessTokenNumUses: 0,
accessTokenNumUsesLimit: identityKubernetesAuth.accessTokenNumUsesLimit
accessTokenNumUsesLimit: identityKubernetesAuth.accessTokenNumUsesLimit,
authMethod: IdentityAuthMethod.KUBERNETES_AUTH
},
tx
);
@@ -260,10 +261,12 @@ export const identityKubernetesAuthServiceFactory = ({
}: TAttachKubernetesAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity.authMethod)
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.KUBERNETES_AUTH)) {
throw new BadRequestError({
message: "Failed to add Kubernetes Auth to already configured identity"
});
}
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
@@ -372,13 +375,6 @@ export const identityKubernetesAuthServiceFactory = ({
},
tx
);
await identityDAL.updateById(
identityMembershipOrg.identityId,
{
authMethod: IdentityAuthMethod.KUBERNETES_AUTH
},
tx
);
return doc;
});
@@ -404,10 +400,12 @@ export const identityKubernetesAuthServiceFactory = ({
}: TUpdateKubernetesAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.KUBERNETES_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.KUBERNETES_AUTH)) {
throw new BadRequestError({
message: "Failed to update Kubernetes Auth"
});
}
const identityKubernetesAuth = await identityKubernetesAuthDAL.findOne({ identityId });
@@ -532,11 +530,12 @@ export const identityKubernetesAuthServiceFactory = ({
}: TGetKubernetesAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.KUBERNETES_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.KUBERNETES_AUTH)) {
throw new BadRequestError({
message: "The identity does not have Kubernetes Auth attached"
});
}
const identityKubernetesAuth = await identityKubernetesAuthDAL.findOne({ identityId });
const { permission } = await permissionService.getOrgPermission(
@@ -597,10 +596,12 @@ export const identityKubernetesAuthServiceFactory = ({
}: TRevokeKubernetesAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.KUBERNETES_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.KUBERNETES_AUTH)) {
throw new BadRequestError({
message: "The identity does not have kubernetes auth"
});
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
@@ -624,7 +625,7 @@ export const identityKubernetesAuthServiceFactory = ({
const revokedIdentityKubernetesAuth = await identityKubernetesAuthDAL.transaction(async (tx) => {
const deletedKubernetesAuth = await identityKubernetesAuthDAL.delete({ identityId }, tx);
await identityDAL.updateById(identityId, { authMethod: null }, tx);
// await identityDAL.updateById(identityId, { authMethod: null }, tx);
return { ...deletedKubernetesAuth?.[0], orgId: identityMembershipOrg.orgId };
});
return revokedIdentityKubernetesAuth;
@@ -22,7 +22,7 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedErro
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityDALFactory } from "../identity/identity-dal";
// import { TIdentityDALFactory } from "../identity/identity-dal";
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
@@ -41,7 +41,7 @@ type TIdentityOidcAuthServiceFactoryDep = {
identityOidcAuthDAL: TIdentityOidcAuthDALFactory;
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
identityDAL: Pick<TIdentityDALFactory, "updateById">;
// identityDAL: Pick<TIdentityDALFactory, "updateById">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "transaction" | "create">;
@@ -52,7 +52,7 @@ export type TIdentityOidcAuthServiceFactory = ReturnType<typeof identityOidcAuth
export const identityOidcAuthServiceFactory = ({
identityOidcAuthDAL,
identityOrgMembershipDAL,
identityDAL,
// identityDAL,
permissionService,
licenseService,
identityAccessTokenDAL,
@@ -61,7 +61,7 @@ export const identityOidcAuthServiceFactory = ({
const login = async ({ identityId, jwt: oidcJwt }: TLoginOidcAuthDTO) => {
const identityOidcAuth = await identityOidcAuthDAL.findOne({ identityId });
if (!identityOidcAuth) {
throw new NotFoundError({ message: "GCP auth method not found for identity, did you configure GCP auth?" });
throw new NotFoundError({ message: "OIDC auth method not found for identity, did you configure OIDC auth?" });
}
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({
@@ -181,7 +181,8 @@ export const identityOidcAuthServiceFactory = ({
accessTokenTTL: identityOidcAuth.accessTokenTTL,
accessTokenMaxTTL: identityOidcAuth.accessTokenMaxTTL,
accessTokenNumUses: 0,
accessTokenNumUsesLimit: identityOidcAuth.accessTokenNumUsesLimit
accessTokenNumUsesLimit: identityOidcAuth.accessTokenNumUsesLimit,
authMethod: IdentityAuthMethod.OIDC_AUTH
},
tx
);
@@ -228,10 +229,11 @@ export const identityOidcAuthServiceFactory = ({
if (!identityMembershipOrg) {
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
}
if (identityMembershipOrg.identity.authMethod)
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OIDC_AUTH)) {
throw new BadRequestError({
message: "Failed to add OIDC Auth to already configured identity"
});
}
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
@@ -334,13 +336,6 @@ export const identityOidcAuthServiceFactory = ({
},
tx
);
await identityDAL.updateById(
identityMembershipOrg.identityId,
{
authMethod: IdentityAuthMethod.OIDC_AUTH
},
tx
);
return doc;
});
return { ...identityOidcAuth, orgId: identityMembershipOrg.orgId, caCert };
@@ -368,7 +363,7 @@ export const identityOidcAuthServiceFactory = ({
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
}
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.OIDC_AUTH) {
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OIDC_AUTH)) {
throw new BadRequestError({
message: "Failed to update OIDC Auth"
});
@@ -471,7 +466,7 @@ export const identityOidcAuthServiceFactory = ({
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
}
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.OIDC_AUTH) {
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OIDC_AUTH)) {
throw new BadRequestError({
message: "The identity does not have OIDC Auth attached"
});
@@ -519,7 +514,7 @@ export const identityOidcAuthServiceFactory = ({
throw new NotFoundError({ message: "Failed to find identity" });
}
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.OIDC_AUTH) {
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OIDC_AUTH)) {
throw new BadRequestError({
message: "The identity does not have OIDC auth"
});
@@ -551,7 +546,7 @@ export const identityOidcAuthServiceFactory = ({
const revokedIdentityOidcAuth = await identityOidcAuthDAL.transaction(async (tx) => {
const deletedOidcAuth = await identityOidcAuthDAL.delete({ identityId }, tx);
await identityDAL.updateById(identityId, { authMethod: null }, tx);
// await identityDAL.updateById(identityId, { authMethod: null }, tx);
return { ...deletedOidcAuth?.[0], orgId: identityMembershipOrg.orgId };
});
@@ -11,7 +11,7 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityDALFactory } from "../identity/identity-dal";
// import { TIdentityDALFactory } from "../identity/identity-dal";
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
@@ -32,11 +32,11 @@ type TIdentityTokenAuthServiceFactoryDep = {
TIdentityTokenAuthDALFactory,
"transaction" | "create" | "findOne" | "updateById" | "delete"
>;
identityDAL: Pick<TIdentityDALFactory, "updateById">;
// identityDAL: Pick<TIdentityDALFactory, "updateById">;
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
identityAccessTokenDAL: Pick<
TIdentityAccessTokenDALFactory,
"create" | "find" | "update" | "findById" | "findOne" | "updateById"
"create" | "find" | "update" | "findById" | "findOne" | "updateById" | "delete"
>;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
@@ -46,7 +46,7 @@ export type TIdentityTokenAuthServiceFactory = ReturnType<typeof identityTokenAu
export const identityTokenAuthServiceFactory = ({
identityTokenAuthDAL,
identityDAL,
// identityDAL,
identityOrgMembershipDAL,
identityAccessTokenDAL,
permissionService,
@@ -65,10 +65,12 @@ export const identityTokenAuthServiceFactory = ({
}: TAttachTokenAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity.authMethod)
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) {
throw new BadRequestError({
message: "Failed to add Token Auth to already configured identity"
});
}
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
@@ -112,13 +114,6 @@ export const identityTokenAuthServiceFactory = ({
},
tx
);
await identityDAL.updateById(
identityMembershipOrg.identityId,
{
authMethod: IdentityAuthMethod.TOKEN_AUTH
},
tx
);
return doc;
});
return { ...identityTokenAuth, orgId: identityMembershipOrg.orgId };
@@ -137,10 +132,12 @@ export const identityTokenAuthServiceFactory = ({
}: TUpdateTokenAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.TOKEN_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) {
throw new BadRequestError({
message: "Failed to update Token Auth"
});
}
const identityTokenAuth = await identityTokenAuthDAL.findOne({ identityId });
@@ -197,10 +194,12 @@ export const identityTokenAuthServiceFactory = ({
const getTokenAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetTokenAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.TOKEN_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) {
throw new BadRequestError({
message: "The identity does not have Token Auth attached"
});
}
const identityTokenAuth = await identityTokenAuthDAL.findOne({ identityId });
@@ -225,10 +224,12 @@ export const identityTokenAuthServiceFactory = ({
}: TRevokeTokenAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.TOKEN_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) {
throw new BadRequestError({
message: "The identity does not have Token Auth"
});
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
@@ -254,7 +255,12 @@ export const identityTokenAuthServiceFactory = ({
const revokedIdentityTokenAuth = await identityTokenAuthDAL.transaction(async (tx) => {
const deletedTokenAuth = await identityTokenAuthDAL.delete({ identityId }, tx);
await identityDAL.updateById(identityId, { authMethod: null }, tx);
await identityAccessTokenDAL.delete({
identityId,
authMethod: IdentityAuthMethod.TOKEN_AUTH
});
// await identityDAL.updateById(identityId, { authMethod: null }, tx);
return { ...deletedTokenAuth?.[0], orgId: identityMembershipOrg.orgId };
});
return revokedIdentityTokenAuth;
@@ -270,10 +276,12 @@ export const identityTokenAuthServiceFactory = ({
}: TCreateTokenAuthTokenDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.TOKEN_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) {
throw new BadRequestError({
message: "The identity does not have Token Auth"
});
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
@@ -307,7 +315,8 @@ export const identityTokenAuthServiceFactory = ({
accessTokenMaxTTL: identityTokenAuth.accessTokenMaxTTL,
accessTokenNumUses: 0,
accessTokenNumUsesLimit: identityTokenAuth.accessTokenNumUsesLimit,
name
name,
authMethod: IdentityAuthMethod.TOKEN_AUTH
},
tx
);
@@ -344,10 +353,12 @@ export const identityTokenAuthServiceFactory = ({
}: TGetTokenAuthTokensDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.TOKEN_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) {
throw new BadRequestError({
message: "The identity does not have Token Auth"
});
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
@@ -381,10 +392,11 @@ export const identityTokenAuthServiceFactory = ({
if (!identityMembershipOrg) {
throw new NotFoundError({ message: `Failed to find identity with ID ${foundToken.identityId}` });
}
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.TOKEN_AUTH)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) {
throw new BadRequestError({
message: "The identity does not have Token Auth"
});
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
@@ -14,7 +14,7 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedErro
import { checkIPAgainstBlocklist, extractIPDetails, isValidIpOrCidr, TIp } from "@app/lib/ip";
import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityDALFactory } from "../identity/identity-dal";
// import { TIdentityDALFactory } from "../identity/identity-dal";
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
@@ -36,7 +36,7 @@ type TIdentityUaServiceFactoryDep = {
identityUaClientSecretDAL: TIdentityUaClientSecretDALFactory;
identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
identityOrgMembershipDAL: TIdentityOrgDALFactory;
identityDAL: Pick<TIdentityDALFactory, "updateById">;
// identityDAL: Pick<TIdentityDALFactory, "updateById">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
};
@@ -48,7 +48,7 @@ export const identityUaServiceFactory = ({
identityUaClientSecretDAL,
identityAccessTokenDAL,
identityOrgMembershipDAL,
identityDAL,
// identityDAL,
permissionService,
licenseService
}: TIdentityUaServiceFactoryDep) => {
@@ -115,7 +115,8 @@ export const identityUaServiceFactory = ({
accessTokenTTL: identityUa.accessTokenTTL,
accessTokenMaxTTL: identityUa.accessTokenMaxTTL,
accessTokenNumUses: 0,
accessTokenNumUsesLimit: identityUa.accessTokenNumUsesLimit
accessTokenNumUsesLimit: identityUa.accessTokenNumUsesLimit,
authMethod: IdentityAuthMethod.UNIVERSAL_AUTH
},
tx
);
@@ -156,10 +157,12 @@ export const identityUaServiceFactory = ({
}: TAttachUaDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity.authMethod)
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.UNIVERSAL_AUTH)) {
throw new BadRequestError({
message: "Failed to add universal auth to already configured identity"
});
}
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
@@ -221,13 +224,6 @@ export const identityUaServiceFactory = ({
},
tx
);
await identityDAL.updateById(
identityMembershipOrg.identityId,
{
authMethod: IdentityAuthMethod.Univeral
},
tx
);
return doc;
});
return { ...identityUa, orgId: identityMembershipOrg.orgId };
@@ -247,10 +243,12 @@ export const identityUaServiceFactory = ({
}: TUpdateUaDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.UNIVERSAL_AUTH)) {
throw new BadRequestError({
message: "Failed to updated universal auth"
message: "Failed to update universal auth"
});
}
const uaIdentityAuth = await identityUaDAL.findOne({ identityId });
@@ -321,10 +319,12 @@ export const identityUaServiceFactory = ({
const getIdentityUniversalAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetUaDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.UNIVERSAL_AUTH)) {
throw new BadRequestError({
message: "The identity does not have universal auth"
});
}
const uaIdentityAuth = await identityUaDAL.findOne({ identityId });
@@ -348,10 +348,12 @@ export const identityUaServiceFactory = ({
}: TRevokeUaDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.UNIVERSAL_AUTH)) {
throw new BadRequestError({
message: "The identity does not have universal auth"
});
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
@@ -375,7 +377,7 @@ export const identityUaServiceFactory = ({
const revokedIdentityUniversalAuth = await identityUaDAL.transaction(async (tx) => {
const deletedUniversalAuth = await identityUaDAL.delete({ identityId }, tx);
await identityDAL.updateById(identityId, { authMethod: null }, tx);
// await identityDAL.updateById(identityId, { authMethod: null }, tx);
return { ...deletedUniversalAuth?.[0], orgId: identityMembershipOrg.orgId };
});
return revokedIdentityUniversalAuth;
@@ -393,10 +395,13 @@ export const identityUaServiceFactory = ({
}: TCreateUaClientSecretDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.UNIVERSAL_AUTH)) {
throw new BadRequestError({
message: "The identity does not have universal auth"
});
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
@@ -422,12 +427,11 @@ export const identityUaServiceFactory = ({
const appCfg = getConfig();
const clientSecret = crypto.randomBytes(32).toString("hex");
const clientSecretHash = await bcrypt.hash(clientSecret, appCfg.SALT_ROUNDS);
const identityUniversalAuth = await identityUaDAL.findOne({
identityId
});
const identityUaAuth = await identityUaDAL.findOne({ identityId: identityMembershipOrg.identityId });
const identityUaClientSecret = await identityUaClientSecretDAL.create({
identityUAId: identityUniversalAuth.id,
identityUAId: identityUaAuth.id,
description,
clientSecretPrefix: clientSecret.slice(0, 4),
clientSecretHash,
@@ -439,7 +443,6 @@ export const identityUaServiceFactory = ({
return {
clientSecret,
clientSecretData: identityUaClientSecret,
uaAuth: identityUniversalAuth,
orgId: identityMembershipOrg.orgId
};
};
@@ -453,10 +456,12 @@ export const identityUaServiceFactory = ({
}: TGetUaClientSecretsDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.UNIVERSAL_AUTH)) {
throw new BadRequestError({
message: "The identity does not have universal auth"
});
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
@@ -500,10 +505,13 @@ export const identityUaServiceFactory = ({
}: TGetUniversalAuthClientSecretByIdDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.UNIVERSAL_AUTH)) {
throw new BadRequestError({
message: "The identity does not have universal auth"
});
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
@@ -539,10 +547,13 @@ export const identityUaServiceFactory = ({
}: TRevokeUaClientSecretDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.UNIVERSAL_AUTH)) {
throw new BadRequestError({
message: "The identity does not have universal auth"
});
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
+180 -19
View File
@@ -1,12 +1,52 @@
import { Knex } from "knex";
import { TDbClient } from "@app/db";
import { TableName, TIdentityOrgMemberships, TOrgRoles } from "@app/db/schemas";
import {
IdentityAuthMethod,
TableName,
TIdentityAwsAuths,
TIdentityAzureAuths,
TIdentityGcpAuths,
TIdentityKubernetesAuths,
TIdentityOidcAuths,
TIdentityOrgMemberships,
TIdentityTokenAuths,
TIdentityUniversalAuths,
TOrgRoles
} from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
import { OrderByDirection } from "@app/lib/types";
import { OrgIdentityOrderBy, TListOrgIdentitiesByOrgIdDTO } from "@app/services/identity/identity-types";
const buildAuthMethods = ({
uaId,
gcpId,
awsId,
kubernetesId,
oidcId,
azureId,
tokenId
}: {
uaId?: string;
gcpId?: string;
awsId?: string;
kubernetesId?: string;
oidcId?: string;
azureId?: string;
tokenId?: string;
}) => {
return [
...(uaId ? [IdentityAuthMethod.UNIVERSAL_AUTH] : []),
...(gcpId ? [IdentityAuthMethod.GCP_AUTH] : []),
...(awsId ? [IdentityAuthMethod.AWS_AUTH] : []),
...(kubernetesId ? [IdentityAuthMethod.KUBERNETES_AUTH] : []),
...(oidcId ? [IdentityAuthMethod.OIDC_AUTH] : []),
...(azureId ? [IdentityAuthMethod.AZURE_AUTH] : []),
...(tokenId ? [IdentityAuthMethod.TOKEN_AUTH] : [])
].filter((authMethod) => authMethod);
};
export type TIdentityOrgDALFactory = ReturnType<typeof identityOrgDALFactory>;
export const identityOrgDALFactory = (db: TDbClient) => {
@@ -15,14 +55,73 @@ export const identityOrgDALFactory = (db: TDbClient) => {
const findOne = async (filter: Partial<TIdentityOrgMemberships>, tx?: Knex) => {
try {
const [data] = await (tx || db.replicaNode())(TableName.IdentityOrgMembership)
.where(filter)
.where((queryBuilder) => {
Object.entries(filter).forEach(([key, value]) => {
void queryBuilder.where(`${TableName.IdentityOrgMembership}.${key}`, value);
});
})
.join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`)
.select(selectAllTableCols(TableName.IdentityOrgMembership))
.select(db.ref("name").withSchema(TableName.Identity))
.select(db.ref("authMethod").withSchema(TableName.Identity));
.leftJoin<TIdentityUniversalAuths>(
TableName.IdentityUniversalAuth,
`${TableName.IdentityOrgMembership}.identityId`,
`${TableName.IdentityUniversalAuth}.identityId`
)
.leftJoin<TIdentityGcpAuths>(
TableName.IdentityGcpAuth,
`${TableName.IdentityOrgMembership}.identityId`,
`${TableName.IdentityGcpAuth}.identityId`
)
.leftJoin<TIdentityAwsAuths>(
TableName.IdentityAwsAuth,
`${TableName.IdentityOrgMembership}.identityId`,
`${TableName.IdentityAwsAuth}.identityId`
)
.leftJoin<TIdentityKubernetesAuths>(
TableName.IdentityKubernetesAuth,
`${TableName.IdentityOrgMembership}.identityId`,
`${TableName.IdentityKubernetesAuth}.identityId`
)
.leftJoin<TIdentityOidcAuths>(
TableName.IdentityOidcAuth,
`${TableName.IdentityOrgMembership}.identityId`,
`${TableName.IdentityOidcAuth}.identityId`
)
.leftJoin<TIdentityAzureAuths>(
TableName.IdentityAzureAuth,
`${TableName.IdentityOrgMembership}.identityId`,
`${TableName.IdentityAzureAuth}.identityId`
)
.leftJoin<TIdentityTokenAuths>(
TableName.IdentityTokenAuth,
`${TableName.IdentityOrgMembership}.identityId`,
`${TableName.IdentityTokenAuth}.identityId`
)
.select(
selectAllTableCols(TableName.IdentityOrgMembership),
db.ref("id").as("uaId").withSchema(TableName.IdentityUniversalAuth),
db.ref("id").as("gcpId").withSchema(TableName.IdentityGcpAuth),
db.ref("id").as("awsId").withSchema(TableName.IdentityAwsAuth),
db.ref("id").as("kubernetesId").withSchema(TableName.IdentityKubernetesAuth),
db.ref("id").as("oidcId").withSchema(TableName.IdentityOidcAuth),
db.ref("id").as("azureId").withSchema(TableName.IdentityAzureAuth),
db.ref("id").as("tokenId").withSchema(TableName.IdentityTokenAuth),
db.ref("name").withSchema(TableName.Identity)
);
if (data) {
const { name, authMethod } = data;
return { ...data, identity: { id: data.identityId, name, authMethod } };
const { name } = data;
return {
...data,
identity: {
id: data.identityId,
name,
authMethods: buildAuthMethods(data)
}
};
}
} catch (error) {
throw new DatabaseError({ error, name: "FindOne" });
@@ -42,7 +141,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
tx?: Knex
) => {
try {
const paginatedIdentity = (tx || db.replicaNode())(TableName.Identity)
const paginatedIdentitySubquery = (tx || db.replicaNode())(TableName.Identity)
.join(
TableName.IdentityOrgMembership,
`${TableName.IdentityOrgMembership}.identityId`,
@@ -51,30 +150,70 @@ export const identityOrgDALFactory = (db: TDbClient) => {
.orderBy(`${TableName.Identity}.${orderBy}`, orderDirection)
.select(
selectAllTableCols(TableName.IdentityOrgMembership),
db.ref("name").withSchema(TableName.Identity).as("identityName"),
db.ref("authMethod").withSchema(TableName.Identity).as("identityAuthMethod")
db.ref("name").withSchema(TableName.Identity).as("identityName")
// db.ref("authMethod").withSchema(TableName.Identity).as("identityAuthMethod")
)
.where(filter)
.as("paginatedIdentity");
if (search?.length) {
void paginatedIdentity.whereILike(`${TableName.Identity}.name`, `%${search}%`);
void paginatedIdentitySubquery.whereILike(`${TableName.Identity}.name`, `%${search}%`);
}
if (limit) {
void paginatedIdentity.offset(offset).limit(limit);
void paginatedIdentitySubquery.offset(offset).limit(limit);
}
const paginatedIdentity = paginatedIdentitySubquery.as("paginatedIdentity");
// akhilmhdh: refer this for pagination with multiple left queries
type TSubquery = Awaited<typeof paginatedIdentity>;
const query = (tx || db.replicaNode())
.from<TSubquery[number], TSubquery>(paginatedIdentity)
.leftJoin<TOrgRoles>(TableName.OrgRoles, `paginatedIdentity.roleId`, `${TableName.OrgRoles}.id`)
.leftJoin(TableName.IdentityMetadata, (queryBuilder) => {
void queryBuilder
.on(`paginatedIdentity.identityId`, `${TableName.IdentityMetadata}.identityId`)
.andOn(`paginatedIdentity.orgId`, `${TableName.IdentityMetadata}.orgId`);
})
.leftJoin<TIdentityUniversalAuths>(
TableName.IdentityUniversalAuth,
"paginatedIdentity.identityId",
`${TableName.IdentityUniversalAuth}.identityId`
)
.leftJoin<TIdentityGcpAuths>(
TableName.IdentityGcpAuth,
"paginatedIdentity.identityId",
`${TableName.IdentityGcpAuth}.identityId`
)
.leftJoin<TIdentityAwsAuths>(
TableName.IdentityAwsAuth,
"paginatedIdentity.identityId",
`${TableName.IdentityAwsAuth}.identityId`
)
.leftJoin<TIdentityKubernetesAuths>(
TableName.IdentityKubernetesAuth,
"paginatedIdentity.identityId",
`${TableName.IdentityKubernetesAuth}.identityId`
)
.leftJoin<TIdentityOidcAuths>(
TableName.IdentityOidcAuth,
"paginatedIdentity.identityId",
`${TableName.IdentityOidcAuth}.identityId`
)
.leftJoin<TIdentityAzureAuths>(
TableName.IdentityAzureAuth,
"paginatedIdentity.identityId",
`${TableName.IdentityAzureAuth}.identityId`
)
.leftJoin<TIdentityTokenAuths>(
TableName.IdentityTokenAuth,
"paginatedIdentity.identityId",
`${TableName.IdentityTokenAuth}.identityId`
)
.select(
db.ref("id").withSchema("paginatedIdentity"),
db.ref("role").withSchema("paginatedIdentity"),
@@ -82,9 +221,16 @@ export const identityOrgDALFactory = (db: TDbClient) => {
db.ref("orgId").withSchema("paginatedIdentity"),
db.ref("createdAt").withSchema("paginatedIdentity"),
db.ref("updatedAt").withSchema("paginatedIdentity"),
db.ref("identityId").withSchema("paginatedIdentity"),
db.ref("identityId").withSchema("paginatedIdentity").as("identityNewId"),
db.ref("identityName").withSchema("paginatedIdentity"),
db.ref("identityAuthMethod").withSchema("paginatedIdentity")
db.ref("id").as("uaId").withSchema(TableName.IdentityUniversalAuth),
db.ref("id").as("gcpId").withSchema(TableName.IdentityGcpAuth),
db.ref("id").as("awsId").withSchema(TableName.IdentityAwsAuth),
db.ref("id").as("kubernetesId").withSchema(TableName.IdentityKubernetesAuth),
db.ref("id").as("oidcId").withSchema(TableName.IdentityOidcAuth),
db.ref("id").as("azureId").withSchema(TableName.IdentityAzureAuth),
db.ref("id").as("tokenId").withSchema(TableName.IdentityTokenAuth)
)
// cr stands for custom role
.select(db.ref("id").as("crId").withSchema(TableName.OrgRoles))
@@ -112,20 +258,27 @@ export const identityOrgDALFactory = (db: TDbClient) => {
crSlug,
crPermission,
crName,
identityId,
identityNewId,
identityName,
identityAuthMethod,
role,
roleId,
id,
orgId,
uaId,
awsId,
gcpId,
kubernetesId,
oidcId,
azureId,
tokenId,
createdAt,
updatedAt
}) => ({
role,
roleId,
identityId,
identityId: identityNewId,
id,
orgId,
createdAt,
updatedAt,
@@ -139,9 +292,17 @@ export const identityOrgDALFactory = (db: TDbClient) => {
}
: undefined,
identity: {
id: identityId,
id: identityNewId,
name: identityName,
authMethod: identityAuthMethod as string
authMethods: buildAuthMethods({
uaId,
awsId,
gcpId,
kubernetesId,
oidcId,
azureId,
tokenId
})
}
}),
childrenMapper: [