Make fixes based on review

This commit is contained in:
Tuan Dang
2024-07-09 11:51:21 +07:00
parent 96e485910c
commit 59ccabec69
25 changed files with 357 additions and 276 deletions
+1 -2
View File
@@ -816,8 +816,7 @@ export const registerRoutes = async (
}); });
const identityAccessTokenService = identityAccessTokenServiceFactory({ const identityAccessTokenService = identityAccessTokenServiceFactory({
identityAccessTokenDAL, identityAccessTokenDAL,
identityOrgMembershipDAL, identityOrgMembershipDAL
permissionService
}); });
const identityProjectService = identityProjectServiceFactory({ const identityProjectService = identityProjectServiceFactory({
permissionService, permissionService,
@@ -2,8 +2,6 @@ import { z } from "zod";
import { UNIVERSAL_AUTH } from "@app/lib/api-docs"; import { UNIVERSAL_AUTH } from "@app/lib/api-docs";
import { writeLimit } from "@app/server/config/rateLimiter"; import { writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvider) => { export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
@@ -63,37 +61,4 @@ export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvid
}; };
} }
}); });
server.route({
url: "/token/revoke-by-id",
method: "POST",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Revoke access token by the id of the token",
body: z.object({
tokenId: z.string().trim()
}),
response: {
200: z.object({
message: z.string()
})
}
},
handler: async (req) => {
await server.services.identityAccessToken.revokeAccessTokenById({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
return {
message: "Successfully revoked access token"
};
}
});
}; };
@@ -253,6 +253,15 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
} }
}); });
// proposed
// update token by id: PATCH /token-auth/tokens/:tokenId
// revoke token by id: POST /token-auth/tokens/:tokenId/revoke
// current
// revoke token by id: POST /token/revoke-by-id
// token-auth/identities/:identityId/tokens
server.route({ server.route({
method: "POST", method: "POST",
url: "/token-auth/identities/:identityId/tokens", url: "/token-auth/identities/:identityId/tokens",
@@ -284,7 +293,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
}, },
handler: async (req) => { handler: async (req) => {
const { identityTokenAuth, accessToken, identityAccessToken, identityMembershipOrg } = const { identityTokenAuth, accessToken, identityAccessToken, identityMembershipOrg } =
await server.services.identityTokenAuth.createTokenTokenAuth({ await server.services.identityTokenAuth.createTokenAuthToken({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
@@ -342,7 +351,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
} }
}, },
handler: async (req) => { handler: async (req) => {
const { tokens, identityMembershipOrg } = await server.services.identityTokenAuth.getTokensTokenAuth({ const { tokens, identityMembershipOrg } = await server.services.identityTokenAuth.getTokenAuthTokens({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
@@ -368,7 +377,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
server.route({ server.route({
method: "PATCH", method: "PATCH",
url: "/token-auth/identities/:identityId/tokens/:tokenId", url: "/token-auth/tokens/:tokenId",
config: { config: {
rateLimit: writeLimit rateLimit: writeLimit
}, },
@@ -381,7 +390,6 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
} }
], ],
params: z.object({ params: z.object({
identityId: z.string(),
tokenId: z.string() tokenId: z.string()
}), }),
body: z.object({ body: z.object({
@@ -394,12 +402,11 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
} }
}, },
handler: async (req) => { handler: async (req) => {
const { token, identityMembershipOrg } = await server.services.identityTokenAuth.updateTokenTokenAuth({ const { token, identityMembershipOrg } = await server.services.identityTokenAuth.updateTokenAuthToken({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
identityId: req.params.identityId,
tokenId: req.params.tokenId, tokenId: req.params.tokenId,
...req.body ...req.body
}); });
@@ -410,7 +417,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
event: { event: {
type: EventType.UPDATE_TOKEN_IDENTITY_TOKEN_AUTH, type: EventType.UPDATE_TOKEN_IDENTITY_TOKEN_AUTH,
metadata: { metadata: {
identityId: req.params.identityId, identityId: token.identityId,
tokenId: token.id, tokenId: token.id,
name: req.body.name name: req.body.name
} }
@@ -420,4 +427,42 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
return { token }; return { token };
} }
}); });
server.route({
method: "POST",
url: "/token-auth/tokens/:tokenId/revoke",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Revoke token for identity with Token Auth configured",
security: [
{
bearerAuth: []
}
],
params: z.object({
tokenId: z.string()
}),
response: {
200: z.object({
message: z.string()
})
}
},
handler: async (req) => {
await server.services.identityTokenAuth.revokeTokenAuthToken({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
tokenId: req.params.tokenId
});
return {
message: "Successfully revoked access token"
};
}
});
}; };
@@ -1,9 +1,6 @@
import { ForbiddenError } from "@casl/ability";
import jwt, { JwtPayload } from "jsonwebtoken"; import jwt, { JwtPayload } from "jsonwebtoken";
import { TableName, TIdentityAccessTokens } from "@app/db/schemas"; import { TableName, TIdentityAccessTokens } from "@app/db/schemas";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
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";
@@ -11,24 +8,18 @@ import { checkIPAgainstBlocklist, TIp } from "@app/lib/ip";
import { AuthTokenType } from "../auth/auth-type"; import { AuthTokenType } from "../auth/auth-type";
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
import { TIdentityAccessTokenDALFactory } from "./identity-access-token-dal"; import { TIdentityAccessTokenDALFactory } from "./identity-access-token-dal";
import { import { TIdentityAccessTokenJwtPayload, TRenewAccessTokenDTO } from "./identity-access-token-types";
TIdentityAccessTokenJwtPayload,
TRenewAccessTokenDTO,
TRevokeAccessTokenByIdDTO
} from "./identity-access-token-types";
type TIdentityAccessTokenServiceFactoryDep = { type TIdentityAccessTokenServiceFactoryDep = {
identityAccessTokenDAL: TIdentityAccessTokenDALFactory; identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
identityOrgMembershipDAL: TIdentityOrgDALFactory; identityOrgMembershipDAL: TIdentityOrgDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
}; };
export type TIdentityAccessTokenServiceFactory = ReturnType<typeof identityAccessTokenServiceFactory>; export type TIdentityAccessTokenServiceFactory = ReturnType<typeof identityAccessTokenServiceFactory>;
export const identityAccessTokenServiceFactory = ({ export const identityAccessTokenServiceFactory = ({
identityAccessTokenDAL, identityAccessTokenDAL,
identityOrgMembershipDAL, identityOrgMembershipDAL
permissionService
}: TIdentityAccessTokenServiceFactoryDep) => { }: TIdentityAccessTokenServiceFactoryDep) => {
const validateAccessTokenExp = async (identityAccessToken: TIdentityAccessTokens) => { const validateAccessTokenExp = async (identityAccessToken: TIdentityAccessTokens) => {
const { const {
@@ -147,43 +138,6 @@ export const identityAccessTokenServiceFactory = ({
return { revokedToken }; return { revokedToken };
}; };
const revokeAccessTokenById = async ({
tokenId,
actorId,
actor,
actorAuthMethod,
actorOrgId
}: TRevokeAccessTokenByIdDTO) => {
const identityAccessToken = await identityAccessTokenDAL.findOne({
[`${TableName.IdentityAccessToken}.id` as "id"]: tokenId,
isAccessTokenRevoked: false
});
if (!identityAccessToken) throw new UnauthorizedError();
const identityOrgMembership = await identityOrgMembershipDAL.findOne({
identityId: identityAccessToken.identityId
});
if (!identityOrgMembership) {
throw new UnauthorizedError({ message: "Identity does not belong to any organization" });
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
identityOrgMembership.orgId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
const revokedToken = await identityAccessTokenDAL.updateById(identityAccessToken.id, {
isAccessTokenRevoked: true
});
return { revokedToken };
};
const fnValidateIdentityAccessToken = async (token: TIdentityAccessTokenJwtPayload, ipAddress?: string) => { const fnValidateIdentityAccessToken = async (token: TIdentityAccessTokenJwtPayload, ipAddress?: string) => {
const identityAccessToken = await identityAccessTokenDAL.findOne({ const identityAccessToken = await identityAccessTokenDAL.findOne({
[`${TableName.IdentityAccessToken}.id` as "id"]: token.identityAccessTokenId, [`${TableName.IdentityAccessToken}.id` as "id"]: token.identityAccessTokenId,
@@ -221,5 +175,5 @@ export const identityAccessTokenServiceFactory = ({
return { ...identityAccessToken, orgId: identityOrgMembership.orgId }; return { ...identityAccessToken, orgId: identityOrgMembership.orgId };
}; };
return { renewAccessToken, revokeAccessToken, revokeAccessTokenById, fnValidateIdentityAccessToken }; return { renewAccessToken, revokeAccessToken, fnValidateIdentityAccessToken };
}; };
@@ -1,5 +1,3 @@
import { TProjectPermission } from "@app/lib/types";
export type TRenewAccessTokenDTO = { export type TRenewAccessTokenDTO = {
accessToken: string; accessToken: string;
}; };
@@ -10,7 +8,3 @@ export type TIdentityAccessTokenJwtPayload = {
identityAccessTokenId: string; identityAccessTokenId: string;
authTokenType: string; authTokenType: string;
}; };
export type TRevokeAccessTokenByIdDTO = {
tokenId: string;
} & Omit<TProjectPermission, "projectId">;
@@ -1,13 +1,13 @@
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError } from "@casl/ability";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import { IdentityAuthMethod } from "@app/db/schemas"; import { IdentityAuthMethod, TableName } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { isAtLeastAsPrivileged } from "@app/lib/casl";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
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";
@@ -18,12 +18,13 @@ import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identit
import { TIdentityTokenAuthDALFactory } from "./identity-token-auth-dal"; import { TIdentityTokenAuthDALFactory } from "./identity-token-auth-dal";
import { import {
TAttachTokenAuthDTO, TAttachTokenAuthDTO,
TCreateTokenTokenAuthDTO, TCreateTokenAuthTokenDTO,
TGetTokenAuthDTO, TGetTokenAuthDTO,
TGetTokensTokenAuthDTO, TGetTokenAuthTokensDTO,
TRevokeTokenAuthDTO, TRevokeTokenAuthDTO,
TRevokeTokenAuthTokenDTO,
TUpdateTokenAuthDTO, TUpdateTokenAuthDTO,
TUpdateTokenTokenAuthDTO TUpdateTokenAuthTokenDTO
} from "./identity-token-auth-types"; } from "./identity-token-auth-types";
type TIdentityTokenAuthServiceFactoryDep = { type TIdentityTokenAuthServiceFactoryDep = {
@@ -33,7 +34,10 @@ type TIdentityTokenAuthServiceFactoryDep = {
>; >;
identityDAL: Pick<TIdentityDALFactory, "updateById">; identityDAL: Pick<TIdentityDALFactory, "updateById">;
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">; identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "find" | "update">; identityAccessTokenDAL: Pick<
TIdentityAccessTokenDALFactory,
"create" | "find" | "update" | "findById" | "findOne" | "updateById"
>;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
}; };
@@ -243,7 +247,7 @@ export const identityTokenAuthServiceFactory = ({
); );
const hasPriviledge = isAtLeastAsPrivileged(permission, rolePermission); const hasPriviledge = isAtLeastAsPrivileged(permission, rolePermission);
if (!hasPriviledge) if (!hasPriviledge)
throw new ForbiddenRequestError({ throw new UnauthorizedError({
message: "Failed to revoke Token Auth of identity with more privileged role" message: "Failed to revoke Token Auth of identity with more privileged role"
}); });
@@ -255,14 +259,14 @@ export const identityTokenAuthServiceFactory = ({
return revokedIdentityTokenAuth; return revokedIdentityTokenAuth;
}; };
const createTokenTokenAuth = async ({ const createTokenAuthToken = async ({
identityId, identityId,
actorId, actorId,
actor, actor,
actorAuthMethod, actorAuthMethod,
actorOrgId, actorOrgId,
name name
}: TCreateTokenTokenAuthDTO) => { }: TCreateTokenAuthTokenDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" }); if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.TOKEN_AUTH) if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.TOKEN_AUTH)
@@ -328,7 +332,7 @@ export const identityTokenAuthServiceFactory = ({
return { accessToken, identityTokenAuth, identityAccessToken, identityMembershipOrg }; return { accessToken, identityTokenAuth, identityAccessToken, identityMembershipOrg };
}; };
const getTokensTokenAuth = async ({ const getTokenAuthTokens = async ({
identityId, identityId,
offset = 0, offset = 0,
limit = 20, limit = 20,
@@ -336,7 +340,7 @@ export const identityTokenAuthServiceFactory = ({
actor, actor,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
}: TGetTokensTokenAuthDTO) => { }: TGetTokenAuthTokensDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" }); if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.TOKEN_AUTH) if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.TOKEN_AUTH)
@@ -350,20 +354,7 @@ export const identityTokenAuthServiceFactory = ({
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
const { permission: rolePermission } = await permissionService.getOrgPermission(
ActorType.IDENTITY,
identityMembershipOrg.identityId,
identityMembershipOrg.orgId,
actorAuthMethod,
actorOrgId
);
const hasPriviledge = isAtLeastAsPrivileged(permission, rolePermission);
if (!hasPriviledge)
throw new ForbiddenRequestError({
message: "Failed to get tokens for identity with more privileged role"
});
const tokens = await identityAccessTokenDAL.find( const tokens = await identityAccessTokenDAL.find(
{ {
@@ -375,16 +366,17 @@ export const identityTokenAuthServiceFactory = ({
return { tokens, identityMembershipOrg }; return { tokens, identityMembershipOrg };
}; };
const updateTokenTokenAuth = async ({ const updateTokenAuthToken = async ({
identityId,
tokenId, tokenId,
name, name,
actorId, actorId,
actor, actor,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
}: TUpdateTokenTokenAuthDTO) => { }: TUpdateTokenAuthTokenDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); const foundToken = await identityAccessTokenDAL.findById(tokenId);
if (!foundToken) throw new NotFoundError({ message: "Failed to find token" });
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: foundToken.identityId });
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" }); if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.TOKEN_AUTH) if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.TOKEN_AUTH)
throw new BadRequestError({ throw new BadRequestError({
@@ -414,7 +406,7 @@ export const identityTokenAuthServiceFactory = ({
const [token] = await identityAccessTokenDAL.update( const [token] = await identityAccessTokenDAL.update(
{ {
identityId, identityId: foundToken.identityId,
id: tokenId id: tokenId
}, },
{ {
@@ -425,13 +417,54 @@ export const identityTokenAuthServiceFactory = ({
return { token, identityMembershipOrg }; return { token, identityMembershipOrg };
}; };
const revokeTokenAuthToken = async ({
tokenId,
actorId,
actor,
actorAuthMethod,
actorOrgId
}: TRevokeTokenAuthTokenDTO) => {
const identityAccessToken = await identityAccessTokenDAL.findOne({
[`${TableName.IdentityAccessToken}.id` as "id"]: tokenId,
isAccessTokenRevoked: false
});
if (!identityAccessToken)
throw new NotFoundError({
message: "Failed to find token"
});
const identityOrgMembership = await identityOrgMembershipDAL.findOne({
identityId: identityAccessToken.identityId
});
if (!identityOrgMembership) {
throw new UnauthorizedError({ message: "Identity does not belong to any organization" });
}
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
identityOrgMembership.orgId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
const revokedToken = await identityAccessTokenDAL.updateById(identityAccessToken.id, {
isAccessTokenRevoked: true
});
return { revokedToken };
};
return { return {
attachTokenAuth, attachTokenAuth,
updateTokenAuth, updateTokenAuth,
getTokenAuth, getTokenAuth,
revokeIdentityTokenAuth, revokeIdentityTokenAuth,
createTokenTokenAuth, createTokenAuthToken,
getTokensTokenAuth, getTokenAuthTokens,
updateTokenTokenAuth updateTokenAuthToken,
revokeTokenAuthToken
}; };
}; };
@@ -24,19 +24,22 @@ export type TRevokeTokenAuthDTO = {
identityId: string; identityId: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TCreateTokenTokenAuthDTO = { export type TCreateTokenAuthTokenDTO = {
identityId: string; identityId: string;
name?: string; name?: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TGetTokensTokenAuthDTO = { export type TGetTokenAuthTokensDTO = {
identityId: string; identityId: string;
offset: number; offset: number;
limit: number; limit: number;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TUpdateTokenTokenAuthDTO = { export type TUpdateTokenAuthTokenDTO = {
identityId: string;
tokenId: string; tokenId: string;
name?: string; name?: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TRevokeTokenAuthTokenDTO = {
tokenId: string;
} & Omit<TProjectPermission, "projectId">;
@@ -5,7 +5,7 @@ import { TLicenseServiceFactory } from "@app/ee/services/license/license-service
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { isAtLeastAsPrivileged } from "@app/lib/casl";
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { TOrgPermission } from "@app/lib/types"; import { TOrgPermission } from "@app/lib/types";
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal"; import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
@@ -213,7 +213,7 @@ export const identityServiceFactory = ({
actorOrgId actorOrgId
}: TListProjectIdentitiesByIdentityIdDTO) => { }: TListProjectIdentitiesByIdentityIdDTO) => {
const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId }); const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityOrgMembership) throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` }); if (!identityOrgMembership) throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
const { permission } = await permissionService.getOrgPermission( const { permission } = await permissionService.getOrgPermission(
actor, actor,
+5 -4
View File
@@ -17,16 +17,16 @@ export {
useDeleteIdentityKubernetesAuth, useDeleteIdentityKubernetesAuth,
useDeleteIdentityTokenAuth, useDeleteIdentityTokenAuth,
useDeleteIdentityUniversalAuth, useDeleteIdentityUniversalAuth,
useRevokeIdentityTokenAuthToken,
useRevokeIdentityUniversalAuthClientSecret, useRevokeIdentityUniversalAuthClientSecret,
useRevokeToken,
useUpdateIdentity, useUpdateIdentity,
useUpdateIdentityAwsAuth, useUpdateIdentityAwsAuth,
useUpdateIdentityAzureAuth, useUpdateIdentityAzureAuth,
useUpdateIdentityGcpAuth, useUpdateIdentityGcpAuth,
useUpdateIdentityKubernetesAuth, useUpdateIdentityKubernetesAuth,
useUpdateIdentityTokenAuth, useUpdateIdentityTokenAuth,
useUpdateIdentityUniversalAuth, useUpdateIdentityTokenAuthToken,
useUpdateTokenIdentityTokenAuth} from "./mutations"; useUpdateIdentityUniversalAuth} from "./mutations";
export { export {
useGetIdentityAwsAuth, useGetIdentityAwsAuth,
useGetIdentityAzureAuth, useGetIdentityAzureAuth,
@@ -37,4 +37,5 @@ export {
useGetIdentityTokenAuth, useGetIdentityTokenAuth,
useGetIdentityTokensTokenAuth, useGetIdentityTokensTokenAuth,
useGetIdentityUniversalAuth, useGetIdentityUniversalAuth,
useGetIdentityUniversalAuthClientSecrets} from "./queries"; useGetIdentityUniversalAuthClientSecrets
} from "./queries";
@@ -42,7 +42,8 @@ import {
UpdateIdentityKubernetesAuthDTO, UpdateIdentityKubernetesAuthDTO,
UpdateIdentityTokenAuthDTO, UpdateIdentityTokenAuthDTO,
UpdateIdentityUniversalAuthDTO, UpdateIdentityUniversalAuthDTO,
UpdateTokenIdentityTokenAuthDTO} from "./types"; UpdateTokenIdentityTokenAuthDTO
} from "./types";
export const useCreateIdentity = () => { export const useCreateIdentity = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@@ -706,14 +707,14 @@ export const useCreateTokenIdentityTokenAuth = () => {
}); });
}; };
export const useUpdateTokenIdentityTokenAuth = () => { export const useUpdateIdentityTokenAuthToken = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<IdentityAccessToken, {}, UpdateTokenIdentityTokenAuthDTO>({ return useMutation<IdentityAccessToken, {}, UpdateTokenIdentityTokenAuthDTO>({
mutationFn: async ({ identityId, tokenId, name }) => { mutationFn: async ({ tokenId, name }) => {
const { const {
data: { token } data: { token }
} = await apiRequest.patch<{ token: IdentityAccessToken }>( } = await apiRequest.patch<{ token: IdentityAccessToken }>(
`/api/v1/auth/token-auth/identities/${identityId}/tokens/${tokenId}`, `/api/v1/auth/token-auth/tokens/${tokenId}`,
{ {
name name
} }
@@ -727,13 +728,13 @@ export const useUpdateTokenIdentityTokenAuth = () => {
}); });
}; };
export const useRevokeToken = () => { export const useRevokeIdentityTokenAuthToken = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<RevokeTokenRes, {}, RevokeTokenDTO>({ return useMutation<RevokeTokenRes, {}, RevokeTokenDTO>({
mutationFn: async ({ tokenId }) => { mutationFn: async ({ tokenId }) => {
const { data } = await apiRequest.post<RevokeTokenRes>("/api/v1/auth/token/revoke-by-id", { const { data } = await apiRequest.post<RevokeTokenRes>(
tokenId `/api/v1/auth/token-auth/tokens/${tokenId}/revoke`
}); );
return data; return data;
}, },
@@ -21,9 +21,8 @@ import { withPermission } from "@app/hoc";
import { import {
useDeleteIdentity, useDeleteIdentity,
useGetIdentityById, useGetIdentityById,
useRevokeIdentityUniversalAuthClientSecret, useRevokeIdentityTokenAuthToken,
useRevokeToken useRevokeIdentityUniversalAuthClientSecret} from "@app/hooks/api";
} from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp"; import { usePopUp } from "@app/hooks/usePopUp";
import { IdentityAuthMethodModal } from "../MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityAuthMethodModal"; import { IdentityAuthMethodModal } from "../MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityAuthMethodModal";
@@ -35,7 +34,8 @@ import {
IdentityDetailsSection, IdentityDetailsSection,
IdentityProjectsSection, IdentityProjectsSection,
IdentityTokenListModal, IdentityTokenListModal,
IdentityTokenModal} from "./components"; IdentityTokenModal
} from "./components";
export const IdentityPage = withPermission( export const IdentityPage = withPermission(
() => { () => {
@@ -45,13 +45,14 @@ export const IdentityPage = withPermission(
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { data } = useGetIdentityById(identityId); const { data } = useGetIdentityById(identityId);
const { mutateAsync: deleteIdentity } = useDeleteIdentity(); const { mutateAsync: deleteIdentity } = useDeleteIdentity();
const { mutateAsync: revokeToken } = useRevokeToken(); const { mutateAsync: revokeToken } = useRevokeIdentityTokenAuthToken();
const { mutateAsync: revokeClientSecret } = useRevokeIdentityUniversalAuthClientSecret(); const { mutateAsync: revokeClientSecret } = useRevokeIdentityUniversalAuthClientSecret();
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"identity", "identity",
"deleteIdentity", "deleteIdentity",
"identityAuthMethod", "identityAuthMethod",
"revokeAuthMethod",
"token", "token",
"tokenList", "tokenList",
"revokeToken", "revokeToken",
@@ -66,18 +66,6 @@ export const IdentityAuthenticationSection = ({ identityId, handlePopUpOpen }: P
<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="text-sm font-semibold text-mineshaft-300">Auth Method</p>
{/* <Button
variant="link"
onClick={() => {
handlePopUpOpen("identityAuthMethod", {
identityId,
name: data.identity.name,
authMethod: data.identity.authMethod
});
}}
>
Manage
</Button> */}
</div> </div>
<p className="text-sm text-mineshaft-300"> <p className="text-sm text-mineshaft-300">
{data.identity.authMethod {data.identity.authMethod
@@ -1,10 +1,11 @@
import { faKey, faTrash } from "@fortawesome/free-solid-svg-icons"; import { faCheck, faCopy,faKey, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format } from "date-fns"; import { format } from "date-fns";
import { OrgPermissionCan } from "@app/components/permissions"; import { OrgPermissionCan } from "@app/components/permissions";
import { Button, IconButton, Tooltip } from "@app/components/v2"; import { Button, IconButton, Tooltip } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { useTimedReset } from "@app/hooks";
import { import {
useGetIdentityById, useGetIdentityById,
useGetIdentityUniversalAuth, useGetIdentityUniversalAuth,
@@ -25,6 +26,10 @@ type Props = {
const SHOW_LIMIT = 3; const SHOW_LIMIT = 3;
export const IdentityClientSecrets = ({ identityId, handlePopUpOpen }: Props) => { export const IdentityClientSecrets = ({ identityId, handlePopUpOpen }: Props) => {
const [copyTextClientId, isCopyingClientId, setCopyTextClientId] = useTimedReset<string>({
initialState: "Copy Client ID to clipboard"
});
const { data } = useGetIdentityById(identityId); const { data } = useGetIdentityById(identityId);
const { data: identityUniversalAuth } = useGetIdentityUniversalAuth(identityId); const { data: identityUniversalAuth } = useGetIdentityUniversalAuth(identityId);
const { data: clientSecrets } = useGetIdentityUniversalAuthClientSecrets(identityId); const { data: clientSecrets } = useGetIdentityUniversalAuthClientSecrets(identityId);
@@ -32,7 +37,22 @@ export const IdentityClientSecrets = ({ identityId, handlePopUpOpen }: Props) =>
<div> <div>
<div className="mb-4"> <div className="mb-4">
<p className="text-sm font-semibold text-mineshaft-300">Client ID</p> <p className="text-sm font-semibold text-mineshaft-300">Client ID</p>
<p className="text-sm text-mineshaft-300">{identityUniversalAuth?.clientId ?? ""}</p> <div className="flex align-top">
<p className="text-sm text-mineshaft-300">{identityUniversalAuth?.clientId ?? ""}</p>
<Tooltip content={copyTextClientId}>
<IconButton
ariaLabel="copy icon"
variant="plain"
className="group relative ml-2"
onClick={() => {
navigator.clipboard.writeText(identityUniversalAuth?.clientId ?? "");
setCopyTextClientId("Copied");
}}
>
<FontAwesomeIcon icon={isCopyingClientId ? faCheck : faCopy} />
</IconButton>
</Tooltip>
</div>
</div> </div>
{clientSecrets?.length ? ( {clientSecrets?.length ? (
<div className="flex justify-between"> <div className="flex justify-between">
@@ -86,17 +86,19 @@ export const IdentityTokens = ({ identityId, handlePopUpOpen }: Props) => {
> >
Edit Token Edit Token
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem {!token.isAccessTokenRevoked && (
onClick={async () => { <DropdownMenuItem
handlePopUpOpen("revokeToken", { onClick={async () => {
identityId, handlePopUpOpen("revokeToken", {
tokenId: token.id, identityId,
name: token.name tokenId: token.id,
}); name: token.name
}} });
> }}
Revoke Token >
</DropdownMenuItem> Revoke Token
</DropdownMenuItem>
)}
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> </div>
@@ -1,9 +1,10 @@
import { faPencil } from "@fortawesome/free-solid-svg-icons"; import { faCheck,faCopy, faPencil } 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 { IconButton,Tooltip } from "@app/components/v2"; import { IconButton, Tooltip } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { useTimedReset } from "@app/hooks";
import { useGetIdentityById } from "@app/hooks/api"; import { useGetIdentityById } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -16,6 +17,10 @@ type Props = {
}; };
export const IdentityDetailsSection = ({ identityId, handlePopUpOpen }: Props) => { export const IdentityDetailsSection = ({ identityId, handlePopUpOpen }: Props) => {
const [copyTextId, isCopyingId, setCopyTextId] = useTimedReset<string>({
initialState: "Copy ID to clipboard"
});
const { data } = useGetIdentityById(identityId); const { data } = useGetIdentityById(identityId);
return data ? ( return data ? (
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"> <div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
@@ -49,7 +54,22 @@ export const IdentityDetailsSection = ({ identityId, handlePopUpOpen }: Props) =
<div className="pt-4"> <div className="pt-4">
<div className="mb-4"> <div className="mb-4">
<p className="text-sm font-semibold text-mineshaft-300">ID</p> <p className="text-sm font-semibold text-mineshaft-300">ID</p>
<p className="text-sm text-mineshaft-300">{data.identity.id}</p> <div className="flex align-top">
<p className="text-sm text-mineshaft-300">{data.identity.id}</p>
<Tooltip content={copyTextId}>
<IconButton
ariaLabel="copy icon"
variant="plain"
className="group relative ml-2"
onClick={() => {
navigator.clipboard.writeText(data.identity.id);
setCopyTextId("Copied");
}}
>
<FontAwesomeIcon icon={isCopyingId ? faCheck : faCopy} />
</IconButton>
</Tooltip>
</div>
</div> </div>
<div className="mb-4"> <div className="mb-4">
<p className="text-sm font-semibold text-mineshaft-300">Name</p> <p className="text-sm font-semibold text-mineshaft-300">Name</p>
@@ -16,7 +16,7 @@ import {
Tooltip Tooltip
} from "@app/components/v2"; } from "@app/components/v2";
import { useTimedReset } from "@app/hooks"; import { useTimedReset } from "@app/hooks";
import { useCreateTokenIdentityTokenAuth, useUpdateTokenIdentityTokenAuth } from "@app/hooks/api"; import { useCreateTokenIdentityTokenAuth, useUpdateIdentityTokenAuthToken } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
const schema = z const schema = z
@@ -34,7 +34,7 @@ type Props = {
export const IdentityTokenModal = ({ popUp, handlePopUpToggle }: Props) => { export const IdentityTokenModal = ({ popUp, handlePopUpToggle }: Props) => {
const { mutateAsync: createToken } = useCreateTokenIdentityTokenAuth(); const { mutateAsync: createToken } = useCreateTokenIdentityTokenAuth();
const { mutateAsync: updateToken } = useUpdateTokenIdentityTokenAuth(); const { mutateAsync: updateToken } = useUpdateIdentityTokenAuthToken();
const [token, setToken] = useState(""); const [token, setToken] = useState("");
const [copyTextToken, isCopyingToken, setCopyTextToken] = useTimedReset<string>({ const [copyTextToken, isCopyingToken, setCopyTextToken] = useTimedReset<string>({
initialState: "Copy to clipboard" initialState: "Copy to clipboard"
@@ -3,15 +3,24 @@ import { Controller, useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup"; import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup"; import * as yup from "yup";
import { createNotification } from "@app/components/notifications";
import { import {
DeleteActionModal,
FormControl, FormControl,
Modal, Modal,
ModalContent, ModalContent,
Select, Select,
SelectItem, SelectItem,
UpgradePlanModal UpgradePlanModal} from "@app/components/v2";
} from "@app/components/v2"; import { useOrganization } from "@app/context";
import { IdentityAuthMethod } from "@app/hooks/api/identities"; import {
useDeleteIdentityAwsAuth,
useDeleteIdentityAzureAuth,
useDeleteIdentityGcpAuth,
useDeleteIdentityKubernetesAuth,
useDeleteIdentityTokenAuth,
useDeleteIdentityUniversalAuth} from "@app/hooks/api";
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 { IdentityAwsAuthForm } from "./IdentityAwsAuthForm";
@@ -22,10 +31,10 @@ import { IdentityTokenAuthForm } from "./IdentityTokenAuthForm";
import { IdentityUniversalAuthForm } from "./IdentityUniversalAuthForm"; import { IdentityUniversalAuthForm } from "./IdentityUniversalAuthForm";
type Props = { type Props = {
popUp: UsePopUpState<["identityAuthMethod", "upgradePlan"]>; popUp: UsePopUpState<["identityAuthMethod", "upgradePlan", "revokeAuthMethod"]>;
handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void; handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void;
handlePopUpToggle: ( handlePopUpToggle: (
popUpName: keyof UsePopUpState<["identityAuthMethod", "upgradePlan"]>, popUpName: keyof UsePopUpState<["identityAuthMethod", "upgradePlan", "revokeAuthMethod"]>,
state?: boolean state?: boolean
) => void; ) => void;
}; };
@@ -48,6 +57,16 @@ const schema = yup
export type FormData = yup.InferType<typeof schema>; 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 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 { control, watch, setValue } = useForm<FormData>({ const { control, watch, setValue } = useForm<FormData>({
resolver: yupResolver(schema), resolver: yupResolver(schema),
defaultValues: { defaultValues: {
@@ -128,13 +147,84 @@ export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpTog
/> />
); );
} }
default: { default: {
return <div />; return <div />;
} }
} }
}; };
const onRevokeAuthMethodSubmit = async () => {
if (!identityAuthMethodData.authMethod) return;
if (!orgId) return;
try {
console.log("onRevokeAuthMethodSubmit identityId: ", identityAuthMethodData);
switch (identityAuthMethodData.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;
}
default:
break;
}
createNotification({
text: `Successfully removed ${
identityAuthToNameMap[identityAuthMethodData.authMethod]
} on ${identityAuthMethodData.name}`,
type: "success"
});
handlePopUpToggle("revokeAuthMethod", false);
handlePopUpToggle("identityAuthMethod", false);
} catch (err) {
console.error(err);
createNotification({
text: `Failed to remove ${identityAuthToNameMap[identityAuthMethodData.authMethod]} on ${
identityAuthMethodData.name
}`,
type: "error"
});
}
};
return ( return (
<Modal <Modal
isOpen={popUp?.identityAuthMethod?.isOpen} isOpen={popUp?.identityAuthMethod?.isOpen}
@@ -175,6 +265,17 @@ export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpTog
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)} onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text="You can use IP allowlisting if you switch to Infisical's Pro plan." 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"
onDeleteApproved={onRevokeAuthMethodSubmit}
/>
</ModalContent> </ModalContent>
</Modal> </Modal>
); );
@@ -10,9 +10,9 @@ 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,
useDeleteIdentityAwsAuth,
useGetIdentityAwsAuth, useGetIdentityAwsAuth,
useUpdateIdentityAwsAuth} from "@app/hooks/api"; useUpdateIdentityAwsAuth
} 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 { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -42,7 +42,7 @@ export type FormData = yup.InferType<typeof schema>;
type Props = { type Props = {
handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void; handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void;
handlePopUpToggle: ( handlePopUpToggle: (
popUpName: keyof UsePopUpState<["identityAuthMethod"]>, popUpName: keyof UsePopUpState<["identityAuthMethod", "revokeAuthMethod"]>,
state?: boolean state?: boolean
) => void; ) => void;
identityAuthMethodData: { identityAuthMethodData: {
@@ -63,7 +63,6 @@ export const IdentityAwsAuthForm = ({
const { mutateAsync: addMutateAsync } = useAddIdentityAwsAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityAwsAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAwsAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityAwsAuth();
const { mutateAsync: deleteMutateAsync } = useDeleteIdentityAwsAuth();
const { data } = useGetIdentityAwsAuth(identityAuthMethodData?.identityId ?? ""); const { data } = useGetIdentityAwsAuth(identityAuthMethodData?.identityId ?? "");
@@ -346,7 +345,7 @@ export const IdentityAwsAuthForm = ({
variant="plain" variant="plain"
onClick={() => handlePopUpToggle("identityAuthMethod", false)} onClick={() => handlePopUpToggle("identityAuthMethod", false)}
> >
{identityAuthMethodData?.authMethod ? "Cancel" : "Skip"} Cancel
</Button> </Button>
</div> </div>
{identityAuthMethodData?.authMethod && ( {identityAuthMethodData?.authMethod && (
@@ -355,14 +354,7 @@ export const IdentityAwsAuthForm = ({
colorSchema="danger" colorSchema="danger"
isLoading={isSubmitting} isLoading={isSubmitting}
isDisabled={isSubmitting} isDisabled={isSubmitting}
onClick={async () => { onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
await deleteMutateAsync({
identityId: identityAuthMethodData.identityId,
organizationId: orgId
});
handlePopUpToggle("identityAuthMethod", false);
}}
> >
Remove Auth Method Remove Auth Method
</Button> </Button>
@@ -10,9 +10,9 @@ 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,
useDeleteIdentityAzureAuth,
useGetIdentityAzureAuth, useGetIdentityAzureAuth,
useUpdateIdentityAzureAuth} from "@app/hooks/api"; useUpdateIdentityAzureAuth
} 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 { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -40,7 +40,7 @@ export type FormData = z.infer<typeof schema>;
type Props = { type Props = {
handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void; handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void;
handlePopUpToggle: ( handlePopUpToggle: (
popUpName: keyof UsePopUpState<["identityAuthMethod"]>, popUpName: keyof UsePopUpState<["identityAuthMethod", "revokeAuthMethod"]>,
state?: boolean state?: boolean
) => void; ) => void;
identityAuthMethodData: { identityAuthMethodData: {
@@ -61,7 +61,6 @@ export const IdentityAzureAuthForm = ({
const { mutateAsync: addMutateAsync } = useAddIdentityAzureAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityAzureAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAzureAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityAzureAuth();
const { mutateAsync: deleteMutateAsync } = useDeleteIdentityAzureAuth();
const { data } = useGetIdentityAzureAuth(identityAuthMethodData?.identityId ?? ""); const { data } = useGetIdentityAzureAuth(identityAuthMethodData?.identityId ?? "");
@@ -344,7 +343,7 @@ export const IdentityAzureAuthForm = ({
variant="plain" variant="plain"
onClick={() => handlePopUpToggle("identityAuthMethod", false)} onClick={() => handlePopUpToggle("identityAuthMethod", false)}
> >
{identityAuthMethodData?.authMethod ? "Cancel" : "Skip"} Cancel
</Button> </Button>
</div> </div>
{identityAuthMethodData?.authMethod && ( {identityAuthMethodData?.authMethod && (
@@ -353,14 +352,7 @@ export const IdentityAzureAuthForm = ({
colorSchema="danger" colorSchema="danger"
isLoading={isSubmitting} isLoading={isSubmitting}
isDisabled={isSubmitting} isDisabled={isSubmitting}
onClick={async () => { onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
await deleteMutateAsync({
identityId: identityAuthMethodData.identityId,
organizationId: orgId
});
handlePopUpToggle("identityAuthMethod", false);
}}
> >
Remove Auth Method Remove Auth Method
</Button> </Button>
@@ -10,9 +10,9 @@ import { Button, FormControl, IconButton, Input, Select, SelectItem } from "@app
import { useOrganization, useSubscription } from "@app/context"; import { useOrganization, useSubscription } from "@app/context";
import { import {
useAddIdentityGcpAuth, useAddIdentityGcpAuth,
useDeleteIdentityGcpAuth,
useGetIdentityGcpAuth, useGetIdentityGcpAuth,
useUpdateIdentityGcpAuth} from "@app/hooks/api"; useUpdateIdentityGcpAuth
} 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 { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -41,7 +41,7 @@ export type FormData = z.infer<typeof schema>;
type Props = { type Props = {
handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void; handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void;
handlePopUpToggle: ( handlePopUpToggle: (
popUpName: keyof UsePopUpState<["identityAuthMethod"]>, popUpName: keyof UsePopUpState<["identityAuthMethod", "revokeAuthMethod"]>,
state?: boolean state?: boolean
) => void; ) => void;
identityAuthMethodData: { identityAuthMethodData: {
@@ -62,7 +62,6 @@ export const IdentityGcpAuthForm = ({
const { mutateAsync: addMutateAsync } = useAddIdentityGcpAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityGcpAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityGcpAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityGcpAuth();
const { mutateAsync: deleteMutateAsync } = useDeleteIdentityGcpAuth();
const { data } = useGetIdentityGcpAuth(identityAuthMethodData?.identityId ?? ""); const { data } = useGetIdentityGcpAuth(identityAuthMethodData?.identityId ?? "");
@@ -378,7 +377,7 @@ export const IdentityGcpAuthForm = ({
variant="plain" variant="plain"
onClick={() => handlePopUpToggle("identityAuthMethod", false)} onClick={() => handlePopUpToggle("identityAuthMethod", false)}
> >
{identityAuthMethodData?.authMethod ? "Cancel" : "Skip"} Cancel
</Button> </Button>
</div> </div>
{identityAuthMethodData?.authMethod && ( {identityAuthMethodData?.authMethod && (
@@ -387,14 +386,7 @@ export const IdentityGcpAuthForm = ({
colorSchema="danger" colorSchema="danger"
isLoading={isSubmitting} isLoading={isSubmitting}
isDisabled={isSubmitting} isDisabled={isSubmitting}
onClick={async () => { onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
await deleteMutateAsync({
identityId: identityAuthMethodData.identityId,
organizationId: orgId
});
handlePopUpToggle("identityAuthMethod", false);
}}
> >
Remove Auth Method Remove Auth Method
</Button> </Button>
@@ -10,9 +10,9 @@ import { Button, FormControl, IconButton, Input, TextArea } from "@app/component
import { useOrganization, useSubscription } from "@app/context"; import { useOrganization, useSubscription } from "@app/context";
import { import {
useAddIdentityKubernetesAuth, useAddIdentityKubernetesAuth,
useDeleteIdentityKubernetesAuth,
useGetIdentityKubernetesAuth, useGetIdentityKubernetesAuth,
useUpdateIdentityKubernetesAuth} from "@app/hooks/api"; useUpdateIdentityKubernetesAuth
} 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 { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -43,7 +43,7 @@ export type FormData = z.infer<typeof schema>;
type Props = { type Props = {
handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void; handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void;
handlePopUpToggle: ( handlePopUpToggle: (
popUpName: keyof UsePopUpState<["identityAuthMethod"]>, popUpName: keyof UsePopUpState<["identityAuthMethod", "revokeAuthMethod"]>,
state?: boolean state?: boolean
) => void; ) => void;
identityAuthMethodData: { identityAuthMethodData: {
@@ -64,7 +64,6 @@ export const IdentityKubernetesAuthForm = ({
const { mutateAsync: addMutateAsync } = useAddIdentityKubernetesAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityKubernetesAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityKubernetesAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityKubernetesAuth();
const { mutateAsync: deleteMutateAsync } = useDeleteIdentityKubernetesAuth();
const { data } = useGetIdentityKubernetesAuth(identityAuthMethodData?.identityId ?? ""); const { data } = useGetIdentityKubernetesAuth(identityAuthMethodData?.identityId ?? "");
@@ -399,7 +398,7 @@ export const IdentityKubernetesAuthForm = ({
variant="plain" variant="plain"
onClick={() => handlePopUpToggle("identityAuthMethod", false)} onClick={() => handlePopUpToggle("identityAuthMethod", false)}
> >
{identityAuthMethodData?.authMethod ? "Cancel" : "Skip"} Cancel
</Button> </Button>
</div> </div>
{identityAuthMethodData?.authMethod && ( {identityAuthMethodData?.authMethod && (
@@ -408,14 +407,7 @@ export const IdentityKubernetesAuthForm = ({
colorSchema="danger" colorSchema="danger"
isLoading={isSubmitting} isLoading={isSubmitting}
isDisabled={isSubmitting} isDisabled={isSubmitting}
onClick={async () => { onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
await deleteMutateAsync({
identityId: identityAuthMethodData.identityId,
organizationId: orgId
});
handlePopUpToggle("identityAuthMethod", false);
}}
> >
Remove Auth Method Remove Auth Method
</Button> </Button>
@@ -15,7 +15,7 @@ import { withPermission } from "@app/hoc";
import { useDeleteIdentity } from "@app/hooks/api"; import { useDeleteIdentity } from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp"; import { usePopUp } from "@app/hooks/usePopUp";
import { IdentityAuthMethodModal } from "./IdentityAuthMethodModal"; // import { IdentityAuthMethodModal } from "./IdentityAuthMethodModal";
import { IdentityModal } from "./IdentityModal"; import { IdentityModal } from "./IdentityModal";
import { IdentityTable } from "./IdentityTable"; import { IdentityTable } from "./IdentityTable";
import { IdentityTokenAuthTokenModal } from "./IdentityTokenAuthTokenModal"; import { IdentityTokenAuthTokenModal } from "./IdentityTokenAuthTokenModal";
@@ -110,11 +110,11 @@ export const IdentitySection = withPermission(
</div> </div>
<IdentityTable /> <IdentityTable />
<IdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} /> <IdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
<IdentityAuthMethodModal {/* <IdentityAuthMethodModal
popUp={popUp} popUp={popUp}
handlePopUpOpen={handlePopUpOpen} handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle} handlePopUpToggle={handlePopUpToggle}
/> /> */}
{/* <IdentityUniversalAuthClientSecretModal {/* <IdentityUniversalAuthClientSecretModal
popUp={popUp} popUp={popUp}
handlePopUpOpen={handlePopUpOpen} handlePopUpOpen={handlePopUpOpen}
@@ -69,11 +69,13 @@ export const IdentityTable = () => {
<TBody> <TBody>
{isLoading && <TableSkeleton columns={4} innerKey="org-identities" />} {isLoading && <TableSkeleton columns={4} innerKey="org-identities" />}
{!isLoading && {!isLoading &&
data && data?.map(({ identity: { id, name }, role, customRole }) => {
data.length > 0 &&
data.map(({ identity: { id, name }, role, customRole }) => {
return ( return (
<Tr className="h-10" key={`identity-${id}`}> <Tr
className="h-10 cursor-pointer transition-colors duration-300 hover:bg-mineshaft-700"
key={`identity-${id}`}
onClick={() => router.push(`/org/${orgId}/identities/${id}`)}
>
<Td> <Td>
<Link href={`/org/${orgId}/identities/${id}`}>{name}</Link> <Link href={`/org/${orgId}/identities/${id}`}>{name}</Link>
</Td> </Td>
@@ -9,9 +9,9 @@ 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,
useDeleteIdentityTokenAuth,
useGetIdentityTokenAuth, useGetIdentityTokenAuth,
useUpdateIdentityTokenAuth} from "@app/hooks/api"; useUpdateIdentityTokenAuth
} from "@app/hooks/api";
import { IdentityAuthMethod } from "@app/hooks/api/identities"; import { IdentityAuthMethod } from "@app/hooks/api/identities";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -35,7 +35,7 @@ export type FormData = z.infer<typeof schema>;
type Props = { type Props = {
handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void; handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void;
handlePopUpToggle: ( handlePopUpToggle: (
popUpName: keyof UsePopUpState<["identityAuthMethod"]>, popUpName: keyof UsePopUpState<["identityAuthMethod", "revokeAuthMethod"]>,
state?: boolean state?: boolean
) => void; ) => void;
identityAuthMethodData: { identityAuthMethodData: {
@@ -56,7 +56,6 @@ export const IdentityTokenAuthForm = ({
const { mutateAsync: addMutateAsync } = useAddIdentityTokenAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityTokenAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityTokenAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityTokenAuth();
const { mutateAsync: deleteMutateAsync } = useDeleteIdentityTokenAuth();
const { data } = useGetIdentityTokenAuth(identityAuthMethodData?.identityId ?? ""); const { data } = useGetIdentityTokenAuth(identityAuthMethodData?.identityId ?? "");
@@ -256,7 +255,7 @@ export const IdentityTokenAuthForm = ({
variant="plain" variant="plain"
onClick={() => handlePopUpToggle("identityAuthMethod", false)} onClick={() => handlePopUpToggle("identityAuthMethod", false)}
> >
{identityAuthMethodData?.authMethod ? "Cancel" : "Skip"} Cancel
</Button> </Button>
</div> </div>
{identityAuthMethodData?.authMethod && ( {identityAuthMethodData?.authMethod && (
@@ -265,14 +264,7 @@ export const IdentityTokenAuthForm = ({
colorSchema="danger" colorSchema="danger"
isLoading={isSubmitting} isLoading={isSubmitting}
isDisabled={isSubmitting} isDisabled={isSubmitting}
onClick={async () => { onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
await deleteMutateAsync({
identityId: identityAuthMethodData.identityId,
organizationId: orgId
});
handlePopUpToggle("identityAuthMethod", false);
}}
> >
Remove Auth Method Remove Auth Method
</Button> </Button>
@@ -10,9 +10,9 @@ 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,
useDeleteIdentityUniversalAuth,
useGetIdentityUniversalAuth, useGetIdentityUniversalAuth,
useUpdateIdentityUniversalAuth} from "@app/hooks/api"; useUpdateIdentityUniversalAuth
} 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 { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -48,7 +48,7 @@ export type FormData = yup.InferType<typeof schema>;
type Props = { type Props = {
handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void; handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void;
handlePopUpToggle: ( handlePopUpToggle: (
popUpName: keyof UsePopUpState<["identityAuthMethod"]>, popUpName: keyof UsePopUpState<["identityAuthMethod", "revokeAuthMethod"]>,
state?: boolean state?: boolean
) => void; ) => void;
identityAuthMethodData: { identityAuthMethodData: {
@@ -68,7 +68,6 @@ export const IdentityUniversalAuthForm = ({
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { mutateAsync: addMutateAsync } = useAddIdentityUniversalAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityUniversalAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityUniversalAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityUniversalAuth();
const { mutateAsync: deleteMutateAsync } = useDeleteIdentityUniversalAuth();
const { data } = useGetIdentityUniversalAuth(identityAuthMethodData?.identityId ?? ""); const { data } = useGetIdentityUniversalAuth(identityAuthMethodData?.identityId ?? "");
const { const {
@@ -385,7 +384,7 @@ export const IdentityUniversalAuthForm = ({
variant="plain" variant="plain"
onClick={() => handlePopUpToggle("identityAuthMethod", false)} onClick={() => handlePopUpToggle("identityAuthMethod", false)}
> >
{identityAuthMethodData?.authMethod ? "Cancel" : "Skip"} Cancel
</Button> </Button>
</div> </div>
{identityAuthMethodData?.authMethod && ( {identityAuthMethodData?.authMethod && (
@@ -394,14 +393,7 @@ export const IdentityUniversalAuthForm = ({
colorSchema="danger" colorSchema="danger"
isLoading={isSubmitting} isLoading={isSubmitting}
isDisabled={isSubmitting} isDisabled={isSubmitting}
onClick={async () => { onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
await deleteMutateAsync({
identityId: identityAuthMethodData.identityId,
organizationId: orgId
});
handlePopUpToggle("identityAuthMethod", false);
}}
> >
Remove Auth Method Remove Auth Method
</Button> </Button>