feat(machine-identity): Alibaba Cloud

This commit is contained in:
x032205
2025-06-11 02:44:53 -04:00
parent 0d7cd357c3
commit c315eed4d4
43 changed files with 1879 additions and 7 deletions
+12
View File
@@ -172,6 +172,8 @@ import { identityOrgDALFactory } from "@app/services/identity/identity-org-dal";
import { identityServiceFactory } from "@app/services/identity/identity-service";
import { identityAccessTokenDALFactory } from "@app/services/identity-access-token/identity-access-token-dal";
import { identityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
import { identityAliCloudAuthDALFactory } from "@app/services/identity-alicloud-auth/identity-alicloud-auth-dal";
import { identityAliCloudAuthServiceFactory } from "@app/services/identity-alicloud-auth/identity-alicloud-auth-service";
import { identityAwsAuthDALFactory } from "@app/services/identity-aws-auth/identity-aws-auth-dal";
import { identityAwsAuthServiceFactory } from "@app/services/identity-aws-auth/identity-aws-auth-service";
import { identityAzureAuthDALFactory } from "@app/services/identity-azure-auth/identity-azure-auth-dal";
@@ -383,6 +385,7 @@ export const registerRoutes = async (
const identityUaDAL = identityUaDALFactory(db);
const identityKubernetesAuthDAL = identityKubernetesAuthDALFactory(db);
const identityUaClientSecretDAL = identityUaClientSecretDALFactory(db);
const identityAliCloudAuthDAL = identityAliCloudAuthDALFactory(db);
const identityAwsAuthDAL = identityAwsAuthDALFactory(db);
const identityGcpAuthDAL = identityGcpAuthDALFactory(db);
const identityOciAuthDAL = identityOciAuthDALFactory(db);
@@ -1482,6 +1485,14 @@ export const registerRoutes = async (
licenseService
});
const identityAliCloudAuthService = identityAliCloudAuthServiceFactory({
identityAccessTokenDAL,
identityAliCloudAuthDAL,
identityOrgMembershipDAL,
licenseService,
permissionService
});
const identityAwsAuthService = identityAwsAuthServiceFactory({
identityAccessTokenDAL,
identityAwsAuthDAL,
@@ -1931,6 +1942,7 @@ export const registerRoutes = async (
identityUa: identityUaService,
identityKubernetesAuth: identityKubernetesAuthService,
identityGcpAuth: identityGcpAuthService,
identityAliCloudAuth: identityAliCloudAuthService,
identityAwsAuth: identityAwsAuthService,
identityAzureAuth: identityAzureAuthService,
identityOciAuth: identityOciAuthService,
@@ -0,0 +1,378 @@
import RE2 from "re2";
import { z } from "zod";
import { IdentityAlicloudAuthsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ALICLOUD_AUTH, ApiDocsTags } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
import { validateArns } from "@app/services/identity-alicloud-auth/identity-alicloud-auth-validators";
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
export const registerIdentityAliCloudAuthRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/alicloud-auth/login",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.AliCloudAuth],
description: "Login with Alibaba Cloud Auth",
body: z.object({
identityId: z.string().trim().describe(ALICLOUD_AUTH.LOGIN.identityId),
Action: z.enum(["GetCallerIdentity"]).describe(ALICLOUD_AUTH.LOGIN.Action),
Format: z.enum(["JSON"]).describe(ALICLOUD_AUTH.LOGIN.Format),
Version: z
.string()
.refine((val) => new RE2("^\\d{4}-\\d{2}-\\d{2}$").test(val), {
message: "Version must be in YYYY-MM-DD format"
})
.describe(ALICLOUD_AUTH.LOGIN.Version),
AccessKeyId: z
.string()
.refine((val) => new RE2("^[A-Za-z0-9]+$").test(val), {
message: "AccessKeyId must be alphanumeric"
})
.describe(ALICLOUD_AUTH.LOGIN.AccessKeyId),
SignatureMethod: z.enum(["HMAC-SHA1"]).describe(ALICLOUD_AUTH.LOGIN.SignatureMethod),
Timestamp: z
.string()
.refine((val) => new RE2("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$").test(val), {
message: "Timestamp must be in YYYY-MM-DDTHH:mm:ssZ format"
})
.describe(ALICLOUD_AUTH.LOGIN.Timestamp),
SignatureVersion: z.enum(["1.0"]).describe(ALICLOUD_AUTH.LOGIN.SignatureVersion),
SignatureNonce: z
.string()
.refine((val) => new RE2("^[a-zA-Z0-9-_.]+$").test(val), {
message:
"SignatureNonce must be at least 1 character long and contain only URL-safe characters (alphanumeric, -, _, .)"
})
.describe(ALICLOUD_AUTH.LOGIN.SignatureNonce),
Signature: z
.string()
.refine((val) => new RE2("^[A-Za-z0-9+/=]+$").test(val), {
message: "Signature must be base64 characters"
})
.describe(ALICLOUD_AUTH.LOGIN.Signature)
}),
response: {
200: z.object({
accessToken: z.string(),
expiresIn: z.coerce.number(),
accessTokenMaxTTL: z.coerce.number(),
tokenType: z.literal("Bearer")
})
}
},
handler: async (req) => {
const { identityAliCloudAuth, accessToken, identityAccessToken, identityMembershipOrg } =
await server.services.identityAliCloudAuth.login(req.body);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: identityMembershipOrg?.orgId,
event: {
type: EventType.LOGIN_IDENTITY_ALICLOUD_AUTH,
metadata: {
identityId: identityAliCloudAuth.identityId,
identityAccessTokenId: identityAccessToken.id,
identityAliCloudAuthId: identityAliCloudAuth.id
}
}
});
return {
accessToken,
tokenType: "Bearer" as const,
expiresIn: identityAliCloudAuth.accessTokenTTL,
accessTokenMaxTTL: identityAliCloudAuth.accessTokenMaxTTL
};
}
});
server.route({
method: "POST",
url: "/alicloud-auth/identities/:identityId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.AliCloudAuth],
description: "Attach Alibaba Cloud Auth configuration onto identity",
security: [
{
bearerAuth: []
}
],
params: z.object({
identityId: z.string().trim().describe(ALICLOUD_AUTH.ATTACH.identityId)
}),
body: z
.object({
allowedArns: validateArns.describe(ALICLOUD_AUTH.ATTACH.allowedArns),
accessTokenTrustedIps: z
.object({
ipAddress: z.string().trim()
})
.array()
.min(1)
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }])
.describe(ALICLOUD_AUTH.ATTACH.accessTokenTrustedIps),
accessTokenTTL: z
.number()
.int()
.min(0)
.max(315360000)
.default(2592000)
.describe(ALICLOUD_AUTH.ATTACH.accessTokenTTL),
accessTokenMaxTTL: z
.number()
.int()
.min(1)
.max(315360000)
.default(2592000)
.describe(ALICLOUD_AUTH.ATTACH.accessTokenMaxTTL),
accessTokenNumUsesLimit: z
.number()
.int()
.min(0)
.default(0)
.describe(ALICLOUD_AUTH.ATTACH.accessTokenNumUsesLimit)
})
.refine(
(val) => val.accessTokenTTL <= val.accessTokenMaxTTL,
"Access Token TTL cannot be greater than Access Token Max TTL."
),
response: {
200: z.object({
identityAliCloudAuth: IdentityAlicloudAuthsSchema
})
}
},
handler: async (req) => {
const identityAliCloudAuth = await server.services.identityAliCloudAuth.attachAliCloudAuth({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body,
identityId: req.params.identityId,
isActorSuperAdmin: isSuperAdmin(req.auth)
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: identityAliCloudAuth.orgId,
event: {
type: EventType.ADD_IDENTITY_ALICLOUD_AUTH,
metadata: {
identityId: identityAliCloudAuth.identityId,
allowedArns: identityAliCloudAuth.allowedArns,
accessTokenTTL: identityAliCloudAuth.accessTokenTTL,
accessTokenMaxTTL: identityAliCloudAuth.accessTokenMaxTTL,
accessTokenTrustedIps: identityAliCloudAuth.accessTokenTrustedIps as TIdentityTrustedIp[],
accessTokenNumUsesLimit: identityAliCloudAuth.accessTokenNumUsesLimit
}
}
});
return { identityAliCloudAuth };
}
});
server.route({
method: "PATCH",
url: "/alicloud-auth/identities/:identityId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.AliCloudAuth],
description: "Update Alibaba Cloud Auth configuration on identity",
security: [
{
bearerAuth: []
}
],
params: z.object({
identityId: z.string().describe(ALICLOUD_AUTH.UPDATE.identityId)
}),
body: z
.object({
allowedArns: validateArns.describe(ALICLOUD_AUTH.UPDATE.allowedArns),
accessTokenTrustedIps: z
.object({
ipAddress: z.string().trim()
})
.array()
.min(1)
.optional()
.describe(ALICLOUD_AUTH.UPDATE.accessTokenTrustedIps),
accessTokenTTL: z
.number()
.int()
.min(0)
.max(315360000)
.optional()
.describe(ALICLOUD_AUTH.UPDATE.accessTokenTTL),
accessTokenNumUsesLimit: z
.number()
.int()
.min(0)
.optional()
.describe(ALICLOUD_AUTH.UPDATE.accessTokenNumUsesLimit),
accessTokenMaxTTL: z
.number()
.int()
.max(315360000)
.min(0)
.optional()
.describe(ALICLOUD_AUTH.UPDATE.accessTokenMaxTTL)
})
.refine(
(val) => (val.accessTokenMaxTTL && val.accessTokenTTL ? val.accessTokenTTL <= val.accessTokenMaxTTL : true),
"Access Token TTL cannot be greater than Access Token Max TTL."
),
response: {
200: z.object({
identityAliCloudAuth: IdentityAlicloudAuthsSchema
})
}
},
handler: async (req) => {
const identityAliCloudAuth = await server.services.identityAliCloudAuth.updateAliCloudAuth({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body,
identityId: req.params.identityId,
allowedArns: req.body.allowedArns
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: identityAliCloudAuth.orgId,
event: {
type: EventType.UPDATE_IDENTITY_ALICLOUD_AUTH,
metadata: {
identityId: identityAliCloudAuth.identityId,
allowedArns: identityAliCloudAuth.allowedArns,
accessTokenTTL: identityAliCloudAuth.accessTokenTTL,
accessTokenMaxTTL: identityAliCloudAuth.accessTokenMaxTTL,
accessTokenTrustedIps: identityAliCloudAuth.accessTokenTrustedIps as TIdentityTrustedIp[],
accessTokenNumUsesLimit: identityAliCloudAuth.accessTokenNumUsesLimit
}
}
});
return { identityAliCloudAuth };
}
});
server.route({
method: "GET",
url: "/alicloud-auth/identities/:identityId",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.AliCloudAuth],
description: "Retrieve Alibaba Cloud Auth configuration on identity",
security: [
{
bearerAuth: []
}
],
params: z.object({
identityId: z.string().describe(ALICLOUD_AUTH.RETRIEVE.identityId)
}),
response: {
200: z.object({
identityAliCloudAuth: IdentityAlicloudAuthsSchema
})
}
},
handler: async (req) => {
const identityAliCloudAuth = await server.services.identityAliCloudAuth.getAliCloudAuth({
identityId: req.params.identityId,
actor: req.permission.type,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: identityAliCloudAuth.orgId,
event: {
type: EventType.GET_IDENTITY_ALICLOUD_AUTH,
metadata: {
identityId: identityAliCloudAuth.identityId
}
}
});
return { identityAliCloudAuth };
}
});
server.route({
method: "DELETE",
url: "/alicloud-auth/identities/:identityId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.AliCloudAuth],
description: "Delete Alibaba Cloud Auth configuration on identity",
security: [
{
bearerAuth: []
}
],
params: z.object({
identityId: z.string().describe(ALICLOUD_AUTH.REVOKE.identityId)
}),
response: {
200: z.object({
identityAliCloudAuth: IdentityAlicloudAuthsSchema
})
}
},
handler: async (req) => {
const identityAliCloudAuth = await server.services.identityAliCloudAuth.revokeIdentityAliCloudAuth({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: identityAliCloudAuth.orgId,
event: {
type: EventType.REVOKE_IDENTITY_ALICLOUD_AUTH,
metadata: {
identityId: identityAliCloudAuth.identityId
}
}
});
return { identityAliCloudAuth };
}
});
};
+2
View File
@@ -15,6 +15,7 @@ import { registerCertRouter } from "./certificate-router";
import { registerCertificateTemplateRouter } from "./certificate-template-router";
import { registerExternalGroupOrgRoleMappingRouter } from "./external-group-org-role-mapping-router";
import { registerIdentityAccessTokenRouter } from "./identity-access-token-router";
import { registerIdentityAliCloudAuthRouter } from "./identity-alicloud-auth-router";
import { registerIdentityAwsAuthRouter } from "./identity-aws-iam-auth-router";
import { registerIdentityAzureAuthRouter } from "./identity-azure-auth-router";
import { registerIdentityGcpAuthRouter } from "./identity-gcp-auth-router";
@@ -63,6 +64,7 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
await authRouter.register(registerIdentityKubernetesRouter);
await authRouter.register(registerIdentityGcpAuthRouter);
await authRouter.register(registerIdentityAccessTokenRouter);
await authRouter.register(registerIdentityAliCloudAuthRouter);
await authRouter.register(registerIdentityAwsAuthRouter);
await authRouter.register(registerIdentityAzureAuthRouter);
await authRouter.register(registerIdentityOciAuthRouter);