diff --git a/backend/src/server/routes/v1/identity-token-auth-router.ts b/backend/src/server/routes/v1/identity-token-auth-router.ts index d7cd86330..71f299e43 100644 --- a/backend/src/server/routes/v1/identity-token-auth-router.ts +++ b/backend/src/server/routes/v1/identity-token-auth-router.ts @@ -408,6 +408,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider } }); + // deprecated - use the GET /token-auth/tokens/:tokenId instead, this endpoint will be removed in the future server.route({ method: "GET", url: "/token-auth/identities/:identityId/tokens/:tokenId", @@ -416,7 +417,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider }, onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { - hide: false, + hide: true, tags: [ApiDocsTags.TokenAuth], description: "Get token for machine identity with Token Auth", security: [ @@ -436,13 +437,11 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider }, handler: async (req) => { const { token, identityMembershipOrg } = await server.services.identityTokenAuth.getTokenAuthTokenById({ - identityId: req.params.identityId, tokenId: req.params.tokenId, actor: req.permission.type, actorId: req.permission.id, actorOrgId: req.permission.orgId, - actorAuthMethod: req.permission.authMethod, - isActorSuperAdmin: isSuperAdmin(req.auth) + actorAuthMethod: req.permission.authMethod }); await server.services.auditLog.createAuditLog({ @@ -462,6 +461,57 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider } }); + server.route({ + method: "GET", + url: "/token-auth/tokens/:tokenId", + config: { + rateLimit: readLimit + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + schema: { + hide: false, + tags: [ApiDocsTags.TokenAuth], + description: "Get token for machine identity with Token Auth", + security: [ + { + bearerAuth: [] + } + ], + params: z.object({ + tokenId: z.string().describe(TOKEN_AUTH.GET_TOKEN.tokenId) + }), + response: { + 200: z.object({ + token: IdentityAccessTokensSchema + }) + } + }, + handler: async (req) => { + const { token, identityMembershipOrg } = await server.services.identityTokenAuth.getTokenAuthTokenById({ + tokenId: req.params.tokenId, + actor: req.permission.type, + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod + }); + + await server.services.auditLog.createAuditLog({ + ...req.auditLogInfo, + orgId: identityMembershipOrg.scopeOrgId, + event: { + type: EventType.GET_TOKEN_IDENTITY_TOKEN_AUTH, + metadata: { + identityId: identityMembershipOrg.identity.id, + identityName: identityMembershipOrg.identity.name, + tokenId: token.id + } + } + }); + + return { token }; + } + }); + server.route({ method: "PATCH", url: "/token-auth/tokens/:tokenId", diff --git a/backend/src/services/identity-token-auth/identity-token-auth-service.ts b/backend/src/services/identity-token-auth/identity-token-auth-service.ts index bdc8ab1c1..1a20b1192 100644 --- a/backend/src/services/identity-token-auth/identity-token-auth-service.ts +++ b/backend/src/services/identity-token-auth/identity-token-auth-service.ts @@ -621,48 +621,61 @@ export const identityTokenAuthServiceFactory = ({ const getTokenAuthTokenById = async ({ tokenId, - identityId, - isActorSuperAdmin, actorId, actor, actorAuthMethod, actorOrgId }: TGetTokenAuthTokenByIdDTO) => { - await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); + const foundToken = await identityAccessTokenDAL.findOne({ + [`${TableName.IdentityAccessToken}.id` as "id"]: tokenId, + [`${TableName.IdentityAccessToken}.authMethod` as "authMethod"]: IdentityAuthMethod.TOKEN_AUTH + }); + if (!foundToken) throw new NotFoundError({ message: `Token with ID ${tokenId} not found` }); const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ scopeData: { scope: AccessScope.Organization, orgId: actorOrgId }, - identityId + identityId: foundToken.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 ${foundToken.identityId}` }); + } if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) { throw new BadRequestError({ message: "The identity does not have Token Auth" }); } - const { permission } = await permissionService.getOrgPermission({ - scope: OrganizationActionScope.Any, - actor, - actorId, - orgId: identityMembershipOrg.scopeOrgId, - actorAuthMethod, - actorOrgId - }); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); - const token = await identityAccessTokenDAL.findOne({ - [`${TableName.IdentityAccessToken}.id` as "id"]: tokenId, - [`${TableName.IdentityAccessToken}.authMethod` as "authMethod"]: IdentityAuthMethod.TOKEN_AUTH, - [`${TableName.IdentityAccessToken}.identityId` as "identityId"]: identityId - }); + if (identityMembershipOrg.identity.projectId) { + const { permission } = await permissionService.getProjectPermission({ + actionProjectType: ActionProjectType.Any, + actor, + actorId, + projectId: identityMembershipOrg.identity.projectId, + actorAuthMethod, + actorOrgId + }); - if (!token) throw new NotFoundError({ message: `Token with ID ${tokenId} not found` }); + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionIdentityActions.Read, + subject(ProjectPermissionSub.Identity, { identityId: identityMembershipOrg.identity.id }) + ); + } else { + const { permission } = await permissionService.getOrgPermission({ + scope: OrganizationActionScope.Any, + actor, + actorId, + orgId: identityMembershipOrg.scopeOrgId, + actorAuthMethod, + actorOrgId + }); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); + } - return { token, identityMembershipOrg }; + return { token: foundToken, identityMembershipOrg }; }; const updateTokenAuthToken = async ({ diff --git a/backend/src/services/identity-token-auth/identity-token-auth-types.ts b/backend/src/services/identity-token-auth/identity-token-auth-types.ts index fdecc6d4c..6be2c5fe0 100644 --- a/backend/src/services/identity-token-auth/identity-token-auth-types.ts +++ b/backend/src/services/identity-token-auth/identity-token-auth-types.ts @@ -42,8 +42,6 @@ export type TGetTokenAuthTokensDTO = { export type TGetTokenAuthTokenByIdDTO = { tokenId: string; - identityId: string; - isActorSuperAdmin?: boolean; } & Omit; export type TUpdateTokenAuthTokenDTO = { diff --git a/docs/api-reference/endpoints/token-auth/get-token.mdx b/docs/api-reference/endpoints/token-auth/get-token.mdx new file mode 100644 index 000000000..69bc14cdc --- /dev/null +++ b/docs/api-reference/endpoints/token-auth/get-token.mdx @@ -0,0 +1,4 @@ +--- +title: "Get Token" +openapi: "GET /api/v1/auth/token-auth/tokens/{tokenId}" +--- diff --git a/frontend/src/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/IdentityTokenAuthTokensTable.tsx b/frontend/src/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/IdentityTokenAuthTokensTable.tsx index d0d0b0fcd..5d7bf30e9 100644 --- a/frontend/src/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/IdentityTokenAuthTokensTable.tsx +++ b/frontend/src/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/IdentityTokenAuthTokensTable.tsx @@ -22,6 +22,7 @@ import { Tooltip, Tr } from "@app/components/v2"; +import { CopyButton } from "@app/components/v2/CopyButton"; import { OrgPermissionIdentityActions, OrgPermissionSubjects, @@ -153,6 +154,7 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
+