mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-05 06:26:27 +00:00
Merge pull request #2606 from Infisical/daniel/multiple-auth-methods
feat: multiple auth methods for identities
This commit is contained in:
@@ -34,7 +34,7 @@ describe("Identity v1", async () => {
|
|||||||
test("Create identity", async () => {
|
test("Create identity", async () => {
|
||||||
const newIdentity = await createIdentity("mac1", OrgMembershipRole.Admin);
|
const newIdentity = await createIdentity("mac1", OrgMembershipRole.Admin);
|
||||||
expect(newIdentity.name).toBe("mac1");
|
expect(newIdentity.name).toBe("mac1");
|
||||||
expect(newIdentity.authMethod).toBeNull();
|
expect(newIdentity.authMethods).toEqual([]);
|
||||||
|
|
||||||
await deleteIdentity(newIdentity.id);
|
await deleteIdentity(newIdentity.id);
|
||||||
});
|
});
|
||||||
@@ -42,7 +42,7 @@ describe("Identity v1", async () => {
|
|||||||
test("Update identity", async () => {
|
test("Update identity", async () => {
|
||||||
const newIdentity = await createIdentity("mac1", OrgMembershipRole.Admin);
|
const newIdentity = await createIdentity("mac1", OrgMembershipRole.Admin);
|
||||||
expect(newIdentity.name).toBe("mac1");
|
expect(newIdentity.name).toBe("mac1");
|
||||||
expect(newIdentity.authMethod).toBeNull();
|
expect(newIdentity.authMethods).toEqual([]);
|
||||||
|
|
||||||
const updatedIdentity = await testServer.inject({
|
const updatedIdentity = await testServer.inject({
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
|
|||||||
Generated
+1
-1
@@ -51,7 +51,7 @@
|
|||||||
"connect-redis": "^7.1.1",
|
"connect-redis": "^7.1.1",
|
||||||
"cron": "^3.1.7",
|
"cron": "^3.1.7",
|
||||||
"dotenv": "^16.4.1",
|
"dotenv": "^16.4.1",
|
||||||
"fastify": "^4.26.0",
|
"fastify": "^4.28.1",
|
||||||
"fastify-plugin": "^4.5.1",
|
"fastify-plugin": "^4.5.1",
|
||||||
"google-auth-library": "^9.9.0",
|
"google-auth-library": "^9.9.0",
|
||||||
"googleapis": "^137.1.0",
|
"googleapis": "^137.1.0",
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
"test:e2e-watch": "vitest -c vitest.e2e.config.ts --bail=1",
|
"test:e2e-watch": "vitest -c vitest.e2e.config.ts --bail=1",
|
||||||
"test:e2e-coverage": "vitest run --coverage -c vitest.e2e.config.ts",
|
"test:e2e-coverage": "vitest run --coverage -c vitest.e2e.config.ts",
|
||||||
"generate:component": "tsx ./scripts/create-backend-file.ts",
|
"generate:component": "tsx ./scripts/create-backend-file.ts",
|
||||||
"generate:schema": "tsx ./scripts/generate-schema-types.ts",
|
"generate:schema": "tsx ./scripts/generate-schema-types.ts && eslint --fix --ext ts ./src/db/schemas",
|
||||||
"auditlog-migration:latest": "knex --knexfile ./src/db/auditlog-knexfile.ts --client pg migrate:latest",
|
"auditlog-migration:latest": "knex --knexfile ./src/db/auditlog-knexfile.ts --client pg migrate:latest",
|
||||||
"auditlog-migration:up": "knex --knexfile ./src/db/auditlog-knexfile.ts --client pg migrate:up",
|
"auditlog-migration:up": "knex --knexfile ./src/db/auditlog-knexfile.ts --client pg migrate:up",
|
||||||
"auditlog-migration:down": "knex --knexfile ./src/db/auditlog-knexfile.ts --client pg migrate:down",
|
"auditlog-migration:down": "knex --knexfile ./src/db/auditlog-knexfile.ts --client pg migrate:down",
|
||||||
@@ -156,7 +156,7 @@
|
|||||||
"connect-redis": "^7.1.1",
|
"connect-redis": "^7.1.1",
|
||||||
"cron": "^3.1.7",
|
"cron": "^3.1.7",
|
||||||
"dotenv": "^16.4.1",
|
"dotenv": "^16.4.1",
|
||||||
"fastify": "^4.26.0",
|
"fastify": "^4.28.1",
|
||||||
"fastify-plugin": "^4.5.1",
|
"fastify-plugin": "^4.5.1",
|
||||||
"google-auth-library": "^9.9.0",
|
"google-auth-library": "^9.9.0",
|
||||||
"googleapis": "^137.1.0",
|
"googleapis": "^137.1.0",
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
const BATCH_SIZE = 10_000;
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
|
let nullableAccessTokens = await knex(TableName.IdentityAccessToken).whereNull("authMethod").limit(BATCH_SIZE);
|
||||||
|
let totalUpdated = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
const batchIds = nullableAccessTokens.map((token) => token.id);
|
||||||
|
|
||||||
|
// ! Update the auth method column in batches for the current batch
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await knex(TableName.IdentityAccessToken)
|
||||||
|
.whereIn("id", batchIds)
|
||||||
|
.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()
|
||||||
|
});
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
nullableAccessTokens = await knex(TableName.IdentityAccessToken).whereNull("authMethod").limit(BATCH_SIZE);
|
||||||
|
|
||||||
|
totalUpdated += batchIds.length;
|
||||||
|
console.log(`Updated ${batchIds.length} access tokens in batch <> Total updated: ${totalUpdated}`);
|
||||||
|
} while (nullableAccessTokens.length > 0);
|
||||||
|
|
||||||
|
// ! 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");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,8 @@ export const IdentityAccessTokensSchema = z.object({
|
|||||||
identityId: z.string().uuid(),
|
identityId: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: 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>;
|
export type TIdentityAccessTokens = z.infer<typeof IdentityAccessTokensSchema>;
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ export enum ProjectUpgradeStatus {
|
|||||||
|
|
||||||
export enum IdentityAuthMethod {
|
export enum IdentityAuthMethod {
|
||||||
TOKEN_AUTH = "token-auth",
|
TOKEN_AUTH = "token-auth",
|
||||||
Univeral = "universal-auth",
|
UNIVERSAL_AUTH = "universal-auth",
|
||||||
KUBERNETES_AUTH = "kubernetes-auth",
|
KUBERNETES_AUTH = "kubernetes-auth",
|
||||||
GCP_AUTH = "gcp-auth",
|
GCP_AUTH = "gcp-auth",
|
||||||
AWS_AUTH = "aws-auth",
|
AWS_AUTH = "aws-auth",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
id: seedData1.machineIdentity.id,
|
id: seedData1.machineIdentity.id,
|
||||||
name: seedData1.machineIdentity.name,
|
name: seedData1.machineIdentity.name,
|
||||||
authMethod: IdentityAuthMethod.Univeral
|
authMethod: IdentityAuthMethod.UNIVERSAL_AUTH
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
const identityUa = await knex(TableName.IdentityUniversalAuth)
|
const identityUa = await knex(TableName.IdentityUniversalAuth)
|
||||||
|
|||||||
@@ -1087,7 +1087,6 @@ export const registerRoutes = async (
|
|||||||
|
|
||||||
const identityTokenAuthService = identityTokenAuthServiceFactory({
|
const identityTokenAuthService = identityTokenAuthServiceFactory({
|
||||||
identityTokenAuthDAL,
|
identityTokenAuthDAL,
|
||||||
identityDAL,
|
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
@@ -1096,7 +1095,6 @@ export const registerRoutes = async (
|
|||||||
const identityUaService = identityUaServiceFactory({
|
const identityUaService = identityUaServiceFactory({
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
identityDAL,
|
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityUaClientSecretDAL,
|
identityUaClientSecretDAL,
|
||||||
identityUaDAL,
|
identityUaDAL,
|
||||||
@@ -1106,7 +1104,6 @@ export const registerRoutes = async (
|
|||||||
identityKubernetesAuthDAL,
|
identityKubernetesAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityDAL,
|
|
||||||
orgBotDAL,
|
orgBotDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService
|
licenseService
|
||||||
@@ -1115,7 +1112,6 @@ export const registerRoutes = async (
|
|||||||
identityGcpAuthDAL,
|
identityGcpAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityDAL,
|
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService
|
licenseService
|
||||||
});
|
});
|
||||||
@@ -1124,7 +1120,6 @@ export const registerRoutes = async (
|
|||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityAwsAuthDAL,
|
identityAwsAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
identityDAL,
|
|
||||||
licenseService,
|
licenseService,
|
||||||
permissionService
|
permissionService
|
||||||
});
|
});
|
||||||
@@ -1133,7 +1128,6 @@ export const registerRoutes = async (
|
|||||||
identityAzureAuthDAL,
|
identityAzureAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityDAL,
|
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService
|
licenseService
|
||||||
});
|
});
|
||||||
@@ -1142,7 +1136,6 @@ export const registerRoutes = async (
|
|||||||
identityOidcAuthDAL,
|
identityOidcAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityDAL,
|
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService,
|
||||||
orgBotDAL
|
orgBotDAL
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
identity: IdentitiesSchema
|
identity: IdentitiesSchema.extend({
|
||||||
|
authMethods: z.array(z.string())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -216,7 +218,9 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
|||||||
permissions: true,
|
permissions: true,
|
||||||
description: true
|
description: true
|
||||||
}).optional(),
|
}).optional(),
|
||||||
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true })
|
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
|
||||||
|
authMethods: z.array(z.string())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -261,7 +265,9 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
|||||||
permissions: true,
|
permissions: true,
|
||||||
description: true
|
description: true
|
||||||
}).optional(),
|
}).optional(),
|
||||||
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true })
|
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
|
||||||
|
authMethods: z.array(z.string())
|
||||||
|
})
|
||||||
}).array(),
|
}).array(),
|
||||||
totalCount: z.number()
|
totalCount: z.number()
|
||||||
})
|
})
|
||||||
@@ -319,7 +325,9 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
|||||||
temporaryAccessEndTime: z.date().nullable().optional()
|
temporaryAccessEndTime: z.date().nullable().optional()
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true }),
|
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
|
||||||
|
authMethods: z.array(z.string())
|
||||||
|
}),
|
||||||
project: SanitizedProjectSchema.pick({ name: true, id: true })
|
project: SanitizedProjectSchema.pick({ name: true, id: true })
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -58,7 +58,9 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
permissions: true,
|
permissions: true,
|
||||||
description: true
|
description: true
|
||||||
}).optional(),
|
}).optional(),
|
||||||
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true })
|
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
|
||||||
|
authMethods: z.array(z.string())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
).array(),
|
).array(),
|
||||||
totalCount: z.number()
|
totalCount: z.number()
|
||||||
|
|||||||
@@ -264,7 +264,9 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
|||||||
temporaryAccessEndTime: z.date().nullable().optional()
|
temporaryAccessEndTime: z.date().nullable().optional()
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true }),
|
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
|
||||||
|
authMethods: z.array(z.string())
|
||||||
|
}),
|
||||||
project: SanitizedProjectSchema.pick({ name: true, id: true })
|
project: SanitizedProjectSchema.pick({ name: true, id: true })
|
||||||
})
|
})
|
||||||
.array(),
|
.array(),
|
||||||
@@ -285,6 +287,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
|||||||
orderDirection: req.query.orderDirection,
|
orderDirection: req.query.orderDirection,
|
||||||
search: req.query.search
|
search: req.query.search
|
||||||
});
|
});
|
||||||
|
|
||||||
return { identityMemberships, totalCount };
|
return { identityMemberships, totalCount };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -328,7 +331,9 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
|||||||
temporaryAccessEndTime: z.date().nullable().optional()
|
temporaryAccessEndTime: z.date().nullable().optional()
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true }),
|
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
|
||||||
|
authMethods: z.array(z.string())
|
||||||
|
}),
|
||||||
project: SanitizedProjectSchema.pick({ name: true, id: true })
|
project: SanitizedProjectSchema.pick({ name: true, id: true })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
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 { DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
@@ -17,54 +17,27 @@ export const identityAccessTokenDALFactory = (db: TDbClient) => {
|
|||||||
const doc = await (tx || db.replicaNode())(TableName.IdentityAccessToken)
|
const doc = await (tx || db.replicaNode())(TableName.IdentityAccessToken)
|
||||||
.where(filter)
|
.where(filter)
|
||||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.IdentityAccessToken}.identityId`)
|
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.IdentityAccessToken}.identityId`)
|
||||||
.leftJoin(TableName.IdentityUaClientSecret, (qb) => {
|
.leftJoin(
|
||||||
qb.on(`${TableName.Identity}.authMethod`, db.raw("?", [IdentityAuthMethod.Univeral])).andOn(
|
TableName.IdentityUaClientSecret,
|
||||||
`${TableName.IdentityAccessToken}.identityUAClientSecretId`,
|
`${TableName.IdentityAccessToken}.identityUAClientSecretId`,
|
||||||
`${TableName.IdentityUaClientSecret}.id`
|
`${TableName.IdentityUaClientSecret}.id`
|
||||||
);
|
)
|
||||||
})
|
.leftJoin(
|
||||||
.leftJoin(TableName.IdentityUniversalAuth, (qb) => {
|
TableName.IdentityUniversalAuth,
|
||||||
qb.on(`${TableName.Identity}.authMethod`, db.raw("?", [IdentityAuthMethod.Univeral])).andOn(
|
|
||||||
`${TableName.IdentityUaClientSecret}.identityUAId`,
|
`${TableName.IdentityUaClientSecret}.identityUAId`,
|
||||||
`${TableName.IdentityUniversalAuth}.id`
|
`${TableName.IdentityUniversalAuth}.id`
|
||||||
);
|
)
|
||||||
})
|
.leftJoin(TableName.IdentityGcpAuth, `${TableName.Identity}.id`, `${TableName.IdentityGcpAuth}.identityId`)
|
||||||
.leftJoin(TableName.IdentityGcpAuth, (qb) => {
|
.leftJoin(TableName.IdentityAwsAuth, `${TableName.Identity}.id`, `${TableName.IdentityAwsAuth}.identityId`)
|
||||||
qb.on(`${TableName.Identity}.authMethod`, db.raw("?", [IdentityAuthMethod.GCP_AUTH])).andOn(
|
.leftJoin(TableName.IdentityAzureAuth, `${TableName.Identity}.id`, `${TableName.IdentityAzureAuth}.identityId`)
|
||||||
`${TableName.Identity}.id`,
|
.leftJoin(
|
||||||
`${TableName.IdentityGcpAuth}.identityId`
|
TableName.IdentityKubernetesAuth,
|
||||||
);
|
|
||||||
})
|
|
||||||
.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.Identity}.id`,
|
||||||
`${TableName.IdentityKubernetesAuth}.identityId`
|
`${TableName.IdentityKubernetesAuth}.identityId`
|
||||||
);
|
)
|
||||||
})
|
.leftJoin(TableName.IdentityOidcAuth, `${TableName.Identity}.id`, `${TableName.IdentityOidcAuth}.identityId`)
|
||||||
.leftJoin(TableName.IdentityOidcAuth, (qb) => {
|
.leftJoin(TableName.IdentityTokenAuth, `${TableName.Identity}.id`, `${TableName.IdentityTokenAuth}.identityId`)
|
||||||
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`
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.select(selectAllTableCols(TableName.IdentityAccessToken))
|
.select(selectAllTableCols(TableName.IdentityAccessToken))
|
||||||
.select(
|
.select(
|
||||||
db.ref("accessTokenTrustedIps").withSchema(TableName.IdentityUniversalAuth).as("accessTokenTrustedIpsUa"),
|
db.ref("accessTokenTrustedIps").withSchema(TableName.IdentityUniversalAuth).as("accessTokenTrustedIpsUa"),
|
||||||
@@ -82,14 +55,13 @@ export const identityAccessTokenDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...doc,
|
...doc,
|
||||||
accessTokenTrustedIps:
|
trustedIpsUniversalAuth: doc.accessTokenTrustedIpsUa,
|
||||||
doc.accessTokenTrustedIpsUa ||
|
trustedIpsGcpAuth: doc.accessTokenTrustedIpsGcp,
|
||||||
doc.accessTokenTrustedIpsGcp ||
|
trustedIpsAwsAuth: doc.accessTokenTrustedIpsAws,
|
||||||
doc.accessTokenTrustedIpsAws ||
|
trustedIpsAzureAuth: doc.accessTokenTrustedIpsAzure,
|
||||||
doc.accessTokenTrustedIpsAzure ||
|
trustedIpsKubernetesAuth: doc.accessTokenTrustedIpsK8s,
|
||||||
doc.accessTokenTrustedIpsK8s ||
|
trustedIpsOidcAuth: doc.accessTokenTrustedIpsOidc,
|
||||||
doc.accessTokenTrustedIpsOidc ||
|
trustedIpsAccessTokenAuth: doc.accessTokenTrustedIpsToken
|
||||||
doc.accessTokenTrustedIpsToken
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "IdAccessTokenFindOne" });
|
throw new DatabaseError({ error, name: "IdAccessTokenFindOne" });
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import jwt, { JwtPayload } from "jsonwebtoken";
|
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 { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { checkIPAgainstBlocklist, TIp } from "@app/lib/ip";
|
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"
|
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({
|
checkIPAgainstBlocklist({
|
||||||
ipAddress,
|
ipAddress,
|
||||||
trustedIps: identityAccessToken?.accessTokenTrustedIps as TIp[]
|
trustedIps: trustedIps as TIp[]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedErro
|
|||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
|
|
||||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
|
||||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||||
@@ -33,7 +32,6 @@ type TIdentityAwsAuthServiceFactoryDep = {
|
|||||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
|
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
|
||||||
identityAwsAuthDAL: Pick<TIdentityAwsAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">;
|
identityAwsAuthDAL: Pick<TIdentityAwsAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">;
|
||||||
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
|
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
|
||||||
identityDAL: Pick<TIdentityDALFactory, "updateById">;
|
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
};
|
};
|
||||||
@@ -44,7 +42,6 @@ export const identityAwsAuthServiceFactory = ({
|
|||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityAwsAuthDAL,
|
identityAwsAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
identityDAL,
|
|
||||||
licenseService,
|
licenseService,
|
||||||
permissionService
|
permissionService
|
||||||
}: TIdentityAwsAuthServiceFactoryDep) => {
|
}: TIdentityAwsAuthServiceFactoryDep) => {
|
||||||
@@ -113,7 +110,8 @@ export const identityAwsAuthServiceFactory = ({
|
|||||||
accessTokenTTL: identityAwsAuth.accessTokenTTL,
|
accessTokenTTL: identityAwsAuth.accessTokenTTL,
|
||||||
accessTokenMaxTTL: identityAwsAuth.accessTokenMaxTTL,
|
accessTokenMaxTTL: identityAwsAuth.accessTokenMaxTTL,
|
||||||
accessTokenNumUses: 0,
|
accessTokenNumUses: 0,
|
||||||
accessTokenNumUsesLimit: identityAwsAuth.accessTokenNumUsesLimit
|
accessTokenNumUsesLimit: identityAwsAuth.accessTokenNumUsesLimit,
|
||||||
|
authMethod: IdentityAuthMethod.AWS_AUTH
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -155,10 +153,12 @@ export const identityAwsAuthServiceFactory = ({
|
|||||||
}: TAttachAwsAuthDTO) => {
|
}: TAttachAwsAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "Failed to add AWS Auth to already configured identity"
|
message: "Failed to add AWS Auth to already configured identity"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
||||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||||
@@ -206,13 +206,6 @@ export const identityAwsAuthServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
await identityDAL.updateById(
|
|
||||||
identityMembershipOrg.identityId,
|
|
||||||
{
|
|
||||||
authMethod: IdentityAuthMethod.AWS_AUTH
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
return doc;
|
return doc;
|
||||||
});
|
});
|
||||||
return { ...identityAwsAuth, orgId: identityMembershipOrg.orgId };
|
return { ...identityAwsAuth, orgId: identityMembershipOrg.orgId };
|
||||||
@@ -234,10 +227,12 @@ export const identityAwsAuthServiceFactory = ({
|
|||||||
}: TUpdateAwsAuthDTO) => {
|
}: TUpdateAwsAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||||
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.AWS_AUTH)
|
|
||||||
throw new BadRequestError({
|
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AWS_AUTH)) {
|
||||||
message: "Failed to update AWS Auth"
|
throw new NotFoundError({
|
||||||
|
message: "The identity does not have AWS Auth attached"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const identityAwsAuth = await identityAwsAuthDAL.findOne({ identityId });
|
const identityAwsAuth = await identityAwsAuthDAL.findOne({ identityId });
|
||||||
|
|
||||||
@@ -293,10 +288,12 @@ export const identityAwsAuthServiceFactory = ({
|
|||||||
const getAwsAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetAwsAuthDTO) => {
|
const getAwsAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetAwsAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have AWS Auth attached"
|
message: "The identity does not have AWS Auth attached"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const awsIdentityAuth = await identityAwsAuthDAL.findOne({ identityId });
|
const awsIdentityAuth = await identityAwsAuthDAL.findOne({ identityId });
|
||||||
|
|
||||||
@@ -320,10 +317,11 @@ export const identityAwsAuthServiceFactory = ({
|
|||||||
}: TRevokeAwsAuthDTO) => {
|
}: TRevokeAwsAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have aws auth"
|
message: "The identity does not have aws auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -348,7 +346,6 @@ export const identityAwsAuthServiceFactory = ({
|
|||||||
|
|
||||||
const revokedIdentityAwsAuth = await identityAwsAuthDAL.transaction(async (tx) => {
|
const revokedIdentityAwsAuth = await identityAwsAuthDAL.transaction(async (tx) => {
|
||||||
const deletedAwsAuth = await identityAwsAuthDAL.delete({ identityId }, tx);
|
const deletedAwsAuth = await identityAwsAuthDAL.delete({ identityId }, tx);
|
||||||
await identityDAL.updateById(identityId, { authMethod: null }, tx);
|
|
||||||
return { ...deletedAwsAuth?.[0], orgId: identityMembershipOrg.orgId };
|
return { ...deletedAwsAuth?.[0], orgId: identityMembershipOrg.orgId };
|
||||||
});
|
});
|
||||||
return revokedIdentityAwsAuth;
|
return revokedIdentityAwsAuth;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedErro
|
|||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
|
|
||||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
|
||||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||||
@@ -32,7 +31,6 @@ type TIdentityAzureAuthServiceFactoryDep = {
|
|||||||
>;
|
>;
|
||||||
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
|
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
|
||||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
|
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
|
||||||
identityDAL: Pick<TIdentityDALFactory, "updateById">;
|
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
};
|
};
|
||||||
@@ -43,7 +41,6 @@ export const identityAzureAuthServiceFactory = ({
|
|||||||
identityAzureAuthDAL,
|
identityAzureAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityDAL,
|
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService
|
licenseService
|
||||||
}: TIdentityAzureAuthServiceFactoryDep) => {
|
}: TIdentityAzureAuthServiceFactoryDep) => {
|
||||||
@@ -84,7 +81,8 @@ export const identityAzureAuthServiceFactory = ({
|
|||||||
accessTokenTTL: identityAzureAuth.accessTokenTTL,
|
accessTokenTTL: identityAzureAuth.accessTokenTTL,
|
||||||
accessTokenMaxTTL: identityAzureAuth.accessTokenMaxTTL,
|
accessTokenMaxTTL: identityAzureAuth.accessTokenMaxTTL,
|
||||||
accessTokenNumUses: 0,
|
accessTokenNumUses: 0,
|
||||||
accessTokenNumUsesLimit: identityAzureAuth.accessTokenNumUsesLimit
|
accessTokenNumUsesLimit: identityAzureAuth.accessTokenNumUsesLimit,
|
||||||
|
authMethod: IdentityAuthMethod.AZURE_AUTH
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -126,11 +124,12 @@ export const identityAzureAuthServiceFactory = ({
|
|||||||
}: TAttachAzureAuthDTO) => {
|
}: TAttachAzureAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "Failed to add Azure Auth to already configured identity"
|
message: "Failed to add Azure Auth to already configured identity"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
||||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||||
}
|
}
|
||||||
@@ -176,13 +175,7 @@ export const identityAzureAuthServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
await identityDAL.updateById(
|
|
||||||
identityMembershipOrg.identityId,
|
|
||||||
{
|
|
||||||
authMethod: IdentityAuthMethod.AZURE_AUTH
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
return doc;
|
return doc;
|
||||||
});
|
});
|
||||||
return { ...identityAzureAuth, orgId: identityMembershipOrg.orgId };
|
return { ...identityAzureAuth, orgId: identityMembershipOrg.orgId };
|
||||||
@@ -204,10 +197,11 @@ export const identityAzureAuthServiceFactory = ({
|
|||||||
}: TUpdateAzureAuthDTO) => {
|
}: TUpdateAzureAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "Failed to update Azure Auth"
|
message: "Failed to update Azure Auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const identityGcpAuth = await identityAzureAuthDAL.findOne({ identityId });
|
const identityGcpAuth = await identityAzureAuthDAL.findOne({ identityId });
|
||||||
|
|
||||||
@@ -266,10 +260,11 @@ export const identityAzureAuthServiceFactory = ({
|
|||||||
const getAzureAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetAzureAuthDTO) => {
|
const getAzureAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetAzureAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have Azure Auth attached"
|
message: "The identity does not have Azure Auth attached"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const identityAzureAuth = await identityAzureAuthDAL.findOne({ identityId });
|
const identityAzureAuth = await identityAzureAuthDAL.findOne({ identityId });
|
||||||
|
|
||||||
@@ -294,10 +289,11 @@ export const identityAzureAuthServiceFactory = ({
|
|||||||
}: TRevokeAzureAuthDTO) => {
|
}: TRevokeAzureAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have azure auth"
|
message: "The identity does not have azure auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -321,7 +317,6 @@ export const identityAzureAuthServiceFactory = ({
|
|||||||
|
|
||||||
const revokedIdentityAzureAuth = await identityAzureAuthDAL.transaction(async (tx) => {
|
const revokedIdentityAzureAuth = await identityAzureAuthDAL.transaction(async (tx) => {
|
||||||
const deletedAzureAuth = await identityAzureAuthDAL.delete({ identityId }, tx);
|
const deletedAzureAuth = await identityAzureAuthDAL.delete({ identityId }, tx);
|
||||||
await identityDAL.updateById(identityId, { authMethod: null }, tx);
|
|
||||||
return { ...deletedAzureAuth?.[0], orgId: identityMembershipOrg.orgId };
|
return { ...deletedAzureAuth?.[0], orgId: identityMembershipOrg.orgId };
|
||||||
});
|
});
|
||||||
return revokedIdentityAzureAuth;
|
return revokedIdentityAzureAuth;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedErro
|
|||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
|
|
||||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
|
||||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||||
@@ -30,7 +29,6 @@ type TIdentityGcpAuthServiceFactoryDep = {
|
|||||||
identityGcpAuthDAL: Pick<TIdentityGcpAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">;
|
identityGcpAuthDAL: Pick<TIdentityGcpAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">;
|
||||||
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
|
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
|
||||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
|
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
|
||||||
identityDAL: Pick<TIdentityDALFactory, "updateById">;
|
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
};
|
};
|
||||||
@@ -41,7 +39,6 @@ export const identityGcpAuthServiceFactory = ({
|
|||||||
identityGcpAuthDAL,
|
identityGcpAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityDAL,
|
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService
|
licenseService
|
||||||
}: TIdentityGcpAuthServiceFactoryDep) => {
|
}: TIdentityGcpAuthServiceFactoryDep) => {
|
||||||
@@ -125,7 +122,8 @@ export const identityGcpAuthServiceFactory = ({
|
|||||||
accessTokenTTL: identityGcpAuth.accessTokenTTL,
|
accessTokenTTL: identityGcpAuth.accessTokenTTL,
|
||||||
accessTokenMaxTTL: identityGcpAuth.accessTokenMaxTTL,
|
accessTokenMaxTTL: identityGcpAuth.accessTokenMaxTTL,
|
||||||
accessTokenNumUses: 0,
|
accessTokenNumUses: 0,
|
||||||
accessTokenNumUsesLimit: identityGcpAuth.accessTokenNumUsesLimit
|
accessTokenNumUsesLimit: identityGcpAuth.accessTokenNumUsesLimit,
|
||||||
|
authMethod: IdentityAuthMethod.GCP_AUTH
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -168,10 +166,12 @@ export const identityGcpAuthServiceFactory = ({
|
|||||||
}: TAttachGcpAuthDTO) => {
|
}: TAttachGcpAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "Failed to add GCP Auth to already configured identity"
|
message: "Failed to add GCP Auth to already configured identity"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
||||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||||
@@ -219,13 +219,6 @@ export const identityGcpAuthServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
await identityDAL.updateById(
|
|
||||||
identityMembershipOrg.identityId,
|
|
||||||
{
|
|
||||||
authMethod: IdentityAuthMethod.GCP_AUTH
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
return doc;
|
return doc;
|
||||||
});
|
});
|
||||||
return { ...identityGcpAuth, orgId: identityMembershipOrg.orgId };
|
return { ...identityGcpAuth, orgId: identityMembershipOrg.orgId };
|
||||||
@@ -248,10 +241,12 @@ export const identityGcpAuthServiceFactory = ({
|
|||||||
}: TUpdateGcpAuthDTO) => {
|
}: TUpdateGcpAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "Failed to update GCP Auth"
|
message: "Failed to update GCP Auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const identityGcpAuth = await identityGcpAuthDAL.findOne({ identityId });
|
const identityGcpAuth = await identityGcpAuthDAL.findOne({ identityId });
|
||||||
|
|
||||||
@@ -311,10 +306,12 @@ export const identityGcpAuthServiceFactory = ({
|
|||||||
const getGcpAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetGcpAuthDTO) => {
|
const getGcpAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetGcpAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have GCP Auth attached"
|
message: "The identity does not have GCP Auth attached"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const identityGcpAuth = await identityGcpAuthDAL.findOne({ identityId });
|
const identityGcpAuth = await identityGcpAuthDAL.findOne({ identityId });
|
||||||
|
|
||||||
@@ -339,10 +336,12 @@ export const identityGcpAuthServiceFactory = ({
|
|||||||
}: TRevokeGcpAuthDTO) => {
|
}: TRevokeGcpAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have gcp auth"
|
message: "The identity does not have gcp auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -366,7 +365,6 @@ export const identityGcpAuthServiceFactory = ({
|
|||||||
|
|
||||||
const revokedIdentityGcpAuth = await identityGcpAuthDAL.transaction(async (tx) => {
|
const revokedIdentityGcpAuth = await identityGcpAuthDAL.transaction(async (tx) => {
|
||||||
const deletedGcpAuth = await identityGcpAuthDAL.delete({ identityId }, tx);
|
const deletedGcpAuth = await identityGcpAuthDAL.delete({ identityId }, tx);
|
||||||
await identityDAL.updateById(identityId, { authMethod: null }, tx);
|
|
||||||
return { ...deletedGcpAuth?.[0], orgId: identityMembershipOrg.orgId };
|
return { ...deletedGcpAuth?.[0], orgId: identityMembershipOrg.orgId };
|
||||||
});
|
});
|
||||||
return revokedIdentityGcpAuth;
|
return revokedIdentityGcpAuth;
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
|||||||
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
|
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
|
||||||
|
|
||||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
|
||||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||||
@@ -44,7 +43,6 @@ type TIdentityKubernetesAuthServiceFactoryDep = {
|
|||||||
>;
|
>;
|
||||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
|
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
|
||||||
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne" | "findById">;
|
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne" | "findById">;
|
||||||
identityDAL: Pick<TIdentityDALFactory, "updateById">;
|
|
||||||
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "transaction" | "create">;
|
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "transaction" | "create">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
@@ -56,7 +54,6 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
identityKubernetesAuthDAL,
|
identityKubernetesAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityDAL,
|
|
||||||
orgBotDAL,
|
orgBotDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService
|
licenseService
|
||||||
@@ -215,7 +212,8 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
accessTokenTTL: identityKubernetesAuth.accessTokenTTL,
|
accessTokenTTL: identityKubernetesAuth.accessTokenTTL,
|
||||||
accessTokenMaxTTL: identityKubernetesAuth.accessTokenMaxTTL,
|
accessTokenMaxTTL: identityKubernetesAuth.accessTokenMaxTTL,
|
||||||
accessTokenNumUses: 0,
|
accessTokenNumUses: 0,
|
||||||
accessTokenNumUsesLimit: identityKubernetesAuth.accessTokenNumUsesLimit
|
accessTokenNumUsesLimit: identityKubernetesAuth.accessTokenNumUsesLimit,
|
||||||
|
authMethod: IdentityAuthMethod.KUBERNETES_AUTH
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -260,10 +258,12 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
}: TAttachKubernetesAuthDTO) => {
|
}: TAttachKubernetesAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "Failed to add Kubernetes Auth to already configured identity"
|
message: "Failed to add Kubernetes Auth to already configured identity"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
||||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||||
@@ -372,13 +372,6 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
await identityDAL.updateById(
|
|
||||||
identityMembershipOrg.identityId,
|
|
||||||
{
|
|
||||||
authMethod: IdentityAuthMethod.KUBERNETES_AUTH
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
return doc;
|
return doc;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -404,10 +397,12 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
}: TUpdateKubernetesAuthDTO) => {
|
}: TUpdateKubernetesAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "Failed to update Kubernetes Auth"
|
message: "Failed to update Kubernetes Auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const identityKubernetesAuth = await identityKubernetesAuthDAL.findOne({ identityId });
|
const identityKubernetesAuth = await identityKubernetesAuthDAL.findOne({ identityId });
|
||||||
|
|
||||||
@@ -532,11 +527,12 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
}: TGetKubernetesAuthDTO) => {
|
}: TGetKubernetesAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have Kubernetes Auth attached"
|
message: "The identity does not have Kubernetes Auth attached"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
const identityKubernetesAuth = await identityKubernetesAuthDAL.findOne({ identityId });
|
const identityKubernetesAuth = await identityKubernetesAuthDAL.findOne({ identityId });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
@@ -597,10 +593,12 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
}: TRevokeKubernetesAuthDTO) => {
|
}: TRevokeKubernetesAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have kubernetes auth"
|
message: "The identity does not have kubernetes auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -624,7 +622,6 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
|
|
||||||
const revokedIdentityKubernetesAuth = await identityKubernetesAuthDAL.transaction(async (tx) => {
|
const revokedIdentityKubernetesAuth = await identityKubernetesAuthDAL.transaction(async (tx) => {
|
||||||
const deletedKubernetesAuth = await identityKubernetesAuthDAL.delete({ identityId }, tx);
|
const deletedKubernetesAuth = await identityKubernetesAuthDAL.delete({ identityId }, tx);
|
||||||
await identityDAL.updateById(identityId, { authMethod: null }, tx);
|
|
||||||
return { ...deletedKubernetesAuth?.[0], orgId: identityMembershipOrg.orgId };
|
return { ...deletedKubernetesAuth?.[0], orgId: identityMembershipOrg.orgId };
|
||||||
});
|
});
|
||||||
return revokedIdentityKubernetesAuth;
|
return revokedIdentityKubernetesAuth;
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedErro
|
|||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
|
|
||||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
|
||||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||||
@@ -41,7 +40,6 @@ type TIdentityOidcAuthServiceFactoryDep = {
|
|||||||
identityOidcAuthDAL: TIdentityOidcAuthDALFactory;
|
identityOidcAuthDAL: TIdentityOidcAuthDALFactory;
|
||||||
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
|
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
|
||||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
|
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
|
||||||
identityDAL: Pick<TIdentityDALFactory, "updateById">;
|
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "transaction" | "create">;
|
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "transaction" | "create">;
|
||||||
@@ -52,7 +50,6 @@ export type TIdentityOidcAuthServiceFactory = ReturnType<typeof identityOidcAuth
|
|||||||
export const identityOidcAuthServiceFactory = ({
|
export const identityOidcAuthServiceFactory = ({
|
||||||
identityOidcAuthDAL,
|
identityOidcAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
identityDAL,
|
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
@@ -61,7 +58,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
const login = async ({ identityId, jwt: oidcJwt }: TLoginOidcAuthDTO) => {
|
const login = async ({ identityId, jwt: oidcJwt }: TLoginOidcAuthDTO) => {
|
||||||
const identityOidcAuth = await identityOidcAuthDAL.findOne({ identityId });
|
const identityOidcAuth = await identityOidcAuthDAL.findOne({ identityId });
|
||||||
if (!identityOidcAuth) {
|
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({
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({
|
||||||
@@ -181,7 +178,8 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
accessTokenTTL: identityOidcAuth.accessTokenTTL,
|
accessTokenTTL: identityOidcAuth.accessTokenTTL,
|
||||||
accessTokenMaxTTL: identityOidcAuth.accessTokenMaxTTL,
|
accessTokenMaxTTL: identityOidcAuth.accessTokenMaxTTL,
|
||||||
accessTokenNumUses: 0,
|
accessTokenNumUses: 0,
|
||||||
accessTokenNumUsesLimit: identityOidcAuth.accessTokenNumUsesLimit
|
accessTokenNumUsesLimit: identityOidcAuth.accessTokenNumUsesLimit,
|
||||||
|
authMethod: IdentityAuthMethod.OIDC_AUTH
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -228,10 +226,11 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
if (!identityMembershipOrg) {
|
if (!identityMembershipOrg) {
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
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({
|
throw new BadRequestError({
|
||||||
message: "Failed to add OIDC Auth to already configured identity"
|
message: "Failed to add OIDC Auth to already configured identity"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
||||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||||
@@ -334,13 +333,6 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
await identityDAL.updateById(
|
|
||||||
identityMembershipOrg.identityId,
|
|
||||||
{
|
|
||||||
authMethod: IdentityAuthMethod.OIDC_AUTH
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
return doc;
|
return doc;
|
||||||
});
|
});
|
||||||
return { ...identityOidcAuth, orgId: identityMembershipOrg.orgId, caCert };
|
return { ...identityOidcAuth, orgId: identityMembershipOrg.orgId, caCert };
|
||||||
@@ -364,11 +356,9 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
actorOrgId
|
actorOrgId
|
||||||
}: TUpdateOidcAuthDTO) => {
|
}: TUpdateOidcAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) {
|
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
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({
|
throw new BadRequestError({
|
||||||
message: "Failed to update OIDC Auth"
|
message: "Failed to update OIDC Auth"
|
||||||
});
|
});
|
||||||
@@ -467,11 +457,9 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
|
|
||||||
const getOidcAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetOidcAuthDTO) => {
|
const getOidcAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetOidcAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) {
|
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have OIDC Auth attached"
|
message: "The identity does not have OIDC Auth attached"
|
||||||
});
|
});
|
||||||
@@ -519,7 +507,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: "Failed to find identity" });
|
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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have OIDC auth"
|
message: "The identity does not have OIDC auth"
|
||||||
});
|
});
|
||||||
@@ -551,7 +539,6 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
|
|
||||||
const revokedIdentityOidcAuth = await identityOidcAuthDAL.transaction(async (tx) => {
|
const revokedIdentityOidcAuth = await identityOidcAuthDAL.transaction(async (tx) => {
|
||||||
const deletedOidcAuth = await identityOidcAuthDAL.delete({ identityId }, tx);
|
const deletedOidcAuth = await identityOidcAuthDAL.delete({ identityId }, tx);
|
||||||
await identityDAL.updateById(identityId, { authMethod: null }, tx);
|
|
||||||
return { ...deletedOidcAuth?.[0], orgId: identityMembershipOrg.orgId };
|
return { ...deletedOidcAuth?.[0], orgId: identityMembershipOrg.orgId };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,24 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName, TIdentities } from "@app/db/schemas";
|
import {
|
||||||
|
TableName,
|
||||||
|
TIdentities,
|
||||||
|
TIdentityAwsAuths,
|
||||||
|
TIdentityAzureAuths,
|
||||||
|
TIdentityGcpAuths,
|
||||||
|
TIdentityKubernetesAuths,
|
||||||
|
TIdentityOidcAuths,
|
||||||
|
TIdentityTokenAuths,
|
||||||
|
TIdentityUniversalAuths
|
||||||
|
} from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
|
import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
|
||||||
import { OrderByDirection } from "@app/lib/types";
|
import { OrderByDirection } from "@app/lib/types";
|
||||||
import { ProjectIdentityOrderBy, TListProjectIdentityDTO } from "@app/services/identity-project/identity-project-types";
|
import { ProjectIdentityOrderBy, TListProjectIdentityDTO } from "@app/services/identity-project/identity-project-types";
|
||||||
|
|
||||||
|
import { buildAuthMethods } from "../identity/identity-fns";
|
||||||
|
|
||||||
export type TIdentityProjectDALFactory = ReturnType<typeof identityProjectDALFactory>;
|
export type TIdentityProjectDALFactory = ReturnType<typeof identityProjectDALFactory>;
|
||||||
|
|
||||||
export const identityProjectDALFactory = (db: TDbClient) => {
|
export const identityProjectDALFactory = (db: TDbClient) => {
|
||||||
@@ -33,11 +45,48 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.IdentityProjectMembership}.id`,
|
`${TableName.IdentityProjectMembership}.id`,
|
||||||
`${TableName.IdentityProjectAdditionalPrivilege}.projectMembershipId`
|
`${TableName.IdentityProjectAdditionalPrivilege}.projectMembershipId`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
.leftJoin(
|
||||||
|
TableName.IdentityUniversalAuth,
|
||||||
|
`${TableName.IdentityProjectMembership}.identityId`,
|
||||||
|
`${TableName.IdentityUniversalAuth}.identityId`
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.IdentityGcpAuth,
|
||||||
|
`${TableName.IdentityProjectMembership}.identityId`,
|
||||||
|
`${TableName.IdentityGcpAuth}.identityId`
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.IdentityAwsAuth,
|
||||||
|
`${TableName.IdentityProjectMembership}.identityId`,
|
||||||
|
`${TableName.IdentityAwsAuth}.identityId`
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.IdentityKubernetesAuth,
|
||||||
|
`${TableName.IdentityProjectMembership}.identityId`,
|
||||||
|
`${TableName.IdentityKubernetesAuth}.identityId`
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.IdentityOidcAuth,
|
||||||
|
`${TableName.IdentityProjectMembership}.identityId`,
|
||||||
|
`${TableName.IdentityOidcAuth}.identityId`
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.IdentityAzureAuth,
|
||||||
|
`${TableName.IdentityProjectMembership}.identityId`,
|
||||||
|
`${TableName.IdentityAzureAuth}.identityId`
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.IdentityTokenAuth,
|
||||||
|
`${TableName.IdentityProjectMembership}.identityId`,
|
||||||
|
`${TableName.IdentityTokenAuth}.identityId`
|
||||||
|
)
|
||||||
|
|
||||||
.select(
|
.select(
|
||||||
db.ref("id").withSchema(TableName.IdentityProjectMembership),
|
db.ref("id").withSchema(TableName.IdentityProjectMembership),
|
||||||
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership),
|
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership),
|
||||||
db.ref("updatedAt").withSchema(TableName.IdentityProjectMembership),
|
db.ref("updatedAt").withSchema(TableName.IdentityProjectMembership),
|
||||||
db.ref("authMethod").as("identityAuthMethod").withSchema(TableName.Identity),
|
|
||||||
db.ref("id").as("identityId").withSchema(TableName.Identity),
|
db.ref("id").as("identityId").withSchema(TableName.Identity),
|
||||||
db.ref("name").as("identityName").withSchema(TableName.Identity),
|
db.ref("name").as("identityName").withSchema(TableName.Identity),
|
||||||
db.ref("id").withSchema(TableName.IdentityProjectMembership),
|
db.ref("id").withSchema(TableName.IdentityProjectMembership),
|
||||||
@@ -52,12 +101,33 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("temporaryAccessStartTime").withSchema(TableName.IdentityProjectMembershipRole),
|
db.ref("temporaryAccessStartTime").withSchema(TableName.IdentityProjectMembershipRole),
|
||||||
db.ref("temporaryAccessEndTime").withSchema(TableName.IdentityProjectMembershipRole),
|
db.ref("temporaryAccessEndTime").withSchema(TableName.IdentityProjectMembershipRole),
|
||||||
db.ref("projectId").withSchema(TableName.IdentityProjectMembership),
|
db.ref("projectId").withSchema(TableName.IdentityProjectMembership),
|
||||||
db.ref("name").as("projectName").withSchema(TableName.Project)
|
db.ref("name").as("projectName").withSchema(TableName.Project),
|
||||||
|
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)
|
||||||
);
|
);
|
||||||
|
|
||||||
const members = sqlNestRelationships({
|
const members = sqlNestRelationships({
|
||||||
data: docs,
|
data: docs,
|
||||||
parentMapper: ({ identityName, identityAuthMethod, id, createdAt, updatedAt, projectId, projectName }) => ({
|
parentMapper: ({
|
||||||
|
identityName,
|
||||||
|
uaId,
|
||||||
|
awsId,
|
||||||
|
gcpId,
|
||||||
|
kubernetesId,
|
||||||
|
oidcId,
|
||||||
|
azureId,
|
||||||
|
tokenId,
|
||||||
|
id,
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
projectId,
|
||||||
|
projectName
|
||||||
|
}) => ({
|
||||||
id,
|
id,
|
||||||
identityId,
|
identityId,
|
||||||
createdAt,
|
createdAt,
|
||||||
@@ -65,7 +135,15 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
|||||||
identity: {
|
identity: {
|
||||||
id: identityId,
|
id: identityId,
|
||||||
name: identityName,
|
name: identityName,
|
||||||
authMethod: identityAuthMethod
|
authMethods: buildAuthMethods({
|
||||||
|
uaId,
|
||||||
|
awsId,
|
||||||
|
gcpId,
|
||||||
|
kubernetesId,
|
||||||
|
oidcId,
|
||||||
|
azureId,
|
||||||
|
tokenId
|
||||||
|
})
|
||||||
},
|
},
|
||||||
project: {
|
project: {
|
||||||
id: projectId,
|
id: projectId,
|
||||||
@@ -150,7 +228,7 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
|||||||
})
|
})
|
||||||
.where((qb) => {
|
.where((qb) => {
|
||||||
if (filter.identityId) {
|
if (filter.identityId) {
|
||||||
void qb.where("identityId", filter.identityId);
|
void qb.where(`${TableName.IdentityProjectMembership}.identityId`, filter.identityId);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.join(
|
.join(
|
||||||
@@ -168,6 +246,43 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.IdentityProjectMembership}.id`,
|
`${TableName.IdentityProjectMembership}.id`,
|
||||||
`${TableName.IdentityProjectAdditionalPrivilege}.projectMembershipId`
|
`${TableName.IdentityProjectAdditionalPrivilege}.projectMembershipId`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
.leftJoin<TIdentityUniversalAuths>(
|
||||||
|
TableName.IdentityUniversalAuth,
|
||||||
|
`${TableName.Identity}.id`,
|
||||||
|
`${TableName.IdentityUniversalAuth}.identityId`
|
||||||
|
)
|
||||||
|
.leftJoin<TIdentityGcpAuths>(
|
||||||
|
TableName.IdentityGcpAuth,
|
||||||
|
`${TableName.Identity}.id`,
|
||||||
|
`${TableName.IdentityGcpAuth}.identityId`
|
||||||
|
)
|
||||||
|
.leftJoin<TIdentityAwsAuths>(
|
||||||
|
TableName.IdentityAwsAuth,
|
||||||
|
`${TableName.Identity}.id`,
|
||||||
|
`${TableName.IdentityAwsAuth}.identityId`
|
||||||
|
)
|
||||||
|
.leftJoin<TIdentityKubernetesAuths>(
|
||||||
|
TableName.IdentityKubernetesAuth,
|
||||||
|
`${TableName.Identity}.id`,
|
||||||
|
`${TableName.IdentityKubernetesAuth}.identityId`
|
||||||
|
)
|
||||||
|
.leftJoin<TIdentityOidcAuths>(
|
||||||
|
TableName.IdentityOidcAuth,
|
||||||
|
`${TableName.Identity}.id`,
|
||||||
|
`${TableName.IdentityOidcAuth}.identityId`
|
||||||
|
)
|
||||||
|
.leftJoin<TIdentityAzureAuths>(
|
||||||
|
TableName.IdentityAzureAuth,
|
||||||
|
`${TableName.Identity}.id`,
|
||||||
|
`${TableName.IdentityAzureAuth}.identityId`
|
||||||
|
)
|
||||||
|
.leftJoin<TIdentityTokenAuths>(
|
||||||
|
TableName.IdentityTokenAuth,
|
||||||
|
`${TableName.Identity}.id`,
|
||||||
|
`${TableName.IdentityTokenAuth}.identityId`
|
||||||
|
)
|
||||||
|
|
||||||
.select(
|
.select(
|
||||||
db.ref("id").withSchema(TableName.IdentityProjectMembership),
|
db.ref("id").withSchema(TableName.IdentityProjectMembership),
|
||||||
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership),
|
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership),
|
||||||
@@ -186,7 +301,14 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("temporaryRange").withSchema(TableName.IdentityProjectMembershipRole),
|
db.ref("temporaryRange").withSchema(TableName.IdentityProjectMembershipRole),
|
||||||
db.ref("temporaryAccessStartTime").withSchema(TableName.IdentityProjectMembershipRole),
|
db.ref("temporaryAccessStartTime").withSchema(TableName.IdentityProjectMembershipRole),
|
||||||
db.ref("temporaryAccessEndTime").withSchema(TableName.IdentityProjectMembershipRole),
|
db.ref("temporaryAccessEndTime").withSchema(TableName.IdentityProjectMembershipRole),
|
||||||
db.ref("name").as("projectName").withSchema(TableName.Project)
|
db.ref("name").as("projectName").withSchema(TableName.Project),
|
||||||
|
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)
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: scott - joins seem to reorder identities so need to order again, for the sake of urgency will optimize at a later point
|
// TODO: scott - joins seem to reorder identities so need to order again, for the sake of urgency will optimize at a later point
|
||||||
@@ -204,7 +326,21 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const members = sqlNestRelationships({
|
const members = sqlNestRelationships({
|
||||||
data: docs,
|
data: docs,
|
||||||
parentMapper: ({ identityId, identityName, identityAuthMethod, id, createdAt, updatedAt, projectName }) => ({
|
parentMapper: ({
|
||||||
|
identityId,
|
||||||
|
identityName,
|
||||||
|
uaId,
|
||||||
|
awsId,
|
||||||
|
gcpId,
|
||||||
|
kubernetesId,
|
||||||
|
oidcId,
|
||||||
|
azureId,
|
||||||
|
tokenId,
|
||||||
|
id,
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
projectName
|
||||||
|
}) => ({
|
||||||
id,
|
id,
|
||||||
identityId,
|
identityId,
|
||||||
createdAt,
|
createdAt,
|
||||||
@@ -212,7 +348,15 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
|||||||
identity: {
|
identity: {
|
||||||
id: identityId,
|
id: identityId,
|
||||||
name: identityName,
|
name: identityName,
|
||||||
authMethod: identityAuthMethod
|
authMethods: buildAuthMethods({
|
||||||
|
uaId,
|
||||||
|
awsId,
|
||||||
|
gcpId,
|
||||||
|
kubernetesId,
|
||||||
|
oidcId,
|
||||||
|
azureId,
|
||||||
|
tokenId
|
||||||
|
})
|
||||||
},
|
},
|
||||||
project: {
|
project: {
|
||||||
id: projectId,
|
id: projectId,
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/
|
|||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
|
|
||||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
|
||||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||||
@@ -32,11 +31,10 @@ type TIdentityTokenAuthServiceFactoryDep = {
|
|||||||
TIdentityTokenAuthDALFactory,
|
TIdentityTokenAuthDALFactory,
|
||||||
"transaction" | "create" | "findOne" | "updateById" | "delete"
|
"transaction" | "create" | "findOne" | "updateById" | "delete"
|
||||||
>;
|
>;
|
||||||
identityDAL: Pick<TIdentityDALFactory, "updateById">;
|
|
||||||
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
|
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
|
||||||
identityAccessTokenDAL: Pick<
|
identityAccessTokenDAL: Pick<
|
||||||
TIdentityAccessTokenDALFactory,
|
TIdentityAccessTokenDALFactory,
|
||||||
"create" | "find" | "update" | "findById" | "findOne" | "updateById"
|
"create" | "find" | "update" | "findById" | "findOne" | "updateById" | "delete"
|
||||||
>;
|
>;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
@@ -46,7 +44,7 @@ export type TIdentityTokenAuthServiceFactory = ReturnType<typeof identityTokenAu
|
|||||||
|
|
||||||
export const identityTokenAuthServiceFactory = ({
|
export const identityTokenAuthServiceFactory = ({
|
||||||
identityTokenAuthDAL,
|
identityTokenAuthDAL,
|
||||||
identityDAL,
|
// identityDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
@@ -65,10 +63,12 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
}: TAttachTokenAuthDTO) => {
|
}: TAttachTokenAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "Failed to add Token Auth to already configured identity"
|
message: "Failed to add Token Auth to already configured identity"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
||||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||||
@@ -112,13 +112,6 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
await identityDAL.updateById(
|
|
||||||
identityMembershipOrg.identityId,
|
|
||||||
{
|
|
||||||
authMethod: IdentityAuthMethod.TOKEN_AUTH
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
return doc;
|
return doc;
|
||||||
});
|
});
|
||||||
return { ...identityTokenAuth, orgId: identityMembershipOrg.orgId };
|
return { ...identityTokenAuth, orgId: identityMembershipOrg.orgId };
|
||||||
@@ -137,10 +130,12 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
}: TUpdateTokenAuthDTO) => {
|
}: TUpdateTokenAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "Failed to update Token Auth"
|
message: "The identity does not have token auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const identityTokenAuth = await identityTokenAuthDAL.findOne({ identityId });
|
const identityTokenAuth = await identityTokenAuthDAL.findOne({ identityId });
|
||||||
|
|
||||||
@@ -197,10 +192,12 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
const getTokenAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetTokenAuthDTO) => {
|
const getTokenAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetTokenAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have Token Auth attached"
|
message: "The identity does not have Token Auth attached"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const identityTokenAuth = await identityTokenAuthDAL.findOne({ identityId });
|
const identityTokenAuth = await identityTokenAuthDAL.findOne({ identityId });
|
||||||
|
|
||||||
@@ -225,10 +222,12 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
}: TRevokeTokenAuthDTO) => {
|
}: TRevokeTokenAuthDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have Token Auth"
|
message: "The identity does not have Token Auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -254,7 +253,11 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
|
|
||||||
const revokedIdentityTokenAuth = await identityTokenAuthDAL.transaction(async (tx) => {
|
const revokedIdentityTokenAuth = await identityTokenAuthDAL.transaction(async (tx) => {
|
||||||
const deletedTokenAuth = await identityTokenAuthDAL.delete({ identityId }, tx);
|
const deletedTokenAuth = await identityTokenAuthDAL.delete({ identityId }, tx);
|
||||||
await identityDAL.updateById(identityId, { authMethod: null }, tx);
|
await identityAccessTokenDAL.delete({
|
||||||
|
identityId,
|
||||||
|
authMethod: IdentityAuthMethod.TOKEN_AUTH
|
||||||
|
});
|
||||||
|
|
||||||
return { ...deletedTokenAuth?.[0], orgId: identityMembershipOrg.orgId };
|
return { ...deletedTokenAuth?.[0], orgId: identityMembershipOrg.orgId };
|
||||||
});
|
});
|
||||||
return revokedIdentityTokenAuth;
|
return revokedIdentityTokenAuth;
|
||||||
@@ -270,10 +273,12 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
}: TCreateTokenAuthTokenDTO) => {
|
}: TCreateTokenAuthTokenDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have Token Auth"
|
message: "The identity does not have Token Auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -307,7 +312,8 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
accessTokenMaxTTL: identityTokenAuth.accessTokenMaxTTL,
|
accessTokenMaxTTL: identityTokenAuth.accessTokenMaxTTL,
|
||||||
accessTokenNumUses: 0,
|
accessTokenNumUses: 0,
|
||||||
accessTokenNumUsesLimit: identityTokenAuth.accessTokenNumUsesLimit,
|
accessTokenNumUsesLimit: identityTokenAuth.accessTokenNumUsesLimit,
|
||||||
name
|
name,
|
||||||
|
authMethod: IdentityAuthMethod.TOKEN_AUTH
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -344,10 +350,12 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
}: TGetTokenAuthTokensDTO) => {
|
}: TGetTokenAuthTokensDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have Token Auth"
|
message: "The identity does not have Token Auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -359,7 +367,8 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
|
|
||||||
const tokens = await identityAccessTokenDAL.find(
|
const tokens = await identityAccessTokenDAL.find(
|
||||||
{
|
{
|
||||||
identityId
|
identityId,
|
||||||
|
authMethod: IdentityAuthMethod.TOKEN_AUTH
|
||||||
},
|
},
|
||||||
{ offset, limit, sort: [["updatedAt", "desc"]] }
|
{ offset, limit, sort: [["updatedAt", "desc"]] }
|
||||||
);
|
);
|
||||||
@@ -375,16 +384,21 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TUpdateTokenAuthTokenDTO) => {
|
}: TUpdateTokenAuthTokenDTO) => {
|
||||||
const foundToken = await identityAccessTokenDAL.findById(tokenId);
|
const foundToken = await identityAccessTokenDAL.findOne({
|
||||||
|
id: tokenId,
|
||||||
|
authMethod: IdentityAuthMethod.TOKEN_AUTH
|
||||||
|
});
|
||||||
if (!foundToken) throw new NotFoundError({ message: `Token with ID ${tokenId} not found` });
|
if (!foundToken) throw new NotFoundError({ message: `Token with ID ${tokenId} not found` });
|
||||||
|
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: foundToken.identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: foundToken.identityId });
|
||||||
if (!identityMembershipOrg) {
|
if (!identityMembershipOrg) {
|
||||||
throw new NotFoundError({ message: `Failed to find identity with ID ${foundToken.identityId}` });
|
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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have Token Auth"
|
message: "The identity does not have Token Auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -409,6 +423,7 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
|
|
||||||
const [token] = await identityAccessTokenDAL.update(
|
const [token] = await identityAccessTokenDAL.update(
|
||||||
{
|
{
|
||||||
|
authMethod: IdentityAuthMethod.TOKEN_AUTH,
|
||||||
identityId: foundToken.identityId,
|
identityId: foundToken.identityId,
|
||||||
id: tokenId
|
id: tokenId
|
||||||
},
|
},
|
||||||
@@ -429,7 +444,8 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
}: TRevokeTokenAuthTokenDTO) => {
|
}: TRevokeTokenAuthTokenDTO) => {
|
||||||
const identityAccessToken = await identityAccessTokenDAL.findOne({
|
const identityAccessToken = await identityAccessTokenDAL.findOne({
|
||||||
[`${TableName.IdentityAccessToken}.id` as "id"]: tokenId,
|
[`${TableName.IdentityAccessToken}.id` as "id"]: tokenId,
|
||||||
isAccessTokenRevoked: false
|
isAccessTokenRevoked: false,
|
||||||
|
authMethod: IdentityAuthMethod.TOKEN_AUTH
|
||||||
});
|
});
|
||||||
if (!identityAccessToken)
|
if (!identityAccessToken)
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
@@ -453,9 +469,15 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
);
|
);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
|
||||||
|
|
||||||
const revokedToken = await identityAccessTokenDAL.updateById(identityAccessToken.id, {
|
const [revokedToken] = await identityAccessTokenDAL.update(
|
||||||
|
{
|
||||||
|
id: identityAccessToken.id,
|
||||||
|
authMethod: IdentityAuthMethod.TOKEN_AUTH
|
||||||
|
},
|
||||||
|
{
|
||||||
isAccessTokenRevoked: true
|
isAccessTokenRevoked: true
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return { revokedToken };
|
return { revokedToken };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedErro
|
|||||||
import { checkIPAgainstBlocklist, extractIPDetails, isValidIpOrCidr, TIp } from "@app/lib/ip";
|
import { checkIPAgainstBlocklist, extractIPDetails, isValidIpOrCidr, TIp } from "@app/lib/ip";
|
||||||
|
|
||||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
|
||||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||||
@@ -36,7 +35,6 @@ type TIdentityUaServiceFactoryDep = {
|
|||||||
identityUaClientSecretDAL: TIdentityUaClientSecretDALFactory;
|
identityUaClientSecretDAL: TIdentityUaClientSecretDALFactory;
|
||||||
identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
|
identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
|
||||||
identityOrgMembershipDAL: TIdentityOrgDALFactory;
|
identityOrgMembershipDAL: TIdentityOrgDALFactory;
|
||||||
identityDAL: Pick<TIdentityDALFactory, "updateById">;
|
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
};
|
};
|
||||||
@@ -48,7 +46,6 @@ export const identityUaServiceFactory = ({
|
|||||||
identityUaClientSecretDAL,
|
identityUaClientSecretDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
identityDAL,
|
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService
|
licenseService
|
||||||
}: TIdentityUaServiceFactoryDep) => {
|
}: TIdentityUaServiceFactoryDep) => {
|
||||||
@@ -115,7 +112,8 @@ export const identityUaServiceFactory = ({
|
|||||||
accessTokenTTL: identityUa.accessTokenTTL,
|
accessTokenTTL: identityUa.accessTokenTTL,
|
||||||
accessTokenMaxTTL: identityUa.accessTokenMaxTTL,
|
accessTokenMaxTTL: identityUa.accessTokenMaxTTL,
|
||||||
accessTokenNumUses: 0,
|
accessTokenNumUses: 0,
|
||||||
accessTokenNumUsesLimit: identityUa.accessTokenNumUsesLimit
|
accessTokenNumUsesLimit: identityUa.accessTokenNumUsesLimit,
|
||||||
|
authMethod: IdentityAuthMethod.UNIVERSAL_AUTH
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -156,10 +154,12 @@ export const identityUaServiceFactory = ({
|
|||||||
}: TAttachUaDTO) => {
|
}: TAttachUaDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "Failed to add universal auth to already configured identity"
|
message: "Failed to add universal auth to already configured identity"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
|
||||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||||
@@ -221,13 +221,6 @@ export const identityUaServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
await identityDAL.updateById(
|
|
||||||
identityMembershipOrg.identityId,
|
|
||||||
{
|
|
||||||
authMethod: IdentityAuthMethod.Univeral
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
return doc;
|
return doc;
|
||||||
});
|
});
|
||||||
return { ...identityUa, orgId: identityMembershipOrg.orgId };
|
return { ...identityUa, orgId: identityMembershipOrg.orgId };
|
||||||
@@ -247,10 +240,12 @@ export const identityUaServiceFactory = ({
|
|||||||
}: TUpdateUaDTO) => {
|
}: TUpdateUaDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "Failed to updated universal auth"
|
message: "The identity does not have universal auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const uaIdentityAuth = await identityUaDAL.findOne({ identityId });
|
const uaIdentityAuth = await identityUaDAL.findOne({ identityId });
|
||||||
|
|
||||||
@@ -321,10 +316,12 @@ export const identityUaServiceFactory = ({
|
|||||||
const getIdentityUniversalAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetUaDTO) => {
|
const getIdentityUniversalAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetUaDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have universal auth"
|
message: "The identity does not have universal auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const uaIdentityAuth = await identityUaDAL.findOne({ identityId });
|
const uaIdentityAuth = await identityUaDAL.findOne({ identityId });
|
||||||
|
|
||||||
@@ -348,10 +345,12 @@ export const identityUaServiceFactory = ({
|
|||||||
}: TRevokeUaDTO) => {
|
}: TRevokeUaDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have universal auth"
|
message: "The identity does not have universal auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -375,7 +374,6 @@ export const identityUaServiceFactory = ({
|
|||||||
|
|
||||||
const revokedIdentityUniversalAuth = await identityUaDAL.transaction(async (tx) => {
|
const revokedIdentityUniversalAuth = await identityUaDAL.transaction(async (tx) => {
|
||||||
const deletedUniversalAuth = await identityUaDAL.delete({ identityId }, tx);
|
const deletedUniversalAuth = await identityUaDAL.delete({ identityId }, tx);
|
||||||
await identityDAL.updateById(identityId, { authMethod: null }, tx);
|
|
||||||
return { ...deletedUniversalAuth?.[0], orgId: identityMembershipOrg.orgId };
|
return { ...deletedUniversalAuth?.[0], orgId: identityMembershipOrg.orgId };
|
||||||
});
|
});
|
||||||
return revokedIdentityUniversalAuth;
|
return revokedIdentityUniversalAuth;
|
||||||
@@ -393,10 +391,13 @@ export const identityUaServiceFactory = ({
|
|||||||
}: TCreateUaClientSecretDTO) => {
|
}: TCreateUaClientSecretDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have universal auth"
|
message: "The identity does not have universal auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -422,12 +423,11 @@ export const identityUaServiceFactory = ({
|
|||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const clientSecret = crypto.randomBytes(32).toString("hex");
|
const clientSecret = crypto.randomBytes(32).toString("hex");
|
||||||
const clientSecretHash = await bcrypt.hash(clientSecret, appCfg.SALT_ROUNDS);
|
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({
|
const identityUaClientSecret = await identityUaClientSecretDAL.create({
|
||||||
identityUAId: identityUniversalAuth.id,
|
identityUAId: identityUaAuth.id,
|
||||||
description,
|
description,
|
||||||
clientSecretPrefix: clientSecret.slice(0, 4),
|
clientSecretPrefix: clientSecret.slice(0, 4),
|
||||||
clientSecretHash,
|
clientSecretHash,
|
||||||
@@ -439,7 +439,6 @@ export const identityUaServiceFactory = ({
|
|||||||
return {
|
return {
|
||||||
clientSecret,
|
clientSecret,
|
||||||
clientSecretData: identityUaClientSecret,
|
clientSecretData: identityUaClientSecret,
|
||||||
uaAuth: identityUniversalAuth,
|
|
||||||
orgId: identityMembershipOrg.orgId
|
orgId: identityMembershipOrg.orgId
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -453,10 +452,12 @@ export const identityUaServiceFactory = ({
|
|||||||
}: TGetUaClientSecretsDTO) => {
|
}: TGetUaClientSecretsDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have universal auth"
|
message: "The identity does not have universal auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -500,10 +501,13 @@ export const identityUaServiceFactory = ({
|
|||||||
}: TGetUniversalAuthClientSecretByIdDTO) => {
|
}: TGetUniversalAuthClientSecretByIdDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have universal auth"
|
message: "The identity does not have universal auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -539,10 +543,13 @@ export const identityUaServiceFactory = ({
|
|||||||
}: TRevokeUaClientSecretDTO) => {
|
}: TRevokeUaClientSecretDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${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({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have universal auth"
|
message: "The identity does not have universal auth"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { IdentityAuthMethod } from "@app/db/schemas";
|
||||||
|
|
||||||
|
export 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 : null],
|
||||||
|
...[gcpId ? IdentityAuthMethod.GCP_AUTH : null],
|
||||||
|
...[awsId ? IdentityAuthMethod.AWS_AUTH : null],
|
||||||
|
...[kubernetesId ? IdentityAuthMethod.KUBERNETES_AUTH : null],
|
||||||
|
...[oidcId ? IdentityAuthMethod.OIDC_AUTH : null],
|
||||||
|
...[azureId ? IdentityAuthMethod.AZURE_AUTH : null],
|
||||||
|
...[tokenId ? IdentityAuthMethod.TOKEN_AUTH : null]
|
||||||
|
].filter((authMethod) => authMethod) as IdentityAuthMethod[];
|
||||||
|
};
|
||||||
@@ -1,12 +1,25 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName, TIdentityOrgMemberships, TOrgRoles } from "@app/db/schemas";
|
import {
|
||||||
|
TableName,
|
||||||
|
TIdentityAwsAuths,
|
||||||
|
TIdentityAzureAuths,
|
||||||
|
TIdentityGcpAuths,
|
||||||
|
TIdentityKubernetesAuths,
|
||||||
|
TIdentityOidcAuths,
|
||||||
|
TIdentityOrgMemberships,
|
||||||
|
TIdentityTokenAuths,
|
||||||
|
TIdentityUniversalAuths,
|
||||||
|
TOrgRoles
|
||||||
|
} from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
|
import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
|
||||||
import { OrderByDirection } from "@app/lib/types";
|
import { OrderByDirection } from "@app/lib/types";
|
||||||
import { OrgIdentityOrderBy, TListOrgIdentitiesByOrgIdDTO } from "@app/services/identity/identity-types";
|
import { OrgIdentityOrderBy, TListOrgIdentitiesByOrgIdDTO } from "@app/services/identity/identity-types";
|
||||||
|
|
||||||
|
import { buildAuthMethods } from "./identity-fns";
|
||||||
|
|
||||||
export type TIdentityOrgDALFactory = ReturnType<typeof identityOrgDALFactory>;
|
export type TIdentityOrgDALFactory = ReturnType<typeof identityOrgDALFactory>;
|
||||||
|
|
||||||
export const identityOrgDALFactory = (db: TDbClient) => {
|
export const identityOrgDALFactory = (db: TDbClient) => {
|
||||||
@@ -15,14 +28,73 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
|||||||
const findOne = async (filter: Partial<TIdentityOrgMemberships>, tx?: Knex) => {
|
const findOne = async (filter: Partial<TIdentityOrgMemberships>, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const [data] = await (tx || db.replicaNode())(TableName.IdentityOrgMembership)
|
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`)
|
.join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`)
|
||||||
.select(selectAllTableCols(TableName.IdentityOrgMembership))
|
|
||||||
.select(db.ref("name").withSchema(TableName.Identity))
|
.leftJoin<TIdentityUniversalAuths>(
|
||||||
.select(db.ref("authMethod").withSchema(TableName.Identity));
|
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) {
|
if (data) {
|
||||||
const { name, authMethod } = data;
|
const { name } = data;
|
||||||
return { ...data, identity: { id: data.identityId, name, authMethod } };
|
return {
|
||||||
|
...data,
|
||||||
|
identity: {
|
||||||
|
id: data.identityId,
|
||||||
|
name,
|
||||||
|
authMethods: buildAuthMethods(data)
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "FindOne" });
|
throw new DatabaseError({ error, name: "FindOne" });
|
||||||
@@ -51,8 +123,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
|||||||
.orderBy(`${TableName.Identity}.${orderBy}`, orderDirection)
|
.orderBy(`${TableName.Identity}.${orderBy}`, orderDirection)
|
||||||
.select(
|
.select(
|
||||||
selectAllTableCols(TableName.IdentityOrgMembership),
|
selectAllTableCols(TableName.IdentityOrgMembership),
|
||||||
db.ref("name").withSchema(TableName.Identity).as("identityName"),
|
db.ref("name").withSchema(TableName.Identity).as("identityName")
|
||||||
db.ref("authMethod").withSchema(TableName.Identity).as("identityAuthMethod")
|
|
||||||
)
|
)
|
||||||
.where(filter)
|
.where(filter)
|
||||||
.as("paginatedIdentity");
|
.as("paginatedIdentity");
|
||||||
@@ -70,11 +141,49 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
|||||||
const query = (tx || db.replicaNode())
|
const query = (tx || db.replicaNode())
|
||||||
.from<TSubquery[number], TSubquery>(paginatedIdentity)
|
.from<TSubquery[number], TSubquery>(paginatedIdentity)
|
||||||
.leftJoin<TOrgRoles>(TableName.OrgRoles, `paginatedIdentity.roleId`, `${TableName.OrgRoles}.id`)
|
.leftJoin<TOrgRoles>(TableName.OrgRoles, `paginatedIdentity.roleId`, `${TableName.OrgRoles}.id`)
|
||||||
|
|
||||||
.leftJoin(TableName.IdentityMetadata, (queryBuilder) => {
|
.leftJoin(TableName.IdentityMetadata, (queryBuilder) => {
|
||||||
void queryBuilder
|
void queryBuilder
|
||||||
.on(`paginatedIdentity.identityId`, `${TableName.IdentityMetadata}.identityId`)
|
.on(`paginatedIdentity.identityId`, `${TableName.IdentityMetadata}.identityId`)
|
||||||
.andOn(`paginatedIdentity.orgId`, `${TableName.IdentityMetadata}.orgId`);
|
.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(
|
.select(
|
||||||
db.ref("id").withSchema("paginatedIdentity"),
|
db.ref("id").withSchema("paginatedIdentity"),
|
||||||
db.ref("role").withSchema("paginatedIdentity"),
|
db.ref("role").withSchema("paginatedIdentity"),
|
||||||
@@ -82,9 +191,16 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("orgId").withSchema("paginatedIdentity"),
|
db.ref("orgId").withSchema("paginatedIdentity"),
|
||||||
db.ref("createdAt").withSchema("paginatedIdentity"),
|
db.ref("createdAt").withSchema("paginatedIdentity"),
|
||||||
db.ref("updatedAt").withSchema("paginatedIdentity"),
|
db.ref("updatedAt").withSchema("paginatedIdentity"),
|
||||||
db.ref("identityId").withSchema("paginatedIdentity"),
|
db.ref("identityId").withSchema("paginatedIdentity").as("identityId"),
|
||||||
db.ref("identityName").withSchema("paginatedIdentity"),
|
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
|
// cr stands for custom role
|
||||||
.select(db.ref("id").as("crId").withSchema(TableName.OrgRoles))
|
.select(db.ref("id").as("crId").withSchema(TableName.OrgRoles))
|
||||||
@@ -114,11 +230,17 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
|||||||
crName,
|
crName,
|
||||||
identityId,
|
identityId,
|
||||||
identityName,
|
identityName,
|
||||||
identityAuthMethod,
|
|
||||||
role,
|
role,
|
||||||
roleId,
|
roleId,
|
||||||
id,
|
id,
|
||||||
orgId,
|
orgId,
|
||||||
|
uaId,
|
||||||
|
awsId,
|
||||||
|
gcpId,
|
||||||
|
kubernetesId,
|
||||||
|
oidcId,
|
||||||
|
azureId,
|
||||||
|
tokenId,
|
||||||
createdAt,
|
createdAt,
|
||||||
updatedAt
|
updatedAt
|
||||||
}) => ({
|
}) => ({
|
||||||
@@ -126,6 +248,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
|||||||
roleId,
|
roleId,
|
||||||
identityId,
|
identityId,
|
||||||
id,
|
id,
|
||||||
|
|
||||||
orgId,
|
orgId,
|
||||||
createdAt,
|
createdAt,
|
||||||
updatedAt,
|
updatedAt,
|
||||||
@@ -141,7 +264,15 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
|||||||
identity: {
|
identity: {
|
||||||
id: identityId,
|
id: identityId,
|
||||||
name: identityName,
|
name: identityName,
|
||||||
authMethod: identityAuthMethod as string
|
authMethods: buildAuthMethods({
|
||||||
|
uaId,
|
||||||
|
awsId,
|
||||||
|
gcpId,
|
||||||
|
kubernetesId,
|
||||||
|
oidcId,
|
||||||
|
azureId,
|
||||||
|
tokenId
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
childrenMapper: [
|
childrenMapper: [
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export const identityServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return newIdentity;
|
return { ...newIdentity, authMethods: [] };
|
||||||
});
|
});
|
||||||
await licenseService.updateSubscriptionOrgMemberCount(orgId);
|
await licenseService.updateSubscriptionOrgMemberCount(orgId);
|
||||||
|
|
||||||
|
|||||||
@@ -127,8 +127,7 @@ export const SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(
|
|||||||
cursor-pointer select-none items-center overflow-hidden text-ellipsis whitespace-nowrap rounded-md py-2
|
cursor-pointer select-none items-center overflow-hidden text-ellipsis whitespace-nowrap rounded-md py-2
|
||||||
pl-10 pr-4 text-sm outline-none transition-all hover:bg-mineshaft-500 data-[highlighted]:bg-mineshaft-700/80`,
|
pl-10 pr-4 text-sm outline-none transition-all hover:bg-mineshaft-500 data-[highlighted]:bg-mineshaft-700/80`,
|
||||||
isSelected && "bg-primary",
|
isSelected && "bg-primary",
|
||||||
isDisabled &&
|
isDisabled && "cursor-not-allowed text-gray-600 opacity-80 hover:!bg-transparent",
|
||||||
"cursor-not-allowed text-gray-600 hover:bg-transparent hover:text-mineshaft-600",
|
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
ref={forwardedRef}
|
ref={forwardedRef}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export type IdentityTrustedIp = {
|
|||||||
export type Identity = {
|
export type Identity = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethods: IdentityAuthMethod[];
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
import { useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { faChevronLeft, faEllipsis } from "@fortawesome/free-solid-svg-icons";
|
import { faChevronLeft, faEllipsis } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
@@ -19,12 +20,15 @@ import {
|
|||||||
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||||
import { withPermission } from "@app/hoc";
|
import { withPermission } from "@app/hoc";
|
||||||
import {
|
import {
|
||||||
|
IdentityAuthMethod,
|
||||||
useDeleteIdentity,
|
useDeleteIdentity,
|
||||||
useGetIdentityById,
|
useGetIdentityById,
|
||||||
useRevokeIdentityTokenAuthToken,
|
useRevokeIdentityTokenAuthToken,
|
||||||
useRevokeIdentityUniversalAuthClientSecret} from "@app/hooks/api";
|
useRevokeIdentityUniversalAuthClientSecret
|
||||||
|
} from "@app/hooks/api";
|
||||||
|
import { Identity } from "@app/hooks/api/identities/types";
|
||||||
import { usePopUp } from "@app/hooks/usePopUp";
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
import { TabSections } from"@app/views/Org/Types";
|
import { TabSections } from "@app/views/Org/Types";
|
||||||
|
|
||||||
import { IdentityAuthMethodModal } from "../MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityAuthMethodModal";
|
import { IdentityAuthMethodModal } from "../MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityAuthMethodModal";
|
||||||
import { IdentityModal } from "../MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityModal";
|
import { IdentityModal } from "../MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityModal";
|
||||||
@@ -49,6 +53,10 @@ export const IdentityPage = withPermission(
|
|||||||
const { mutateAsync: revokeToken } = useRevokeIdentityTokenAuthToken();
|
const { mutateAsync: revokeToken } = useRevokeIdentityTokenAuthToken();
|
||||||
const { mutateAsync: revokeClientSecret } = useRevokeIdentityUniversalAuthClientSecret();
|
const { mutateAsync: revokeClientSecret } = useRevokeIdentityUniversalAuthClientSecret();
|
||||||
|
|
||||||
|
const [selectedAuthMethod, setSelectedAuthMethod] = useState<
|
||||||
|
Identity["authMethods"][number] | null
|
||||||
|
>(null);
|
||||||
|
|
||||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||||
"identity",
|
"identity",
|
||||||
"deleteIdentity",
|
"deleteIdentity",
|
||||||
@@ -124,7 +132,7 @@ export const IdentityPage = withPermission(
|
|||||||
|
|
||||||
const onDeleteClientSecretSubmit = async ({ clientSecretId }: { clientSecretId: string }) => {
|
const onDeleteClientSecretSubmit = async ({ clientSecretId }: { clientSecretId: string }) => {
|
||||||
try {
|
try {
|
||||||
if (!data?.identity.id) return;
|
if (!data?.identity.id || selectedAuthMethod !== IdentityAuthMethod.UNIVERSAL_AUTH) return;
|
||||||
|
|
||||||
await revokeClientSecret({
|
await revokeClientSecret({
|
||||||
identityId: data?.identity.id,
|
identityId: data?.identity.id,
|
||||||
@@ -208,12 +216,12 @@ export const IdentityPage = withPermission(
|
|||||||
handlePopUpOpen("identityAuthMethod", {
|
handlePopUpOpen("identityAuthMethod", {
|
||||||
identityId,
|
identityId,
|
||||||
name: data.identity.name,
|
name: data.identity.name,
|
||||||
authMethod: data.identity.authMethod
|
allAuthMethods: data.identity.authMethods
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
disabled={!isAllowed}
|
disabled={!isAllowed}
|
||||||
>
|
>
|
||||||
{`${data.identity.authMethod ? "Edit" : "Configure"} Auth Method`}
|
Add new auth method
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
</OrgPermissionCan>
|
</OrgPermissionCan>
|
||||||
@@ -247,6 +255,8 @@ export const IdentityPage = withPermission(
|
|||||||
<div className="mr-4 w-96">
|
<div className="mr-4 w-96">
|
||||||
<IdentityDetailsSection identityId={identityId} handlePopUpOpen={handlePopUpOpen} />
|
<IdentityDetailsSection identityId={identityId} handlePopUpOpen={handlePopUpOpen} />
|
||||||
<IdentityAuthenticationSection
|
<IdentityAuthenticationSection
|
||||||
|
selectedAuthMethod={selectedAuthMethod}
|
||||||
|
setSelectedAuthMethod={setSelectedAuthMethod}
|
||||||
identityId={identityId}
|
identityId={identityId}
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+89
-18
@@ -1,15 +1,13 @@
|
|||||||
import { faPencil } from "@fortawesome/free-solid-svg-icons";
|
import { useEffect } from "react";
|
||||||
|
import { faPencil, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { OrgPermissionCan } from "@app/components/permissions";
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
import {
|
import { Button, IconButton, Select, SelectItem, Tooltip } from "@app/components/v2";
|
||||||
IconButton,
|
|
||||||
// Button,
|
|
||||||
Tooltip
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
|
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
|
||||||
import { useGetIdentityById } from "@app/hooks/api";
|
import { useGetIdentityById } from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod, identityAuthToNameMap } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod, identityAuthToNameMap } from "@app/hooks/api/identities";
|
||||||
|
import { Identity } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
import { IdentityClientSecrets } from "./IdentityClientSecrets";
|
import { IdentityClientSecrets } from "./IdentityClientSecrets";
|
||||||
@@ -17,6 +15,8 @@ import { IdentityTokens } from "./IdentityTokens";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
identityId: string;
|
identityId: string;
|
||||||
|
setSelectedAuthMethod: (authMethod: Identity["authMethods"][number] | null) => void;
|
||||||
|
selectedAuthMethod: Identity["authMethods"][number] | null;
|
||||||
handlePopUpOpen: (
|
handlePopUpOpen: (
|
||||||
popUpName: keyof UsePopUpState<
|
popUpName: keyof UsePopUpState<
|
||||||
[
|
[
|
||||||
@@ -33,16 +33,34 @@ type Props = {
|
|||||||
) => void;
|
) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityAuthenticationSection = ({ identityId, handlePopUpOpen }: Props) => {
|
export const IdentityAuthenticationSection = ({
|
||||||
|
identityId,
|
||||||
|
setSelectedAuthMethod,
|
||||||
|
selectedAuthMethod,
|
||||||
|
handlePopUpOpen
|
||||||
|
}: Props) => {
|
||||||
const { data } = useGetIdentityById(identityId);
|
const { data } = useGetIdentityById(identityId);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!data?.identity) return;
|
||||||
|
|
||||||
|
if (data.identity.authMethods?.length) {
|
||||||
|
setSelectedAuthMethod(data.identity.authMethods[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line consistent-return
|
||||||
|
return () => setSelectedAuthMethod(null);
|
||||||
|
}, [data?.identity]);
|
||||||
|
|
||||||
return data ? (
|
return data ? (
|
||||||
<div className="mt-4 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
<div className="mt-4 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||||
<h3 className="text-lg font-semibold text-mineshaft-100">Authentication</h3>
|
<h3 className="text-lg font-semibold text-mineshaft-100">Authentication</h3>
|
||||||
|
|
||||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Identity}>
|
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Identity}>
|
||||||
{(isAllowed) => {
|
{(isAllowed) => {
|
||||||
return (
|
return (
|
||||||
<Tooltip content={`${data.identity.authMethod ? "Edit" : "Configure"} Auth Method`}>
|
<Tooltip content="Add new auth method">
|
||||||
<IconButton
|
<IconButton
|
||||||
isDisabled={!isAllowed}
|
isDisabled={!isAllowed}
|
||||||
ariaLabel="copy icon"
|
ariaLabel="copy icon"
|
||||||
@@ -52,33 +70,86 @@ export const IdentityAuthenticationSection = ({ identityId, handlePopUpOpen }: P
|
|||||||
handlePopUpOpen("identityAuthMethod", {
|
handlePopUpOpen("identityAuthMethod", {
|
||||||
identityId,
|
identityId,
|
||||||
name: data.identity.name,
|
name: data.identity.name,
|
||||||
authMethod: data.identity.authMethod
|
allAuthMethods: data.identity.authMethods
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faPencil} />
|
<FontAwesomeIcon icon={faPlus} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</OrgPermissionCan>
|
</OrgPermissionCan>
|
||||||
</div>
|
</div>
|
||||||
|
{data.identity.authMethods.length > 0 ? (
|
||||||
|
<>
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<p className="text-sm font-semibold text-mineshaft-300">Auth Method</p>
|
<p className="ml-px mb-0.5 text-sm font-semibold text-mineshaft-300">Auth Method</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-mineshaft-300">
|
<div className="flex items-center gap-2">
|
||||||
{data.identity.authMethod
|
<div className="w-full">
|
||||||
? identityAuthToNameMap[data.identity.authMethod]
|
<Select
|
||||||
: "Not configured"}
|
className="w-full"
|
||||||
</p>
|
value={selectedAuthMethod as string}
|
||||||
|
onValueChange={(value) => setSelectedAuthMethod(value as IdentityAuthMethod)}
|
||||||
|
>
|
||||||
|
{(data.identity?.authMethods || []).map((authMethod) => (
|
||||||
|
<SelectItem key={authMethod || authMethod} value={authMethod}>
|
||||||
|
{identityAuthToNameMap[authMethod]}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
{data.identity.authMethod === IdentityAuthMethod.UNIVERSAL_AUTH && (
|
<div>
|
||||||
|
<Tooltip content="Edit auth method">
|
||||||
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
handlePopUpOpen("identityAuthMethod", {
|
||||||
|
identityId,
|
||||||
|
name: data.identity.name,
|
||||||
|
authMethod: selectedAuthMethod,
|
||||||
|
allAuthMethods: data.identity.authMethods
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
ariaLabel="copy icon"
|
||||||
|
variant="plain"
|
||||||
|
className="group relative"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faPencil} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>{" "}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{selectedAuthMethod === IdentityAuthMethod.UNIVERSAL_AUTH && (
|
||||||
<IdentityClientSecrets identityId={identityId} handlePopUpOpen={handlePopUpOpen} />
|
<IdentityClientSecrets identityId={identityId} handlePopUpOpen={handlePopUpOpen} />
|
||||||
)}
|
)}
|
||||||
{data.identity.authMethod === IdentityAuthMethod.TOKEN_AUTH && (
|
{selectedAuthMethod === IdentityAuthMethod.TOKEN_AUTH && (
|
||||||
<IdentityTokens identityId={identityId} handlePopUpOpen={handlePopUpOpen} />
|
<IdentityTokens identityId={identityId} handlePopUpOpen={handlePopUpOpen} />
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="w-full space-y-2 pt-2">
|
||||||
|
<p className="text-sm text-mineshaft-300">
|
||||||
|
No authentication methods configured. Get started by creating a new auth method.
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
handlePopUpOpen("identityAuthMethod", {
|
||||||
|
identityId,
|
||||||
|
name: data.identity.name,
|
||||||
|
allAuthMethods: data.identity.authMethods
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
variant="outline_bg"
|
||||||
|
className="w-full"
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
Create Auth Method
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div />
|
<div />
|
||||||
|
|||||||
+21
-294
@@ -1,38 +1,10 @@
|
|||||||
import { useEffect } from "react";
|
import { useState } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
|
||||||
import { yupResolver } from "@hookform/resolvers/yup";
|
|
||||||
import * as yup from "yup";
|
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { Modal, ModalContent } from "@app/components/v2";
|
||||||
import {
|
|
||||||
DeleteActionModal,
|
|
||||||
FormControl,
|
|
||||||
Modal,
|
|
||||||
ModalContent,
|
|
||||||
Select,
|
|
||||||
SelectItem,
|
|
||||||
UpgradePlanModal
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { useOrganization } from "@app/context";
|
|
||||||
import {
|
|
||||||
useDeleteIdentityAwsAuth,
|
|
||||||
useDeleteIdentityAzureAuth,
|
|
||||||
useDeleteIdentityGcpAuth,
|
|
||||||
useDeleteIdentityKubernetesAuth,
|
|
||||||
useDeleteIdentityOidcAuth,
|
|
||||||
useDeleteIdentityTokenAuth,
|
|
||||||
useDeleteIdentityUniversalAuth
|
|
||||||
} from "@app/hooks/api";
|
|
||||||
import { IdentityAuthMethod, identityAuthToNameMap } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod, identityAuthToNameMap } from "@app/hooks/api/identities";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
import { IdentityAwsAuthForm } from "./IdentityAwsAuthForm";
|
import { IdentityAuthMethodModalContent } from "./IdentityAuthMethodModalContent";
|
||||||
import { IdentityAzureAuthForm } from "./IdentityAzureAuthForm";
|
|
||||||
import { IdentityGcpAuthForm } from "./IdentityGcpAuthForm";
|
|
||||||
import { IdentityKubernetesAuthForm } from "./IdentityKubernetesAuthForm";
|
|
||||||
import { IdentityOidcAuthForm } from "./IdentityOidcAuthForm";
|
|
||||||
import { IdentityTokenAuthForm } from "./IdentityTokenAuthForm";
|
|
||||||
import { IdentityUniversalAuthForm } from "./IdentityUniversalAuthForm";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
popUp: UsePopUpState<["identityAuthMethod", "upgradePlan", "revokeAuthMethod"]>;
|
popUp: UsePopUpState<["identityAuthMethod", "upgradePlan", "revokeAuthMethod"]>;
|
||||||
@@ -43,229 +15,13 @@ type Props = {
|
|||||||
) => void;
|
) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const identityAuthMethods = [
|
|
||||||
{ label: "Token Auth", value: IdentityAuthMethod.TOKEN_AUTH },
|
|
||||||
{ label: "Universal Auth", value: IdentityAuthMethod.UNIVERSAL_AUTH },
|
|
||||||
{ label: "Kubernetes Auth", value: IdentityAuthMethod.KUBERNETES_AUTH },
|
|
||||||
{ label: "GCP Auth", value: IdentityAuthMethod.GCP_AUTH },
|
|
||||||
{ label: "AWS Auth", value: IdentityAuthMethod.AWS_AUTH },
|
|
||||||
{ label: "Azure Auth", value: IdentityAuthMethod.AZURE_AUTH },
|
|
||||||
{ label: "OIDC Auth", value: IdentityAuthMethod.OIDC_AUTH }
|
|
||||||
];
|
|
||||||
|
|
||||||
const schema = yup
|
|
||||||
.object({
|
|
||||||
authMethod: yup
|
|
||||||
.mixed<IdentityAuthMethod>()
|
|
||||||
.oneOf(Object.values(IdentityAuthMethod))
|
|
||||||
.required("Auth method is required")
|
|
||||||
})
|
|
||||||
.required();
|
|
||||||
|
|
||||||
export type FormData = yup.InferType<typeof schema>;
|
|
||||||
|
|
||||||
export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Props) => {
|
export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const [selectedAuthMethod, setSelectedAuthMethod] = useState<IdentityAuthMethod | null>(null);
|
||||||
const orgId = currentOrg?.id || "";
|
|
||||||
|
|
||||||
const { mutateAsync: revokeUniversalAuth } = useDeleteIdentityUniversalAuth();
|
|
||||||
const { mutateAsync: revokeTokenAuth } = useDeleteIdentityTokenAuth();
|
|
||||||
const { mutateAsync: revokeKubernetesAuth } = useDeleteIdentityKubernetesAuth();
|
|
||||||
const { mutateAsync: revokeGcpAuth } = useDeleteIdentityGcpAuth();
|
|
||||||
const { mutateAsync: revokeAwsAuth } = useDeleteIdentityAwsAuth();
|
|
||||||
const { mutateAsync: revokeAzureAuth } = useDeleteIdentityAzureAuth();
|
|
||||||
const { mutateAsync: revokeOidcAuth } = useDeleteIdentityOidcAuth();
|
|
||||||
|
|
||||||
const initialAuthMethod = popUp?.identityAuthMethod?.data?.authMethod;
|
const initialAuthMethod = popUp?.identityAuthMethod?.data?.authMethod;
|
||||||
|
|
||||||
const { control, watch, setValue, reset } = useForm<FormData>({
|
const isSelectedAuthAlreadyConfigured =
|
||||||
resolver: yupResolver(schema),
|
popUp?.identityAuthMethod?.data?.allAuthMethods?.includes(selectedAuthMethod);
|
||||||
defaultValues: {
|
|
||||||
authMethod: initialAuthMethod
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// reset form on open
|
|
||||||
if (popUp.identityAuthMethod.isOpen)
|
|
||||||
reset({ authMethod: popUp?.identityAuthMethod?.data?.authMethod });
|
|
||||||
}, [popUp.identityAuthMethod.isOpen]);
|
|
||||||
|
|
||||||
const identityAuthMethodData = {
|
|
||||||
identityId: popUp?.identityAuthMethod.data?.identityId,
|
|
||||||
name: popUp?.identityAuthMethod?.data?.name,
|
|
||||||
authMethod: watch("authMethod")
|
|
||||||
} as {
|
|
||||||
identityId: string;
|
|
||||||
name: string;
|
|
||||||
authMethod?: IdentityAuthMethod;
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (identityAuthMethodData?.authMethod) {
|
|
||||||
setValue("authMethod", identityAuthMethodData.authMethod);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setValue("authMethod", IdentityAuthMethod.UNIVERSAL_AUTH);
|
|
||||||
}, [identityAuthMethodData?.authMethod]);
|
|
||||||
|
|
||||||
const onRevokeAuthMethodSubmit = async (authMethod: IdentityAuthMethod) => {
|
|
||||||
if (!orgId || !authMethod) return;
|
|
||||||
try {
|
|
||||||
switch (authMethod) {
|
|
||||||
case IdentityAuthMethod.UNIVERSAL_AUTH: {
|
|
||||||
await revokeUniversalAuth({
|
|
||||||
identityId: identityAuthMethodData.identityId,
|
|
||||||
organizationId: orgId
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.TOKEN_AUTH: {
|
|
||||||
await revokeTokenAuth({
|
|
||||||
identityId: identityAuthMethodData.identityId,
|
|
||||||
organizationId: orgId
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.KUBERNETES_AUTH: {
|
|
||||||
await revokeKubernetesAuth({
|
|
||||||
identityId: identityAuthMethodData.identityId,
|
|
||||||
organizationId: orgId
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.GCP_AUTH: {
|
|
||||||
await revokeGcpAuth({
|
|
||||||
identityId: identityAuthMethodData.identityId,
|
|
||||||
organizationId: orgId
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.AWS_AUTH: {
|
|
||||||
await revokeAwsAuth({
|
|
||||||
identityId: identityAuthMethodData.identityId,
|
|
||||||
organizationId: orgId
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.AZURE_AUTH: {
|
|
||||||
await revokeAzureAuth({
|
|
||||||
identityId: identityAuthMethodData.identityId,
|
|
||||||
organizationId: orgId
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.OIDC_AUTH: {
|
|
||||||
await revokeOidcAuth({
|
|
||||||
identityId: identityAuthMethodData.identityId,
|
|
||||||
organizationId: orgId
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
createNotification({
|
|
||||||
text: `Successfully removed ${identityAuthToNameMap[authMethod]} on ${identityAuthMethodData.name}`,
|
|
||||||
type: "success"
|
|
||||||
});
|
|
||||||
|
|
||||||
handlePopUpToggle("revokeAuthMethod", false);
|
|
||||||
handlePopUpToggle("identityAuthMethod", false);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
createNotification({
|
|
||||||
text: `Failed to remove ${identityAuthToNameMap[authMethod]} on ${identityAuthMethodData.name}`,
|
|
||||||
type: "error"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const renderIdentityAuthForm = () => {
|
|
||||||
switch (identityAuthMethodData.authMethod) {
|
|
||||||
case IdentityAuthMethod.AWS_AUTH: {
|
|
||||||
return (
|
|
||||||
<IdentityAwsAuthForm
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
initialAuthMethod={initialAuthMethod!}
|
|
||||||
revokeAuth={onRevokeAuthMethodSubmit}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.KUBERNETES_AUTH: {
|
|
||||||
return (
|
|
||||||
<IdentityKubernetesAuthForm
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
initialAuthMethod={initialAuthMethod!}
|
|
||||||
revokeAuth={onRevokeAuthMethodSubmit}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.GCP_AUTH: {
|
|
||||||
return (
|
|
||||||
<IdentityGcpAuthForm
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
initialAuthMethod={initialAuthMethod!}
|
|
||||||
revokeAuth={onRevokeAuthMethodSubmit}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.AZURE_AUTH: {
|
|
||||||
return (
|
|
||||||
<IdentityAzureAuthForm
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
initialAuthMethod={initialAuthMethod!}
|
|
||||||
revokeAuth={onRevokeAuthMethodSubmit}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.UNIVERSAL_AUTH: {
|
|
||||||
return (
|
|
||||||
<IdentityUniversalAuthForm
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
initialAuthMethod={initialAuthMethod!}
|
|
||||||
revokeAuth={onRevokeAuthMethodSubmit}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.OIDC_AUTH: {
|
|
||||||
return (
|
|
||||||
<IdentityOidcAuthForm
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
initialAuthMethod={initialAuthMethod!}
|
|
||||||
revokeAuth={onRevokeAuthMethodSubmit}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.TOKEN_AUTH: {
|
|
||||||
return (
|
|
||||||
<IdentityTokenAuthForm
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
initialAuthMethod={initialAuthMethod!}
|
|
||||||
revokeAuth={onRevokeAuthMethodSubmit}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return <div />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -275,52 +31,23 @@ export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpTog
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title={`${
|
title={
|
||||||
identityAuthMethodData.authMethod === initialAuthMethod ? "Update" : "Configure"
|
isSelectedAuthAlreadyConfigured
|
||||||
} Identity Auth Method for ${
|
? `Edit ${identityAuthToNameMap[selectedAuthMethod!] ?? ""}`
|
||||||
identityAuthToNameMap[identityAuthMethodData.authMethod!] ?? ""
|
: `Create new ${identityAuthToNameMap[selectedAuthMethod!] ?? ""}`
|
||||||
}`}
|
}
|
||||||
>
|
>
|
||||||
<Controller
|
<IdentityAuthMethodModalContent
|
||||||
control={control}
|
popUp={popUp}
|
||||||
name="authMethod"
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
defaultValue={IdentityAuthMethod.UNIVERSAL_AUTH}
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
identity={{
|
||||||
<FormControl label="Auth Method" errorText={error?.message} isError={Boolean(error)}>
|
name: popUp?.identityAuthMethod?.data?.name,
|
||||||
<Select
|
authMethods: popUp?.identityAuthMethod?.data?.allAuthMethods,
|
||||||
defaultValue={field.value}
|
id: popUp?.identityAuthMethod.data?.identityId
|
||||||
{...field}
|
|
||||||
onValueChange={(e) => {
|
|
||||||
onChange(e);
|
|
||||||
}}
|
}}
|
||||||
className="w-full"
|
initialAuthMethod={initialAuthMethod}
|
||||||
>
|
setSelectedAuthMethod={setSelectedAuthMethod}
|
||||||
{identityAuthMethods.map(({ label, value }) => (
|
|
||||||
<SelectItem value={String(value || "")} key={label}>
|
|
||||||
{label}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{renderIdentityAuthForm()}
|
|
||||||
<UpgradePlanModal
|
|
||||||
isOpen={popUp?.upgradePlan?.isOpen}
|
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
|
||||||
text="You can use IP allowlisting if you switch to Infisical's Pro plan."
|
|
||||||
/>
|
|
||||||
<DeleteActionModal
|
|
||||||
isOpen={popUp?.revokeAuthMethod?.isOpen}
|
|
||||||
title={`Are you sure want to remove ${
|
|
||||||
identityAuthMethodData?.authMethod
|
|
||||||
? identityAuthToNameMap[identityAuthMethodData.authMethod]
|
|
||||||
: "the auth method"
|
|
||||||
} on ${identityAuthMethodData?.name ?? ""}?`}
|
|
||||||
onChange={(isOpen) => handlePopUpToggle("revokeAuthMethod", isOpen)}
|
|
||||||
deleteKey="confirm"
|
|
||||||
buttonText="Remove"
|
|
||||||
onDeleteApproved={() => onRevokeAuthMethodSubmit(identityAuthMethodData.authMethod!)}
|
|
||||||
/>
|
/>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
+317
@@ -0,0 +1,317 @@
|
|||||||
|
import { useCallback } from "react";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
|
import * as yup from "yup";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import {
|
||||||
|
Badge,
|
||||||
|
DeleteActionModal,
|
||||||
|
FormControl,
|
||||||
|
Select,
|
||||||
|
SelectItem,
|
||||||
|
Tooltip,
|
||||||
|
UpgradePlanModal
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { useOrganization } from "@app/context";
|
||||||
|
import {
|
||||||
|
useDeleteIdentityAwsAuth,
|
||||||
|
useDeleteIdentityAzureAuth,
|
||||||
|
useDeleteIdentityGcpAuth,
|
||||||
|
useDeleteIdentityKubernetesAuth,
|
||||||
|
useDeleteIdentityOidcAuth,
|
||||||
|
useDeleteIdentityTokenAuth,
|
||||||
|
useDeleteIdentityUniversalAuth
|
||||||
|
} from "@app/hooks/api";
|
||||||
|
import { IdentityAuthMethod, identityAuthToNameMap } from "@app/hooks/api/identities";
|
||||||
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
import { IdentityAwsAuthForm } from "./IdentityAwsAuthForm";
|
||||||
|
import { IdentityAzureAuthForm } from "./IdentityAzureAuthForm";
|
||||||
|
import { IdentityGcpAuthForm } from "./IdentityGcpAuthForm";
|
||||||
|
import { IdentityKubernetesAuthForm } from "./IdentityKubernetesAuthForm";
|
||||||
|
import { IdentityOidcAuthForm } from "./IdentityOidcAuthForm";
|
||||||
|
import { IdentityTokenAuthForm } from "./IdentityTokenAuthForm";
|
||||||
|
import { IdentityUniversalAuthForm } from "./IdentityUniversalAuthForm";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
popUp: UsePopUpState<["identityAuthMethod", "upgradePlan", "revokeAuthMethod"]>;
|
||||||
|
handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void;
|
||||||
|
handlePopUpToggle: (
|
||||||
|
popUpName: keyof UsePopUpState<["identityAuthMethod", "upgradePlan", "revokeAuthMethod"]>,
|
||||||
|
state?: boolean
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
identity: {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
authMethods: IdentityAuthMethod[];
|
||||||
|
};
|
||||||
|
initialAuthMethod: IdentityAuthMethod;
|
||||||
|
setSelectedAuthMethod: (authMethod: IdentityAuthMethod) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TRevokeOptions = {
|
||||||
|
identityId: string;
|
||||||
|
organizationId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TRevokeMethods = {
|
||||||
|
revokeMethod: (revokeOptions: TRevokeOptions) => Promise<any>;
|
||||||
|
render: () => JSX.Element;
|
||||||
|
};
|
||||||
|
|
||||||
|
const identityAuthMethods = [
|
||||||
|
{ label: "Token Auth", value: IdentityAuthMethod.TOKEN_AUTH },
|
||||||
|
{ label: "Universal Auth", value: IdentityAuthMethod.UNIVERSAL_AUTH },
|
||||||
|
{ label: "Kubernetes Auth", value: IdentityAuthMethod.KUBERNETES_AUTH },
|
||||||
|
{ label: "GCP Auth", value: IdentityAuthMethod.GCP_AUTH },
|
||||||
|
{ label: "AWS Auth", value: IdentityAuthMethod.AWS_AUTH },
|
||||||
|
{ label: "Azure Auth", value: IdentityAuthMethod.AZURE_AUTH },
|
||||||
|
{ label: "OIDC Auth", value: IdentityAuthMethod.OIDC_AUTH }
|
||||||
|
];
|
||||||
|
|
||||||
|
const schema = yup
|
||||||
|
.object({
|
||||||
|
authMethod: yup
|
||||||
|
.mixed<IdentityAuthMethod>()
|
||||||
|
.oneOf(Object.values(IdentityAuthMethod))
|
||||||
|
.required("Auth method is required")
|
||||||
|
})
|
||||||
|
.required();
|
||||||
|
|
||||||
|
export type FormData = yup.InferType<typeof schema>;
|
||||||
|
|
||||||
|
export const IdentityAuthMethodModalContent = ({
|
||||||
|
popUp,
|
||||||
|
handlePopUpOpen,
|
||||||
|
handlePopUpToggle,
|
||||||
|
identity,
|
||||||
|
initialAuthMethod,
|
||||||
|
setSelectedAuthMethod
|
||||||
|
}: Props) => {
|
||||||
|
const { currentOrg } = useOrganization();
|
||||||
|
const orgId = currentOrg?.id || "";
|
||||||
|
|
||||||
|
const { mutateAsync: revokeUniversalAuth } = useDeleteIdentityUniversalAuth();
|
||||||
|
const { mutateAsync: revokeTokenAuth } = useDeleteIdentityTokenAuth();
|
||||||
|
const { mutateAsync: revokeKubernetesAuth } = useDeleteIdentityKubernetesAuth();
|
||||||
|
const { mutateAsync: revokeGcpAuth } = useDeleteIdentityGcpAuth();
|
||||||
|
const { mutateAsync: revokeAwsAuth } = useDeleteIdentityAwsAuth();
|
||||||
|
const { mutateAsync: revokeAzureAuth } = useDeleteIdentityAzureAuth();
|
||||||
|
const { mutateAsync: revokeOidcAuth } = useDeleteIdentityOidcAuth();
|
||||||
|
|
||||||
|
const { control, watch } = useForm<FormData>({
|
||||||
|
resolver: yupResolver(schema),
|
||||||
|
defaultValues: async () => {
|
||||||
|
let authMethod = initialAuthMethod;
|
||||||
|
|
||||||
|
if (!authMethod) {
|
||||||
|
const firstAuthMethodNotConfiguredAuthMethod = identityAuthMethods.find(
|
||||||
|
({ value }) => !identity?.authMethods?.includes(value)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (firstAuthMethodNotConfiguredAuthMethod) {
|
||||||
|
authMethod = firstAuthMethodNotConfiguredAuthMethod.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedAuthMethod(authMethod);
|
||||||
|
return {
|
||||||
|
authMethod
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const watchedAuthMethod = watch("authMethod");
|
||||||
|
|
||||||
|
const identityAuthMethodData = {
|
||||||
|
identityId: identity.id,
|
||||||
|
name: identity.name,
|
||||||
|
authMethod: watch("authMethod"),
|
||||||
|
configuredAuthMethods: identity.authMethods
|
||||||
|
} as {
|
||||||
|
identityId: string;
|
||||||
|
name: string;
|
||||||
|
authMethod?: IdentityAuthMethod;
|
||||||
|
configuredAuthMethods?: IdentityAuthMethod[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const isSelectedAuthAlreadyConfigured =
|
||||||
|
identityAuthMethodData?.configuredAuthMethods?.includes(watchedAuthMethod);
|
||||||
|
|
||||||
|
const methodMap: Record<IdentityAuthMethod, TRevokeMethods | undefined> = {
|
||||||
|
[IdentityAuthMethod.UNIVERSAL_AUTH]: {
|
||||||
|
revokeMethod: revokeUniversalAuth,
|
||||||
|
render: () => (
|
||||||
|
<IdentityUniversalAuthForm
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
[IdentityAuthMethod.OIDC_AUTH]: {
|
||||||
|
revokeMethod: revokeOidcAuth,
|
||||||
|
render: () => (
|
||||||
|
<IdentityOidcAuthForm
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
[IdentityAuthMethod.TOKEN_AUTH]: {
|
||||||
|
revokeMethod: revokeTokenAuth,
|
||||||
|
render: () => (
|
||||||
|
<IdentityTokenAuthForm
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
[IdentityAuthMethod.AZURE_AUTH]: {
|
||||||
|
revokeMethod: revokeAzureAuth,
|
||||||
|
render: () => (
|
||||||
|
<IdentityAzureAuthForm
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
[IdentityAuthMethod.GCP_AUTH]: {
|
||||||
|
revokeMethod: revokeGcpAuth,
|
||||||
|
render: () => (
|
||||||
|
<IdentityGcpAuthForm
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
[IdentityAuthMethod.KUBERNETES_AUTH]: {
|
||||||
|
revokeMethod: revokeKubernetesAuth,
|
||||||
|
render: () => (
|
||||||
|
<IdentityKubernetesAuthForm
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
[IdentityAuthMethod.AWS_AUTH]: {
|
||||||
|
revokeMethod: revokeAwsAuth,
|
||||||
|
render: () => (
|
||||||
|
<IdentityAwsAuthForm
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isAlreadyConfigured = useCallback((method: IdentityAuthMethod) => {
|
||||||
|
return identityAuthMethodData?.configuredAuthMethods?.includes(method);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const selectedMethodItem = methodMap[identityAuthMethodData.authMethod!];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="authMethod"
|
||||||
|
defaultValue={IdentityAuthMethod.UNIVERSAL_AUTH}
|
||||||
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
|
<FormControl label="Auth Method" errorText={error?.message} isError={Boolean(error)}>
|
||||||
|
<Select
|
||||||
|
isDisabled={isSelectedAuthAlreadyConfigured}
|
||||||
|
defaultValue={field.value}
|
||||||
|
{...field}
|
||||||
|
onValueChange={(e) => {
|
||||||
|
if (!isAlreadyConfigured(e as IdentityAuthMethod)) {
|
||||||
|
setSelectedAuthMethod(e as IdentityAuthMethod);
|
||||||
|
onChange(e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
{identityAuthMethods.map(({ label, value }) => {
|
||||||
|
const alreadyConfigured = isAlreadyConfigured(value);
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
key={`auth-method-${value}`}
|
||||||
|
content="Authentication method already configured"
|
||||||
|
isDisabled={!alreadyConfigured}
|
||||||
|
>
|
||||||
|
<SelectItem
|
||||||
|
isDisabled={alreadyConfigured}
|
||||||
|
value={String(value || "")}
|
||||||
|
key={label}
|
||||||
|
>
|
||||||
|
{label}{" "}
|
||||||
|
{alreadyConfigured && !isSelectedAuthAlreadyConfigured && (
|
||||||
|
<Badge>Configured</Badge>
|
||||||
|
)}
|
||||||
|
</SelectItem>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{selectedMethodItem?.render ? selectedMethodItem.render() : <div />}
|
||||||
|
<UpgradePlanModal
|
||||||
|
isOpen={popUp?.upgradePlan?.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||||
|
text="You can use IP allowlisting if you switch to Infisical's Pro plan."
|
||||||
|
/>
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={popUp?.revokeAuthMethod?.isOpen}
|
||||||
|
title={`Are you sure want to remove ${
|
||||||
|
identityAuthMethodData?.authMethod
|
||||||
|
? identityAuthToNameMap[identityAuthMethodData.authMethod]
|
||||||
|
: "the auth method"
|
||||||
|
} on ${identityAuthMethodData?.name ?? ""}?`}
|
||||||
|
onChange={(isOpen) => handlePopUpToggle("revokeAuthMethod", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
buttonText="Remove"
|
||||||
|
onDeleteApproved={async () => {
|
||||||
|
if (!identityAuthMethodData.authMethod || !orgId || !selectedMethodItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await selectedMethodItem.revokeMethod({
|
||||||
|
identityId: identityAuthMethodData.identityId,
|
||||||
|
organizationId: orgId
|
||||||
|
});
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: "Successfully removed auth method",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
|
||||||
|
handlePopUpToggle("revokeAuthMethod", false);
|
||||||
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
} catch (err) {
|
||||||
|
createNotification({
|
||||||
|
text: "Failed to remove auth method",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
+13
-42
@@ -6,7 +6,7 @@ import { yupResolver } from "@hookform/resolvers/yup";
|
|||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, DeleteActionModal, FormControl, IconButton, Input } from "@app/components/v2";
|
import { Button, FormControl, IconButton, Input } from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityAwsAuth,
|
useAddIdentityAwsAuth,
|
||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { usePopUp, UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const schema = yup
|
const schema = yup
|
||||||
.object({
|
.object({
|
||||||
@@ -62,18 +62,15 @@ type Props = {
|
|||||||
identityAuthMethodData: {
|
identityAuthMethodData: {
|
||||||
identityId: string;
|
identityId: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
configuredAuthMethods?: IdentityAuthMethod[];
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
};
|
};
|
||||||
initialAuthMethod: IdentityAuthMethod;
|
|
||||||
revokeAuth: (authMethod: IdentityAuthMethod) => Promise<void>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityAwsAuthForm = ({
|
export const IdentityAwsAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData,
|
identityAuthMethodData
|
||||||
initialAuthMethod,
|
|
||||||
revokeAuth
|
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -82,13 +79,13 @@ export const IdentityAwsAuthForm = ({
|
|||||||
const { mutateAsync: addMutateAsync } = useAddIdentityAwsAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityAwsAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAwsAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAwsAuth();
|
||||||
|
|
||||||
const isCurrentAuthMethod = identityAuthMethodData?.authMethod === initialAuthMethod;
|
const isUpdate = identityAuthMethodData?.configuredAuthMethods?.includes(
|
||||||
|
identityAuthMethodData.authMethod! || ""
|
||||||
|
);
|
||||||
const { data } = useGetIdentityAwsAuth(identityAuthMethodData?.identityId ?? "", {
|
const { data } = useGetIdentityAwsAuth(identityAuthMethodData?.identityId ?? "", {
|
||||||
enabled: isCurrentAuthMethod
|
enabled: isUpdate
|
||||||
});
|
});
|
||||||
|
|
||||||
const internalPopUpState = usePopUp(["overwriteAuthMethod"] as const);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -184,21 +181,20 @@ export const IdentityAwsAuthForm = ({
|
|||||||
handlePopUpToggle("identityAuthMethod", false);
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Successfully ${isCurrentAuthMethod ? "updated" : "configured"} auth method`,
|
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Failed to ${identityAuthMethodData?.authMethod ? "update" : "configure"} identity`,
|
text: `Failed to ${isUpdate ? "update" : "configure"} identity`,
|
||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -353,17 +349,6 @@ export const IdentityAwsAuthForm = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{initialAuthMethod && identityAuthMethodData?.authMethod !== initialAuthMethod ? (
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
onClick={() => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", true)}
|
|
||||||
>
|
|
||||||
Overwrite
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -371,9 +356,9 @@ export const IdentityAwsAuthForm = ({
|
|||||||
isLoading={isSubmitting}
|
isLoading={isSubmitting}
|
||||||
isDisabled={isSubmitting}
|
isDisabled={isSubmitting}
|
||||||
>
|
>
|
||||||
Submit
|
{!isUpdate ? "Create" : "Edit"}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
@@ -382,7 +367,7 @@ export const IdentityAwsAuthForm = ({
|
|||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{isCurrentAuthMethod && (
|
{isUpdate && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
colorSchema="danger"
|
colorSchema="danger"
|
||||||
@@ -395,19 +380,5 @@ export const IdentityAwsAuthForm = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<DeleteActionModal
|
|
||||||
isOpen={internalPopUpState.popUp.overwriteAuthMethod?.isOpen}
|
|
||||||
title={`Are you sure want to overwrite ${initialAuthMethod || "the auth method"} on ${
|
|
||||||
identityAuthMethodData?.name ?? ""
|
|
||||||
}?`}
|
|
||||||
onChange={(isOpen) => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", isOpen)}
|
|
||||||
deleteKey="confirm"
|
|
||||||
buttonText="Overwrite"
|
|
||||||
onDeleteApproved={async () => {
|
|
||||||
await revokeAuth(initialAuthMethod);
|
|
||||||
handleSubmit(onFormSubmit)();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+14
-52
@@ -6,7 +6,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, DeleteActionModal, FormControl, IconButton, Input } from "@app/components/v2";
|
import { Button, FormControl, IconButton, Input } from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityAzureAuth,
|
useAddIdentityAzureAuth,
|
||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { usePopUp, UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const schema = z
|
const schema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -50,18 +50,15 @@ type Props = {
|
|||||||
identityAuthMethodData: {
|
identityAuthMethodData: {
|
||||||
identityId: string;
|
identityId: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
configuredAuthMethods?: IdentityAuthMethod[];
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
};
|
};
|
||||||
initialAuthMethod: IdentityAuthMethod;
|
|
||||||
revokeAuth: (authMethod: IdentityAuthMethod) => Promise<void>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityAzureAuthForm = ({
|
export const IdentityAzureAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData,
|
identityAuthMethodData
|
||||||
initialAuthMethod,
|
|
||||||
revokeAuth
|
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -70,18 +67,18 @@ export const IdentityAzureAuthForm = ({
|
|||||||
const { mutateAsync: addMutateAsync } = useAddIdentityAzureAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityAzureAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAzureAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAzureAuth();
|
||||||
|
|
||||||
const isCurrentAuthMethod = identityAuthMethodData?.authMethod === initialAuthMethod;
|
const isUpdate = identityAuthMethodData?.configuredAuthMethods?.includes(
|
||||||
|
identityAuthMethodData.authMethod! || ""
|
||||||
|
);
|
||||||
const { data } = useGetIdentityAzureAuth(identityAuthMethodData?.identityId ?? "", {
|
const { data } = useGetIdentityAzureAuth(identityAuthMethodData?.identityId ?? "", {
|
||||||
enabled: isCurrentAuthMethod
|
enabled: isUpdate
|
||||||
});
|
});
|
||||||
|
|
||||||
const internalPopUpState = usePopUp(["overwriteAuthMethod"] as const);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
reset,
|
reset,
|
||||||
trigger,
|
|
||||||
formState: { isSubmitting }
|
formState: { isSubmitting }
|
||||||
} = useForm<FormData>({
|
} = useForm<FormData>({
|
||||||
resolver: zodResolver(schema),
|
resolver: zodResolver(schema),
|
||||||
@@ -173,21 +170,20 @@ export const IdentityAzureAuthForm = ({
|
|||||||
handlePopUpToggle("identityAuthMethod", false);
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Successfully ${isCurrentAuthMethod ? "updated" : "configured"} auth method`,
|
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Failed to ${identityAuthMethodData?.authMethod ? "update" : "configure"} identity`,
|
text: `Failed to ${isUpdate ? "update" : "configure"} identity`,
|
||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -342,17 +338,6 @@ export const IdentityAzureAuthForm = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{initialAuthMethod && identityAuthMethodData?.authMethod !== initialAuthMethod ? (
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
onClick={() => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", true)}
|
|
||||||
>
|
|
||||||
Overwrite
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -360,9 +345,9 @@ export const IdentityAzureAuthForm = ({
|
|||||||
isLoading={isSubmitting}
|
isLoading={isSubmitting}
|
||||||
isDisabled={isSubmitting}
|
isDisabled={isSubmitting}
|
||||||
>
|
>
|
||||||
Submit
|
{!isUpdate ? "Create" : "Edit"}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
@@ -371,7 +356,7 @@ export const IdentityAzureAuthForm = ({
|
|||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{isCurrentAuthMethod && (
|
{isUpdate && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
colorSchema="danger"
|
colorSchema="danger"
|
||||||
@@ -384,28 +369,5 @@ export const IdentityAzureAuthForm = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<DeleteActionModal
|
|
||||||
isOpen={internalPopUpState.popUp.overwriteAuthMethod?.isOpen}
|
|
||||||
title={`Are you sure want to overwrite ${initialAuthMethod || "the auth method"} on ${
|
|
||||||
identityAuthMethodData?.name ?? ""
|
|
||||||
}?`}
|
|
||||||
onChange={(isOpen) => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", isOpen)}
|
|
||||||
deleteKey="confirm"
|
|
||||||
buttonText="Overwrite"
|
|
||||||
onDeleteApproved={async () => {
|
|
||||||
const result = await trigger();
|
|
||||||
if (result) {
|
|
||||||
await revokeAuth(initialAuthMethod);
|
|
||||||
handleSubmit(onFormSubmit)();
|
|
||||||
} else {
|
|
||||||
createNotification({
|
|
||||||
text: "Please fill in all required fields",
|
|
||||||
type: "error"
|
|
||||||
});
|
|
||||||
internalPopUpState.handlePopUpToggle("overwriteAuthMethod", false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+14
-54
@@ -6,15 +6,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import {
|
import { Button, FormControl, IconButton, Input, Select, SelectItem } from "@app/components/v2";
|
||||||
Button,
|
|
||||||
DeleteActionModal,
|
|
||||||
FormControl,
|
|
||||||
IconButton,
|
|
||||||
Input,
|
|
||||||
Select,
|
|
||||||
SelectItem
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityGcpAuth,
|
useAddIdentityGcpAuth,
|
||||||
@@ -23,7 +15,7 @@ import {
|
|||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { usePopUp, UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const schema = z
|
const schema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -59,18 +51,15 @@ type Props = {
|
|||||||
identityAuthMethodData: {
|
identityAuthMethodData: {
|
||||||
identityId: string;
|
identityId: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
configuredAuthMethods?: IdentityAuthMethod[];
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
};
|
};
|
||||||
initialAuthMethod: IdentityAuthMethod;
|
|
||||||
revokeAuth: (authMethod: IdentityAuthMethod) => Promise<void>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityGcpAuthForm = ({
|
export const IdentityGcpAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData,
|
identityAuthMethodData
|
||||||
revokeAuth,
|
|
||||||
initialAuthMethod
|
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -79,11 +68,12 @@ export const IdentityGcpAuthForm = ({
|
|||||||
const { mutateAsync: addMutateAsync } = useAddIdentityGcpAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityGcpAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityGcpAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityGcpAuth();
|
||||||
|
|
||||||
const isCurrentAuthMethod = identityAuthMethodData?.authMethod === initialAuthMethod;
|
const isUpdate = identityAuthMethodData?.configuredAuthMethods?.includes(
|
||||||
|
identityAuthMethodData.authMethod! || ""
|
||||||
|
);
|
||||||
const { data } = useGetIdentityGcpAuth(identityAuthMethodData?.identityId ?? "", {
|
const { data } = useGetIdentityGcpAuth(identityAuthMethodData?.identityId ?? "", {
|
||||||
enabled: isCurrentAuthMethod
|
enabled: isUpdate
|
||||||
});
|
});
|
||||||
const internalPopUpState = usePopUp(["overwriteAuthMethod"] as const);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -189,21 +179,20 @@ export const IdentityGcpAuthForm = ({
|
|||||||
handlePopUpToggle("identityAuthMethod", false);
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Successfully ${isCurrentAuthMethod ? "updated" : "configured"} auth method`,
|
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Failed to ${identityAuthMethodData?.authMethod ? "update" : "configure"} identity`,
|
text: `Failed to ${isUpdate ? "update" : "configure"} identity`,
|
||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -264,11 +253,7 @@ export const IdentityGcpAuthForm = ({
|
|||||||
control={control}
|
control={control}
|
||||||
name="allowedZones"
|
name="allowedZones"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl label="Allowed Zones" isError={Boolean(error)} errorText={error?.message}>
|
||||||
label="Allowed Zones"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="us-west2-a, us-central1-a, ..." />
|
<Input {...field} placeholder="us-west2-a, us-central1-a, ..." />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
@@ -386,17 +371,6 @@ export const IdentityGcpAuthForm = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{initialAuthMethod && identityAuthMethodData?.authMethod !== initialAuthMethod ? (
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
onClick={() => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", true)}
|
|
||||||
>
|
|
||||||
Overwrite
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -404,9 +378,9 @@ export const IdentityGcpAuthForm = ({
|
|||||||
isLoading={isSubmitting}
|
isLoading={isSubmitting}
|
||||||
isDisabled={isSubmitting}
|
isDisabled={isSubmitting}
|
||||||
>
|
>
|
||||||
Submit
|
{!isUpdate ? "Create" : "Edit"}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
@@ -415,7 +389,7 @@ export const IdentityGcpAuthForm = ({
|
|||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{isCurrentAuthMethod && (
|
{isUpdate && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
colorSchema="danger"
|
colorSchema="danger"
|
||||||
@@ -428,19 +402,5 @@ export const IdentityGcpAuthForm = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<DeleteActionModal
|
|
||||||
isOpen={internalPopUpState.popUp.overwriteAuthMethod?.isOpen}
|
|
||||||
title={`Are you sure want to overwrite ${initialAuthMethod || "the auth method"} on ${
|
|
||||||
identityAuthMethodData?.name ?? ""
|
|
||||||
}?`}
|
|
||||||
buttonText="Overwrite"
|
|
||||||
onChange={(isOpen) => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", isOpen)}
|
|
||||||
deleteKey="confirm"
|
|
||||||
onDeleteApproved={async () => {
|
|
||||||
await revokeAuth(initialAuthMethod);
|
|
||||||
handleSubmit(onFormSubmit)();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+14
-58
@@ -6,14 +6,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import {
|
import { Button, FormControl, IconButton, Input, TextArea } from "@app/components/v2";
|
||||||
Button,
|
|
||||||
DeleteActionModal,
|
|
||||||
FormControl,
|
|
||||||
IconButton,
|
|
||||||
Input,
|
|
||||||
TextArea
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityKubernetesAuth,
|
useAddIdentityKubernetesAuth,
|
||||||
@@ -22,7 +15,7 @@ import {
|
|||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { usePopUp, UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const schema = z
|
const schema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -60,18 +53,15 @@ type Props = {
|
|||||||
identityAuthMethodData: {
|
identityAuthMethodData: {
|
||||||
identityId: string;
|
identityId: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
configuredAuthMethods?: IdentityAuthMethod[];
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
};
|
};
|
||||||
initialAuthMethod: IdentityAuthMethod;
|
|
||||||
revokeAuth: (authMethod: IdentityAuthMethod) => Promise<void>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityKubernetesAuthForm = ({
|
export const IdentityKubernetesAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData,
|
identityAuthMethodData
|
||||||
initialAuthMethod,
|
|
||||||
revokeAuth
|
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -80,17 +70,18 @@ export const IdentityKubernetesAuthForm = ({
|
|||||||
const { mutateAsync: addMutateAsync } = useAddIdentityKubernetesAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityKubernetesAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityKubernetesAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityKubernetesAuth();
|
||||||
|
|
||||||
const isCurrentAuthMethod = identityAuthMethodData?.authMethod === initialAuthMethod;
|
const isUpdate = identityAuthMethodData?.configuredAuthMethods?.includes(
|
||||||
|
identityAuthMethodData.authMethod! || ""
|
||||||
|
);
|
||||||
const { data } = useGetIdentityKubernetesAuth(identityAuthMethodData?.identityId ?? "", {
|
const { data } = useGetIdentityKubernetesAuth(identityAuthMethodData?.identityId ?? "", {
|
||||||
enabled: isCurrentAuthMethod
|
enabled: isUpdate
|
||||||
});
|
});
|
||||||
const internalPopUpState = usePopUp(["overwriteAuthMethod"] as const);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
reset,
|
reset,
|
||||||
trigger,
|
|
||||||
formState: { isSubmitting }
|
formState: { isSubmitting }
|
||||||
} = useForm<FormData>({
|
} = useForm<FormData>({
|
||||||
resolver: zodResolver(schema),
|
resolver: zodResolver(schema),
|
||||||
@@ -200,21 +191,20 @@ export const IdentityKubernetesAuthForm = ({
|
|||||||
handlePopUpToggle("identityAuthMethod", false);
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Successfully ${isCurrentAuthMethod ? "updated" : "configured"} auth method`,
|
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Failed to ${identityAuthMethodData?.authMethod ? "update" : "configure"} identity`,
|
text: `Failed to ${isUpdate ? "update" : "configure"} identity`,
|
||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -421,17 +411,6 @@ export const IdentityKubernetesAuthForm = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{initialAuthMethod && identityAuthMethodData?.authMethod !== initialAuthMethod ? (
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
onClick={() => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", true)}
|
|
||||||
>
|
|
||||||
Overwrite
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -439,9 +418,9 @@ export const IdentityKubernetesAuthForm = ({
|
|||||||
isLoading={isSubmitting}
|
isLoading={isSubmitting}
|
||||||
isDisabled={isSubmitting}
|
isDisabled={isSubmitting}
|
||||||
>
|
>
|
||||||
Submit
|
{isUpdate ? "Update" : "Create"}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
@@ -450,7 +429,7 @@ export const IdentityKubernetesAuthForm = ({
|
|||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{isCurrentAuthMethod && (
|
{isUpdate && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
colorSchema="danger"
|
colorSchema="danger"
|
||||||
@@ -463,28 +442,5 @@ export const IdentityKubernetesAuthForm = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<DeleteActionModal
|
|
||||||
isOpen={internalPopUpState.popUp.overwriteAuthMethod?.isOpen}
|
|
||||||
title={`Are you sure want to overwrite ${initialAuthMethod || "the auth method"} on ${
|
|
||||||
identityAuthMethodData?.name ?? ""
|
|
||||||
}?`}
|
|
||||||
onChange={(isOpen) => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", isOpen)}
|
|
||||||
deleteKey="confirm"
|
|
||||||
buttonText="Overwrite"
|
|
||||||
onDeleteApproved={async () => {
|
|
||||||
const result = await trigger();
|
|
||||||
if (result) {
|
|
||||||
await revokeAuth(initialAuthMethod);
|
|
||||||
handleSubmit(onFormSubmit)();
|
|
||||||
} else {
|
|
||||||
createNotification({
|
|
||||||
text: "Please fill in all required fields",
|
|
||||||
type: "error"
|
|
||||||
});
|
|
||||||
internalPopUpState.handlePopUpToggle("overwriteAuthMethod", false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
-6
@@ -154,12 +154,6 @@ export const IdentityModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
|
|
||||||
handlePopUpToggle("identity", false);
|
handlePopUpToggle("identity", false);
|
||||||
router.push(`/org/${orgId}/identities/${createdId}`);
|
router.push(`/org/${orgId}/identities/${createdId}`);
|
||||||
|
|
||||||
// handlePopUpOpen("identityAuthMethod", {
|
|
||||||
// identityId: createdId,
|
|
||||||
// name: createdName,
|
|
||||||
// authMethod
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
|
|||||||
+13
-59
@@ -7,21 +7,13 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import {
|
import { Button, FormControl, IconButton, Input, TextArea, Tooltip } from "@app/components/v2";
|
||||||
Button,
|
|
||||||
DeleteActionModal,
|
|
||||||
FormControl,
|
|
||||||
IconButton,
|
|
||||||
Input,
|
|
||||||
TextArea,
|
|
||||||
Tooltip
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import { useAddIdentityOidcAuth, useUpdateIdentityOidcAuth } from "@app/hooks/api";
|
import { useAddIdentityOidcAuth, useUpdateIdentityOidcAuth } from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
import { useGetIdentityOidcAuth } from "@app/hooks/api/identities/queries";
|
import { useGetIdentityOidcAuth } from "@app/hooks/api/identities/queries";
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { usePopUp, UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
accessTokenTrustedIps: z
|
accessTokenTrustedIps: z
|
||||||
@@ -62,18 +54,15 @@ type Props = {
|
|||||||
identityAuthMethodData: {
|
identityAuthMethodData: {
|
||||||
identityId: string;
|
identityId: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
configuredAuthMethods?: IdentityAuthMethod[];
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
};
|
};
|
||||||
initialAuthMethod: IdentityAuthMethod;
|
|
||||||
revokeAuth: (authMethod: IdentityAuthMethod) => Promise<void>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityOidcAuthForm = ({
|
export const IdentityOidcAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData,
|
identityAuthMethodData
|
||||||
initialAuthMethod,
|
|
||||||
revokeAuth
|
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -82,17 +71,17 @@ export const IdentityOidcAuthForm = ({
|
|||||||
const { mutateAsync: addMutateAsync } = useAddIdentityOidcAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityOidcAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityOidcAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityOidcAuth();
|
||||||
|
|
||||||
const isCurrentAuthMethod = identityAuthMethodData?.authMethod === initialAuthMethod;
|
const isUpdate = identityAuthMethodData?.configuredAuthMethods?.includes(
|
||||||
|
identityAuthMethodData.authMethod! || ""
|
||||||
|
);
|
||||||
const { data } = useGetIdentityOidcAuth(identityAuthMethodData?.identityId ?? "", {
|
const { data } = useGetIdentityOidcAuth(identityAuthMethodData?.identityId ?? "", {
|
||||||
enabled: isCurrentAuthMethod
|
enabled: isUpdate
|
||||||
});
|
});
|
||||||
const internalPopUpState = usePopUp(["overwriteAuthMethod"] as const);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
reset,
|
reset,
|
||||||
trigger,
|
|
||||||
formState: { isSubmitting }
|
formState: { isSubmitting }
|
||||||
} = useForm<FormData>({
|
} = useForm<FormData>({
|
||||||
resolver: zodResolver(schema),
|
resolver: zodResolver(schema),
|
||||||
@@ -210,21 +199,20 @@ export const IdentityOidcAuthForm = ({
|
|||||||
handlePopUpToggle("identityAuthMethod", false);
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Successfully ${isCurrentAuthMethod ? "updated" : "configured"} auth method`,
|
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Failed to ${identityAuthMethodData?.authMethod ? "update" : "configure"} identity`,
|
text: `Failed to ${isUpdate ? "update" : "configure"} identity`,
|
||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -504,17 +492,6 @@ export const IdentityOidcAuthForm = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{initialAuthMethod && identityAuthMethodData?.authMethod !== initialAuthMethod ? (
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
onClick={() => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", true)}
|
|
||||||
>
|
|
||||||
Overwrite
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -522,9 +499,9 @@ export const IdentityOidcAuthForm = ({
|
|||||||
isLoading={isSubmitting}
|
isLoading={isSubmitting}
|
||||||
isDisabled={isSubmitting}
|
isDisabled={isSubmitting}
|
||||||
>
|
>
|
||||||
Submit
|
{isUpdate ? "Update" : "Create"}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
@@ -533,7 +510,7 @@ export const IdentityOidcAuthForm = ({
|
|||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{isCurrentAuthMethod && (
|
{isUpdate && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
colorSchema="danger"
|
colorSchema="danger"
|
||||||
@@ -546,28 +523,5 @@ export const IdentityOidcAuthForm = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<DeleteActionModal
|
|
||||||
isOpen={internalPopUpState.popUp.overwriteAuthMethod?.isOpen}
|
|
||||||
title={`Are you sure want to overwrite ${initialAuthMethod || "the auth method"} on ${
|
|
||||||
identityAuthMethodData?.name ?? ""
|
|
||||||
}?`}
|
|
||||||
onChange={(isOpen) => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", isOpen)}
|
|
||||||
deleteKey="confirm"
|
|
||||||
buttonText="Overwrite"
|
|
||||||
onDeleteApproved={async () => {
|
|
||||||
const result = await trigger();
|
|
||||||
if (result) {
|
|
||||||
await revokeAuth(initialAuthMethod);
|
|
||||||
handleSubmit(onFormSubmit)();
|
|
||||||
} else {
|
|
||||||
createNotification({
|
|
||||||
text: "Please fill in all required fields",
|
|
||||||
type: "error"
|
|
||||||
});
|
|
||||||
internalPopUpState.handlePopUpToggle("overwriteAuthMethod", false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+15
-42
@@ -5,7 +5,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, DeleteActionModal, FormControl, IconButton, Input } from "@app/components/v2";
|
import { Button, FormControl, IconButton, Input } from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityTokenAuth,
|
useAddIdentityTokenAuth,
|
||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
useUpdateIdentityTokenAuth
|
useUpdateIdentityTokenAuth
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
import { usePopUp, UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const schema = z
|
const schema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -42,21 +42,18 @@ type Props = {
|
|||||||
popUpName: keyof UsePopUpState<["identityAuthMethod", "revokeAuthMethod"]>,
|
popUpName: keyof UsePopUpState<["identityAuthMethod", "revokeAuthMethod"]>,
|
||||||
state?: boolean
|
state?: boolean
|
||||||
) => void;
|
) => void;
|
||||||
identityAuthMethodData: {
|
identityAuthMethodData?: {
|
||||||
identityId: string;
|
identityId: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
configuredAuthMethods?: IdentityAuthMethod[];
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
};
|
};
|
||||||
initialAuthMethod: IdentityAuthMethod;
|
|
||||||
revokeAuth: (authMethod: IdentityAuthMethod) => Promise<void>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityTokenAuthForm = ({
|
export const IdentityTokenAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData,
|
identityAuthMethodData
|
||||||
initialAuthMethod,
|
|
||||||
revokeAuth
|
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -65,11 +62,13 @@ export const IdentityTokenAuthForm = ({
|
|||||||
const { mutateAsync: addMutateAsync } = useAddIdentityTokenAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityTokenAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityTokenAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityTokenAuth();
|
||||||
|
|
||||||
const isCurrentAuthMethod = identityAuthMethodData?.authMethod === initialAuthMethod;
|
const isUpdate = identityAuthMethodData?.configuredAuthMethods?.includes(
|
||||||
|
identityAuthMethodData.authMethod! || ""
|
||||||
|
);
|
||||||
|
|
||||||
const { data } = useGetIdentityTokenAuth(identityAuthMethodData?.identityId ?? "", {
|
const { data } = useGetIdentityTokenAuth(identityAuthMethodData?.identityId ?? "", {
|
||||||
enabled: isCurrentAuthMethod
|
enabled: isUpdate
|
||||||
});
|
});
|
||||||
const internalPopUpState = usePopUp(["overwriteAuthMethod"] as const);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -124,21 +123,20 @@ export const IdentityTokenAuthForm = ({
|
|||||||
handlePopUpToggle("identityAuthMethod", false);
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Successfully ${isCurrentAuthMethod ? "updated" : "configured"} auth method`,
|
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Failed to ${isCurrentAuthMethod ? "update" : "configure"} identity`,
|
text: `Failed to ${isUpdate ? "update" : "configure"} identity`,
|
||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -252,17 +250,6 @@ export const IdentityTokenAuthForm = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{initialAuthMethod && identityAuthMethodData?.authMethod !== initialAuthMethod ? (
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
onClick={() => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", true)}
|
|
||||||
>
|
|
||||||
Overwrite
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -270,9 +257,9 @@ export const IdentityTokenAuthForm = ({
|
|||||||
isLoading={isSubmitting}
|
isLoading={isSubmitting}
|
||||||
isDisabled={isSubmitting}
|
isDisabled={isSubmitting}
|
||||||
>
|
>
|
||||||
Submit
|
{isUpdate ? "Update" : "Create"}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
@@ -281,7 +268,7 @@ export const IdentityTokenAuthForm = ({
|
|||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{isCurrentAuthMethod && (
|
{isUpdate && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
colorSchema="danger"
|
colorSchema="danger"
|
||||||
@@ -294,19 +281,5 @@ export const IdentityTokenAuthForm = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<DeleteActionModal
|
|
||||||
isOpen={internalPopUpState.popUp.overwriteAuthMethod?.isOpen}
|
|
||||||
title={`Are you sure want to overwrite ${initialAuthMethod || "the auth method"} on ${
|
|
||||||
identityAuthMethodData?.name ?? ""
|
|
||||||
}?`}
|
|
||||||
onChange={(isOpen) => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", isOpen)}
|
|
||||||
deleteKey="confirm"
|
|
||||||
buttonText="Overwrite"
|
|
||||||
onDeleteApproved={async () => {
|
|
||||||
await revokeAuth(initialAuthMethod);
|
|
||||||
handleSubmit(onFormSubmit)();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+15
-44
@@ -6,7 +6,7 @@ import { yupResolver } from "@hookform/resolvers/yup";
|
|||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, DeleteActionModal, FormControl, IconButton, Input } from "@app/components/v2";
|
import { Button, FormControl, IconButton, Input } from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityUniversalAuth,
|
useAddIdentityUniversalAuth,
|
||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { usePopUp, UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const schema = yup
|
const schema = yup
|
||||||
.object({
|
.object({
|
||||||
@@ -65,21 +65,18 @@ type Props = {
|
|||||||
popUpName: keyof UsePopUpState<["identityAuthMethod", "revokeAuthMethod"]>,
|
popUpName: keyof UsePopUpState<["identityAuthMethod", "revokeAuthMethod"]>,
|
||||||
state?: boolean
|
state?: boolean
|
||||||
) => void;
|
) => void;
|
||||||
identityAuthMethodData: {
|
identityAuthMethodData?: {
|
||||||
identityId: string;
|
identityId: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
configuredAuthMethods?: IdentityAuthMethod[];
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
};
|
};
|
||||||
initialAuthMethod: IdentityAuthMethod;
|
|
||||||
revokeAuth: (authMethod: IdentityAuthMethod) => Promise<void>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityUniversalAuthForm = ({
|
export const IdentityUniversalAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData,
|
identityAuthMethodData
|
||||||
initialAuthMethod,
|
|
||||||
revokeAuth
|
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -87,11 +84,13 @@ export const IdentityUniversalAuthForm = ({
|
|||||||
const { mutateAsync: addMutateAsync } = useAddIdentityUniversalAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityUniversalAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityUniversalAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityUniversalAuth();
|
||||||
|
|
||||||
const isCurrentAuthMethod = identityAuthMethodData?.authMethod === initialAuthMethod;
|
const isUpdate = identityAuthMethodData?.configuredAuthMethods?.includes(
|
||||||
|
identityAuthMethodData.authMethod! || ""
|
||||||
|
);
|
||||||
|
|
||||||
const { data } = useGetIdentityUniversalAuth(identityAuthMethodData?.identityId ?? "", {
|
const { data } = useGetIdentityUniversalAuth(identityAuthMethodData?.identityId ?? "", {
|
||||||
enabled: isCurrentAuthMethod
|
enabled: isUpdate
|
||||||
});
|
});
|
||||||
const internalPopUpState = usePopUp(["overwriteAuthMethod"] as const);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -190,7 +189,7 @@ export const IdentityUniversalAuthForm = ({
|
|||||||
handlePopUpToggle("identityAuthMethod", false);
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Successfully ${isCurrentAuthMethod ? "updated" : "configured"} auth method`,
|
text: `Successfully ${isUpdate ? "updated" : "created"} auth method`,
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -199,8 +198,7 @@ export const IdentityUniversalAuthForm = ({
|
|||||||
console.error(err);
|
console.error(err);
|
||||||
const error = err as any;
|
const error = err as any;
|
||||||
const text =
|
const text =
|
||||||
error?.response?.data?.message ??
|
error?.response?.data?.message ?? `Failed to ${isUpdate ? "update" : "configure"} identity`;
|
||||||
`Failed to ${identityAuthMethodData?.authMethod ? "update" : "configure"} identity`;
|
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text,
|
text,
|
||||||
@@ -210,7 +208,6 @@ export const IdentityUniversalAuthForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -392,17 +389,6 @@ export const IdentityUniversalAuthForm = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{initialAuthMethod && identityAuthMethodData?.authMethod !== initialAuthMethod ? (
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
onClick={() => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", true)}
|
|
||||||
>
|
|
||||||
Overwrite
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -410,9 +396,8 @@ export const IdentityUniversalAuthForm = ({
|
|||||||
isLoading={isSubmitting}
|
isLoading={isSubmitting}
|
||||||
isDisabled={isSubmitting}
|
isDisabled={isSubmitting}
|
||||||
>
|
>
|
||||||
Submit
|
{isUpdate ? "Edit" : "Create"}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
@@ -421,7 +406,7 @@ export const IdentityUniversalAuthForm = ({
|
|||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{isCurrentAuthMethod && (
|
{isUpdate && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
colorSchema="danger"
|
colorSchema="danger"
|
||||||
@@ -429,24 +414,10 @@ export const IdentityUniversalAuthForm = ({
|
|||||||
isDisabled={isSubmitting}
|
isDisabled={isSubmitting}
|
||||||
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
||||||
>
|
>
|
||||||
Remove Auth Method
|
Delete Auth Method
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<DeleteActionModal
|
|
||||||
isOpen={internalPopUpState.popUp.overwriteAuthMethod?.isOpen}
|
|
||||||
title={`Are you sure want to overwrite ${initialAuthMethod || "the auth method"} on ${
|
|
||||||
identityAuthMethodData?.name ?? ""
|
|
||||||
}?`}
|
|
||||||
onChange={(isOpen) => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", isOpen)}
|
|
||||||
deleteKey="confirm"
|
|
||||||
buttonText="Overwrite"
|
|
||||||
onDeleteApproved={async () => {
|
|
||||||
await revokeAuth(initialAuthMethod);
|
|
||||||
handleSubmit(onFormSubmit)();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user