mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-16 02:16:34 +00:00
Merge remote-tracking branch 'origin' into cert-mgmt
This commit is contained in:
@@ -52,9 +52,25 @@ export const inviteUserRateLimit: RateLimitOptions = {
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
||||
export const mfaRateLimit: RateLimitOptions = {
|
||||
timeWindow: 60 * 1000,
|
||||
max: 20,
|
||||
keyGenerator: (req) => {
|
||||
return req.headers.authorization?.split(" ")[1] || req.realIp;
|
||||
}
|
||||
};
|
||||
|
||||
export const creationLimit: RateLimitOptions = {
|
||||
// identity, project, org
|
||||
timeWindow: 60 * 1000,
|
||||
max: 30,
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
||||
// Public endpoints to avoid brute force attacks
|
||||
export const publicEndpointLimit: RateLimitOptions = {
|
||||
// Shared Secrets
|
||||
timeWindow: 60 * 1000,
|
||||
max: 30,
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import fp from "fastify-plugin";
|
||||
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
|
||||
// inject permission type needed based on auth extracted
|
||||
@@ -15,6 +16,10 @@ export const injectPermission = fp(async (server) => {
|
||||
orgId: req.auth.orgId, // if the req.auth.authMode is AuthMode.API_KEY, the orgId will be "API_KEY"
|
||||
authMethod: req.auth.authMethod // if the req.auth.authMode is AuthMode.API_KEY, the authMethod will be null
|
||||
};
|
||||
|
||||
logger.info(
|
||||
`injectPermission: Injecting permissions for [permissionsForIdentity=${req.auth.userId}] [type=${ActorType.USER}]`
|
||||
);
|
||||
} else if (req.auth.actor === ActorType.IDENTITY) {
|
||||
req.permission = {
|
||||
type: ActorType.IDENTITY,
|
||||
@@ -22,6 +27,10 @@ export const injectPermission = fp(async (server) => {
|
||||
orgId: req.auth.orgId,
|
||||
authMethod: null
|
||||
};
|
||||
|
||||
logger.info(
|
||||
`injectPermission: Injecting permissions for [permissionsForIdentity=${req.auth.identityId}] [type=${ActorType.IDENTITY}]`
|
||||
);
|
||||
} else if (req.auth.actor === ActorType.SERVICE) {
|
||||
req.permission = {
|
||||
type: ActorType.SERVICE,
|
||||
@@ -29,6 +38,10 @@ export const injectPermission = fp(async (server) => {
|
||||
orgId: req.auth.orgId,
|
||||
authMethod: null
|
||||
};
|
||||
|
||||
logger.info(
|
||||
`injectPermission: Injecting permissions for [permissionsForIdentity=${req.auth.serviceTokenId}] [type=${ActorType.SERVICE}]`
|
||||
);
|
||||
} else if (req.auth.actor === ActorType.SCIM_CLIENT) {
|
||||
req.permission = {
|
||||
type: ActorType.SCIM_CLIENT,
|
||||
@@ -36,6 +49,10 @@ export const injectPermission = fp(async (server) => {
|
||||
orgId: req.auth.orgId,
|
||||
authMethod: null
|
||||
};
|
||||
|
||||
logger.info(
|
||||
`injectPermission: Injecting permissions for [permissionsForIdentity=${req.auth.scimTokenId}] [type=${ActorType.SCIM_CLIENT}]`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ const headersOrder = [
|
||||
"cf-connecting-ip", // Cloudflare
|
||||
"Cf-Pseudo-IPv4", // Cloudflare
|
||||
"x-client-ip", // Most common
|
||||
"x-envoy-external-address", // for envoy
|
||||
"x-forwarded-for", // Mostly used by proxies
|
||||
"fastly-client-ip",
|
||||
"true-client-ip", // Akamai and Cloudflare
|
||||
@@ -23,7 +24,21 @@ export const fastifyIp = fp(async (fastify) => {
|
||||
const forwardedIpHeader = headersOrder.find((header) => Boolean(req.headers[header]));
|
||||
const forwardedIp = forwardedIpHeader ? req.headers[forwardedIpHeader] : undefined;
|
||||
if (forwardedIp) {
|
||||
req.realIp = Array.isArray(forwardedIp) ? forwardedIp[0] : forwardedIp;
|
||||
if (Array.isArray(forwardedIp)) {
|
||||
// eslint-disable-next-line
|
||||
req.realIp = forwardedIp[0];
|
||||
return;
|
||||
}
|
||||
|
||||
if (forwardedIp.includes(",")) {
|
||||
// the ip header when placed with load balancers that proxy request
|
||||
// will attach the internal ips to header by appending with comma
|
||||
// https://github.com/go-chi/chi/blob/master/middleware/realip.go
|
||||
const clientIPFromProxy = forwardedIp.slice(0, forwardedIp.indexOf(",")).trim();
|
||||
req.realIp = clientIPFromProxy;
|
||||
return;
|
||||
}
|
||||
req.realIp = forwardedIp;
|
||||
} else {
|
||||
req.realIp = req.ip;
|
||||
}
|
||||
|
||||
@@ -87,6 +87,8 @@ import { identityAccessTokenDALFactory } from "@app/services/identity-access-tok
|
||||
import { identityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-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";
|
||||
import { identityAzureAuthServiceFactory } from "@app/services/identity-azure-auth/identity-azure-auth-service";
|
||||
import { identityGcpAuthDALFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-dal";
|
||||
import { identityGcpAuthServiceFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-service";
|
||||
import { identityKubernetesAuthDALFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-dal";
|
||||
@@ -122,6 +124,7 @@ import { projectMembershipServiceFactory } from "@app/services/project-membershi
|
||||
import { projectUserMembershipRoleDALFactory } from "@app/services/project-membership/project-user-membership-role-dal";
|
||||
import { projectRoleDALFactory } from "@app/services/project-role/project-role-dal";
|
||||
import { projectRoleServiceFactory } from "@app/services/project-role/project-role-service";
|
||||
import { dailyResourceCleanUpQueueServiceFactory } from "@app/services/resource-cleanup/resource-cleanup-queue";
|
||||
import { secretDALFactory } from "@app/services/secret/secret-dal";
|
||||
import { secretQueueFactory } from "@app/services/secret/secret-queue";
|
||||
import { secretServiceFactory } from "@app/services/secret/secret-service";
|
||||
@@ -134,6 +137,8 @@ import { secretFolderServiceFactory } from "@app/services/secret-folder/secret-f
|
||||
import { secretFolderVersionDALFactory } from "@app/services/secret-folder/secret-folder-version-dal";
|
||||
import { secretImportDALFactory } from "@app/services/secret-import/secret-import-dal";
|
||||
import { secretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
|
||||
import { secretSharingDALFactory } from "@app/services/secret-sharing/secret-sharing-dal";
|
||||
import { secretSharingServiceFactory } from "@app/services/secret-sharing/secret-sharing-service";
|
||||
import { secretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
|
||||
import { secretTagServiceFactory } from "@app/services/secret-tag/secret-tag-service";
|
||||
import { serviceTokenDALFactory } from "@app/services/service-token/service-token-dal";
|
||||
@@ -219,8 +224,8 @@ export const registerRoutes = async (
|
||||
const identityKubernetesAuthDAL = identityKubernetesAuthDALFactory(db);
|
||||
const identityUaClientSecretDAL = identityUaClientSecretDALFactory(db);
|
||||
const identityAwsAuthDAL = identityAwsAuthDALFactory(db);
|
||||
|
||||
const identityGcpAuthDAL = identityGcpAuthDALFactory(db);
|
||||
const identityAzureAuthDAL = identityAzureAuthDALFactory(db);
|
||||
|
||||
const auditLogDAL = auditLogDALFactory(db);
|
||||
const auditLogStreamDAL = auditLogStreamDALFactory(db);
|
||||
@@ -257,6 +262,7 @@ export const registerRoutes = async (
|
||||
const groupProjectMembershipRoleDAL = groupProjectMembershipRoleDALFactory(db);
|
||||
const userGroupMembershipDAL = userGroupMembershipDALFactory(db);
|
||||
const secretScanningDAL = secretScanningDALFactory(db);
|
||||
const secretSharingDAL = secretSharingDALFactory(db);
|
||||
const licenseDAL = licenseDALFactory(db);
|
||||
const dynamicSecretDAL = dynamicSecretDALFactory(db);
|
||||
const dynamicSecretLeaseDAL = dynamicSecretLeaseDALFactory(db);
|
||||
@@ -553,7 +559,8 @@ export const registerRoutes = async (
|
||||
permissionService,
|
||||
projectRoleDAL,
|
||||
projectUserMembershipRoleDAL,
|
||||
identityProjectMembershipRoleDAL
|
||||
identityProjectMembershipRoleDAL,
|
||||
projectDAL
|
||||
});
|
||||
|
||||
const snapshotService = secretSnapshotServiceFactory({
|
||||
@@ -641,6 +648,12 @@ export const registerRoutes = async (
|
||||
projectEnvDAL,
|
||||
projectBotService
|
||||
});
|
||||
|
||||
const secretSharingService = secretSharingServiceFactory({
|
||||
permissionService,
|
||||
secretSharingDAL
|
||||
});
|
||||
|
||||
const sarService = secretApprovalRequestServiceFactory({
|
||||
permissionService,
|
||||
projectBotService,
|
||||
@@ -775,6 +788,15 @@ export const registerRoutes = async (
|
||||
permissionService
|
||||
});
|
||||
|
||||
const identityAzureAuthService = identityAzureAuthServiceFactory({
|
||||
identityAzureAuthDAL,
|
||||
identityOrgMembershipDAL,
|
||||
identityAccessTokenDAL,
|
||||
identityDAL,
|
||||
permissionService,
|
||||
licenseService
|
||||
});
|
||||
|
||||
const dynamicSecretProviders = buildDynamicSecretProviders();
|
||||
const dynamicSecretQueueService = dynamicSecretLeaseQueueServiceFactory({
|
||||
queueService,
|
||||
@@ -802,14 +824,20 @@ export const registerRoutes = async (
|
||||
folderDAL,
|
||||
licenseService
|
||||
});
|
||||
const dailyResourceCleanUp = dailyResourceCleanUpQueueServiceFactory({
|
||||
auditLogDAL,
|
||||
queueService,
|
||||
identityAccessTokenDAL,
|
||||
secretSharingDAL
|
||||
});
|
||||
|
||||
await superAdminService.initServerCfg();
|
||||
//
|
||||
// setup the communication with license key server
|
||||
await licenseService.init();
|
||||
|
||||
await auditLogQueue.startAuditLogPruneJob();
|
||||
await telemetryQueue.startTelemetryCheck();
|
||||
await dailyResourceCleanUp.startCleanUp();
|
||||
|
||||
// inject all services
|
||||
server.decorate<FastifyZodProvider["services"]>("services", {
|
||||
@@ -846,6 +874,7 @@ export const registerRoutes = async (
|
||||
identityKubernetesAuth: identityKubernetesAuthService,
|
||||
identityGcpAuth: identityGcpAuthService,
|
||||
identityAwsAuth: identityAwsAuthService,
|
||||
identityAzureAuth: identityAzureAuthService,
|
||||
secretApprovalPolicy: sapService,
|
||||
accessApprovalPolicy: accessApprovalPolicyService,
|
||||
accessApprovalRequest: accessApprovalRequestService,
|
||||
@@ -867,7 +896,8 @@ export const registerRoutes = async (
|
||||
secretBlindIndex: secretBlindIndexService,
|
||||
telemetry: telemetryService,
|
||||
projectUserAdditionalPrivilege: projectUserAdditionalPrivilegeService,
|
||||
identityProjectAdditionalPrivilege: identityProjectAdditionalPrivilegeService
|
||||
identityProjectAdditionalPrivilege: identityProjectAdditionalPrivilegeService,
|
||||
secretSharing: secretSharingService
|
||||
});
|
||||
|
||||
server.decorate<FastifyZodProvider["store"]>("store", {
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
DynamicSecretsSchema,
|
||||
IdentityProjectAdditionalPrivilegeSchema,
|
||||
IntegrationAuthsSchema,
|
||||
ProjectRolesSchema,
|
||||
SecretApprovalPoliciesSchema,
|
||||
UsersSchema
|
||||
} from "@app/db/schemas";
|
||||
@@ -88,10 +89,38 @@ export const ProjectPermissionSchema = z.object({
|
||||
.optional()
|
||||
});
|
||||
|
||||
export const ProjectSpecificPrivilegePermissionSchema = z.object({
|
||||
actions: z
|
||||
.nativeEnum(ProjectPermissionActions)
|
||||
.describe("Describe what action an entity can take. Possible actions: create, edit, delete, and read")
|
||||
.array()
|
||||
.min(1),
|
||||
subject: z
|
||||
.enum([ProjectPermissionSub.Secrets])
|
||||
.describe("The entity this permission pertains to. Possible options: secrets, environments"),
|
||||
conditions: z
|
||||
.object({
|
||||
environment: z.string().describe("The environment slug this permission should allow."),
|
||||
secretPath: z
|
||||
.object({
|
||||
$glob: z
|
||||
.string()
|
||||
.min(1)
|
||||
.describe("The secret path this permission should allow. Can be a glob pattern such as /folder-name/*/** ")
|
||||
})
|
||||
.optional()
|
||||
})
|
||||
.describe("When specified, only matching conditions will be allowed to access given resource.")
|
||||
});
|
||||
|
||||
export const SanitizedIdentityPrivilegeSchema = IdentityProjectAdditionalPrivilegeSchema.extend({
|
||||
permissions: UnpackedPermissionSchema.array()
|
||||
});
|
||||
|
||||
export const SanitizedRoleSchema = ProjectRolesSchema.extend({
|
||||
permissions: UnpackedPermissionSchema.array()
|
||||
});
|
||||
|
||||
export const SanitizedDynamicSecretSchema = DynamicSecretsSchema.omit({
|
||||
inputIV: true,
|
||||
inputTag: true,
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { IdentityAzureAuthsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
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 { validateAzureAuthField } from "@app/services/identity-azure-auth/identity-azure-auth-validators";
|
||||
|
||||
export const registerIdentityAzureAuthRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/azure-auth/login",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Login with Azure Auth",
|
||||
body: z.object({
|
||||
identityId: z.string(),
|
||||
jwt: z.string()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
accessToken: z.string(),
|
||||
expiresIn: z.coerce.number(),
|
||||
accessTokenMaxTTL: z.coerce.number(),
|
||||
tokenType: z.literal("Bearer")
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { identityAzureAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
||||
await server.services.identityAzureAuth.login(req.body);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityMembershipOrg.orgId,
|
||||
event: {
|
||||
type: EventType.LOGIN_IDENTITY_AZURE_AUTH,
|
||||
metadata: {
|
||||
identityId: identityAzureAuth.identityId,
|
||||
identityAccessTokenId: identityAccessToken.id,
|
||||
identityAzureAuthId: identityAzureAuth.id
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
accessToken,
|
||||
tokenType: "Bearer" as const,
|
||||
expiresIn: identityAzureAuth.accessTokenTTL,
|
||||
accessTokenMaxTTL: identityAzureAuth.accessTokenMaxTTL
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/azure-auth/identities/:identityId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Attach Azure Auth configuration onto identity",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
identityId: z.string().trim()
|
||||
}),
|
||||
body: z.object({
|
||||
tenantId: z.string().trim(),
|
||||
resource: z.string().trim(),
|
||||
allowedServicePrincipalIds: validateAzureAuthField,
|
||||
accessTokenTrustedIps: z
|
||||
.object({
|
||||
ipAddress: z.string().trim()
|
||||
})
|
||||
.array()
|
||||
.min(1)
|
||||
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]),
|
||||
accessTokenTTL: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.refine((value) => value !== 0, {
|
||||
message: "accessTokenTTL must have a non zero number"
|
||||
})
|
||||
.default(2592000),
|
||||
accessTokenMaxTTL: z
|
||||
.number()
|
||||
.int()
|
||||
.refine((value) => value !== 0, {
|
||||
message: "accessTokenMaxTTL must have a non zero number"
|
||||
})
|
||||
.default(2592000),
|
||||
accessTokenNumUsesLimit: z.number().int().min(0).default(0)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
identityAzureAuth: IdentityAzureAuthsSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const identityAzureAuth = await server.services.identityAzureAuth.attachAzureAuth({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityAzureAuth.orgId,
|
||||
event: {
|
||||
type: EventType.ADD_IDENTITY_AZURE_AUTH,
|
||||
metadata: {
|
||||
identityId: identityAzureAuth.identityId,
|
||||
tenantId: identityAzureAuth.tenantId,
|
||||
resource: identityAzureAuth.resource,
|
||||
accessTokenTTL: identityAzureAuth.accessTokenTTL,
|
||||
accessTokenMaxTTL: identityAzureAuth.accessTokenMaxTTL,
|
||||
accessTokenTrustedIps: identityAzureAuth.accessTokenTrustedIps as TIdentityTrustedIp[],
|
||||
accessTokenNumUsesLimit: identityAzureAuth.accessTokenNumUsesLimit
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { identityAzureAuth };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/azure-auth/identities/:identityId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Update Azure Auth configuration on identity",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
identityId: z.string().trim()
|
||||
}),
|
||||
body: z.object({
|
||||
tenantId: z.string().trim().optional(),
|
||||
resource: z.string().trim().optional(),
|
||||
allowedServicePrincipalIds: validateAzureAuthField.optional(),
|
||||
accessTokenTrustedIps: z
|
||||
.object({
|
||||
ipAddress: z.string().trim()
|
||||
})
|
||||
.array()
|
||||
.min(1)
|
||||
.optional(),
|
||||
accessTokenTTL: z.number().int().min(0).optional(),
|
||||
accessTokenNumUsesLimit: z.number().int().min(0).optional(),
|
||||
accessTokenMaxTTL: z
|
||||
.number()
|
||||
.int()
|
||||
.refine((value) => value !== 0, {
|
||||
message: "accessTokenMaxTTL must have a non zero number"
|
||||
})
|
||||
.optional()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
identityAzureAuth: IdentityAzureAuthsSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const identityAzureAuth = await server.services.identityAzureAuth.updateAzureAuth({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
...req.body,
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityAzureAuth.orgId,
|
||||
event: {
|
||||
type: EventType.UPDATE_IDENTITY_AZURE_AUTH,
|
||||
metadata: {
|
||||
identityId: identityAzureAuth.identityId,
|
||||
tenantId: identityAzureAuth.tenantId,
|
||||
resource: identityAzureAuth.resource,
|
||||
accessTokenTTL: identityAzureAuth.accessTokenTTL,
|
||||
accessTokenMaxTTL: identityAzureAuth.accessTokenMaxTTL,
|
||||
accessTokenTrustedIps: identityAzureAuth.accessTokenTrustedIps as TIdentityTrustedIp[],
|
||||
accessTokenNumUsesLimit: identityAzureAuth.accessTokenNumUsesLimit
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { identityAzureAuth };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/azure-auth/identities/:identityId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Retrieve Azure Auth configuration on identity",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
identityId: z.string()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
identityAzureAuth: IdentityAzureAuthsSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const identityAzureAuth = await server.services.identityAzureAuth.getAzureAuth({
|
||||
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: identityAzureAuth.orgId,
|
||||
event: {
|
||||
type: EventType.GET_IDENTITY_AZURE_AUTH,
|
||||
metadata: {
|
||||
identityId: identityAzureAuth.identityId
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { identityAzureAuth };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -160,9 +160,9 @@ export const registerIdentityGcpAuthRouter = async (server: FastifyZodProvider)
|
||||
}),
|
||||
body: z.object({
|
||||
type: z.enum(["iam", "gce"]).optional(),
|
||||
allowedServiceAccounts: validateGcpAuthField,
|
||||
allowedProjects: validateGcpAuthField,
|
||||
allowedZones: validateGcpAuthField,
|
||||
allowedServiceAccounts: validateGcpAuthField.optional(),
|
||||
allowedProjects: validateGcpAuthField.optional(),
|
||||
allowedZones: validateGcpAuthField.optional(),
|
||||
accessTokenTrustedIps: z
|
||||
.object({
|
||||
ipAddress: z.string().trim()
|
||||
|
||||
@@ -5,6 +5,7 @@ import { registerCaRouter } from "./certificate-authority-router";
|
||||
import { registerCertRouter } from "./certificate-router";
|
||||
import { registerIdentityAccessTokenRouter } from "./identity-access-token-router";
|
||||
import { registerIdentityAwsAuthRouter } from "./identity-aws-iam-auth-router";
|
||||
import { registerIdentityAzureAuthRouter } from "./identity-azure-auth-router";
|
||||
import { registerIdentityGcpAuthRouter } from "./identity-gcp-auth-router";
|
||||
import { registerIdentityKubernetesRouter } from "./identity-kubernetes-auth-router";
|
||||
import { registerIdentityRouter } from "./identity-router";
|
||||
@@ -20,6 +21,7 @@ import { registerProjectMembershipRouter } from "./project-membership-router";
|
||||
import { registerProjectRouter } from "./project-router";
|
||||
import { registerSecretFolderRouter } from "./secret-folder-router";
|
||||
import { registerSecretImportRouter } from "./secret-import-router";
|
||||
import { registerSecretSharingRouter } from "./secret-sharing-router";
|
||||
import { registerSecretTagRouter } from "./secret-tag-router";
|
||||
import { registerSsoRouter } from "./sso-router";
|
||||
import { registerUserActionRouter } from "./user-action-router";
|
||||
@@ -36,6 +38,7 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
|
||||
await authRouter.register(registerIdentityGcpAuthRouter);
|
||||
await authRouter.register(registerIdentityAccessTokenRouter);
|
||||
await authRouter.register(registerIdentityAwsAuthRouter);
|
||||
await authRouter.register(registerIdentityAzureAuthRouter);
|
||||
},
|
||||
{ prefix: "/auth" }
|
||||
);
|
||||
@@ -73,4 +76,5 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
|
||||
await server.register(registerIntegrationAuthRouter, { prefix: "/integration-auth" });
|
||||
await server.register(registerWebhookRouter, { prefix: "/webhooks" });
|
||||
await server.register(registerIdentityRouter, { prefix: "/identities" });
|
||||
await server.register(registerSecretSharingRouter, { prefix: "/secret-sharing" });
|
||||
};
|
||||
|
||||
@@ -330,7 +330,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
teams: z
|
||||
.object({
|
||||
name: z.string(),
|
||||
id: z.string().optional()
|
||||
id: z.string()
|
||||
})
|
||||
.array()
|
||||
})
|
||||
|
||||
@@ -8,6 +8,7 @@ import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { IntegrationMappingBehavior } from "@app/services/integration-auth/integration-list";
|
||||
import { PostHogEventTypes, TIntegrationCreatedEvent } from "@app/services/telemetry/telemetry-types";
|
||||
|
||||
export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
@@ -49,6 +50,10 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
secretPrefix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretPrefix),
|
||||
secretSuffix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretSuffix),
|
||||
initialSyncBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.initialSyncBehavoir),
|
||||
mappingBehavior: z
|
||||
.nativeEnum(IntegrationMappingBehavior)
|
||||
.optional()
|
||||
.describe(INTEGRATION.CREATE.metadata.mappingBehavior),
|
||||
shouldAutoRedeploy: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldAutoRedeploy),
|
||||
secretGCPLabel: z
|
||||
.object({
|
||||
@@ -160,6 +165,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
secretPrefix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretPrefix),
|
||||
secretSuffix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretSuffix),
|
||||
initialSyncBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.initialSyncBehavoir),
|
||||
mappingBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.mappingBehavior),
|
||||
shouldAutoRedeploy: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldAutoRedeploy),
|
||||
secretGCPLabel: z
|
||||
.object({
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretSharingSchema } from "@app/db/schemas";
|
||||
import { publicEndpointLimit, readLimit, 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 registerSecretSharingRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
200: z.array(SecretSharingSchema)
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const sharedSecrets = await req.server.services.secretSharing.getSharedSecrets({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
orgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
|
||||
return sharedSecrets;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/public/:id",
|
||||
config: {
|
||||
rateLimit: publicEndpointLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
id: z.string().uuid()
|
||||
}),
|
||||
querystring: z.object({
|
||||
hashedHex: z.string()
|
||||
}),
|
||||
response: {
|
||||
200: SecretSharingSchema.pick({ name: true, encryptedValue: true, iv: true, tag: true, expiresAt: true })
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const sharedSecret = await req.server.services.secretSharing.getActiveSharedSecretByIdAndHashedHex(
|
||||
req.params.id,
|
||||
req.query.hashedHex
|
||||
);
|
||||
if (!sharedSecret) return undefined;
|
||||
return {
|
||||
name: sharedSecret.name,
|
||||
encryptedValue: sharedSecret.encryptedValue,
|
||||
iv: sharedSecret.iv,
|
||||
tag: sharedSecret.tag,
|
||||
expiresAt: sharedSecret.expiresAt
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
name: z.string(),
|
||||
encryptedValue: z.string(),
|
||||
iv: z.string(),
|
||||
tag: z.string(),
|
||||
hashedHex: z.string(),
|
||||
expiresAt: z.string().refine((date) => new Date(date) > new Date(), {
|
||||
message: "Expires at should be a future date"
|
||||
})
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
id: z.string().uuid()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { name, encryptedValue, iv, tag, hashedHex, expiresAt } = req.body;
|
||||
const sharedSecret = await req.server.services.secretSharing.createSharedSecret({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
orgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
name,
|
||||
encryptedValue,
|
||||
iv,
|
||||
tag,
|
||||
hashedHex,
|
||||
expiresAt: new Date(expiresAt)
|
||||
});
|
||||
return { id: sharedSecret.id };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:sharedSecretId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
sharedSecretId: z.string().uuid()
|
||||
}),
|
||||
response: {
|
||||
200: SecretSharingSchema
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { sharedSecretId } = req.params;
|
||||
const deletedSharedSecret = await req.server.services.secretSharing.deleteSharedSecretById({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
orgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
sharedSecretId
|
||||
});
|
||||
|
||||
return { ...deletedSharedSecret };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,11 +1,15 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { authRateLimit, readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
const appCfg = getConfig();
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/",
|
||||
@@ -25,4 +29,29 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
return { user };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:userId/unlock",
|
||||
config: {
|
||||
rateLimit: authRateLimit
|
||||
},
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
token: z.string().trim()
|
||||
}),
|
||||
params: z.object({
|
||||
userId: z.string()
|
||||
})
|
||||
},
|
||||
handler: async (req, res) => {
|
||||
try {
|
||||
await server.services.user.unlockUser(req.params.userId, req.query.token);
|
||||
} catch (err) {
|
||||
logger.error(`User unlock failed for ${req.params.userId}`);
|
||||
logger.error(err);
|
||||
}
|
||||
return res.redirect(`${appCfg.SITE_URL}/login`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import jwt from "jsonwebtoken";
|
||||
import { z } from "zod";
|
||||
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { mfaRateLimit } from "@app/server/config/rateLimiter";
|
||||
import { AuthModeMfaJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerMfaRouter = async (server: FastifyZodProvider) => {
|
||||
@@ -34,7 +34,7 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => {
|
||||
method: "POST",
|
||||
url: "/mfa/send",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
rateLimit: mfaRateLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
@@ -53,7 +53,7 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => {
|
||||
url: "/mfa/verify",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
rateLimit: mfaRateLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
|
||||
Reference in New Issue
Block a user