Fix merge conflicts

This commit is contained in:
Tuan Dang
2025-04-30 18:32:56 -07:00
149 changed files with 2974 additions and 304 deletions
+1
View File
@@ -1559,6 +1559,7 @@ export const registerRoutes = async (
const secretSyncService = secretSyncServiceFactory({
secretSyncDAL,
secretImportDAL,
permissionService,
appConnectionService,
folderDAL,
@@ -10,6 +10,10 @@ import {
AzureAppConfigurationConnectionListItemSchema,
SanitizedAzureAppConfigurationConnectionSchema
} from "@app/services/app-connection/azure-app-configuration";
import {
AzureClientSecretsConnectionListItemSchema,
SanitizedAzureClientSecretsConnectionSchema
} from "@app/services/app-connection/azure-client-secrets";
import {
AzureKeyVaultConnectionListItemSchema,
SanitizedAzureKeyVaultConnectionSchema
@@ -63,8 +67,9 @@ const SanitizedAppConnectionSchema = z.union([
...SanitizedPostgresConnectionSchema.options,
...SanitizedMsSqlConnectionSchema.options,
...SanitizedCamundaConnectionSchema.options,
...SanitizedWindmillConnectionSchema.options,
...SanitizedAuth0ConnectionSchema.options,
...SanitizedAzureClientSecretsConnectionSchema.options,
...SanitizedWindmillConnectionSchema.options,
...SanitizedLdapConnectionSchema.options,
...SanitizedTeamCityConnectionSchema.options
]);
@@ -82,8 +87,9 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
PostgresConnectionListItemSchema,
MsSqlConnectionListItemSchema,
CamundaConnectionListItemSchema,
WindmillConnectionListItemSchema,
Auth0ConnectionListItemSchema,
AzureClientSecretsConnectionListItemSchema,
WindmillConnectionListItemSchema,
LdapConnectionListItemSchema,
TeamCityConnectionListItemSchema
]);
@@ -0,0 +1,49 @@
import { z } from "zod";
import { readLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import {
CreateAzureClientSecretsConnectionSchema,
SanitizedAzureClientSecretsConnectionSchema,
UpdateAzureClientSecretsConnectionSchema
} from "@app/services/app-connection/azure-client-secrets";
import { AuthMode } from "@app/services/auth/auth-type";
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
export const registerAzureClientSecretsConnectionRouter = async (server: FastifyZodProvider) => {
registerAppConnectionEndpoints({
app: AppConnection.AzureClientSecrets,
server,
sanitizedResponseSchema: SanitizedAzureClientSecretsConnectionSchema,
createSchema: CreateAzureClientSecretsConnectionSchema,
updateSchema: UpdateAzureClientSecretsConnectionSchema
});
server.route({
method: "GET",
url: `/:connectionId/clients`,
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
connectionId: z.string().uuid()
}),
response: {
200: z.object({
clients: z.object({ name: z.string(), id: z.string(), appId: z.string() }).array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { connectionId } = req.params;
const clients = await server.services.appConnection.azureClientSecrets.listApps(connectionId, req.permission);
return { clients };
}
});
};
@@ -3,6 +3,7 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums
import { registerAuth0ConnectionRouter } from "./auth0-connection-router";
import { registerAwsConnectionRouter } from "./aws-connection-router";
import { registerAzureAppConfigurationConnectionRouter } from "./azure-app-configuration-connection-router";
import { registerAzureClientSecretsConnectionRouter } from "./azure-client-secrets-connection-router";
import { registerAzureKeyVaultConnectionRouter } from "./azure-key-vault-connection-router";
import { registerCamundaConnectionRouter } from "./camunda-connection-router";
import { registerDatabricksConnectionRouter } from "./databricks-connection-router";
@@ -26,6 +27,7 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record<AppConnection, (server:
[AppConnection.GCP]: registerGcpConnectionRouter,
[AppConnection.AzureKeyVault]: registerAzureKeyVaultConnectionRouter,
[AppConnection.AzureAppConfiguration]: registerAzureAppConfigurationConnectionRouter,
[AppConnection.AzureClientSecrets]: registerAzureClientSecretsConnectionRouter,
[AppConnection.Databricks]: registerDatabricksConnectionRouter,
[AppConnection.Humanitec]: registerHumanitecConnectionRouter,
[AppConnection.TerraformCloud]: registerTerraformCloudConnectionRouter,
+14 -1
View File
@@ -2,6 +2,7 @@ import jwt from "jsonwebtoken";
import { z } from "zod";
import { getConfig } from "@app/lib/config/env";
import { getMinExpiresIn } from "@app/lib/fn";
import { authRateLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode, AuthTokenType } from "@app/services/auth/auth-type";
@@ -79,6 +80,18 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
handler: async (req) => {
const { decodedToken, tokenVersion } = await server.services.authToken.validateRefreshToken(req.cookies.jid);
const appCfg = getConfig();
let expiresIn: string | number = appCfg.JWT_AUTH_LIFETIME;
if (decodedToken.organizationId) {
const org = await server.services.org.findOrganizationById(
decodedToken.userId,
decodedToken.organizationId,
decodedToken.authMethod,
decodedToken.organizationId
);
if (org && org.userTokenExpiration) {
expiresIn = getMinExpiresIn(appCfg.JWT_AUTH_LIFETIME, org.userTokenExpiration);
}
}
const token = jwt.sign(
{
@@ -92,7 +105,7 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
mfaMethod: decodedToken.mfaMethod
},
appCfg.AUTH_SECRET,
{ expiresIn: appCfg.JWT_AUTH_LIFETIME }
{ expiresIn }
);
return { token, organizationId: decodedToken.organizationId };
@@ -154,7 +154,8 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
secrets: z
.object({
secretId: z.string(),
referencedSecretKey: z.string()
referencedSecretKey: z.string(),
referencedSecretEnv: z.string()
})
.array()
.optional()
@@ -166,6 +167,16 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
})
.array()
.optional(),
usedBySecretSyncs: z
.object({
name: z.string(),
destination: z.string(),
environment: z.string(),
id: z.string(),
path: z.string()
})
.array()
.optional(),
totalFolderCount: z.number().optional(),
totalDynamicSecretCount: z.number().optional(),
totalSecretCount: z.number().optional(),
@@ -500,6 +511,24 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
}
}
const usedBySecretSyncs: { name: string; destination: string; environment: string; id: string; path: string }[] =
[];
for await (const environment of environments) {
const secretSyncs = await server.services.secretSync.listSecretSyncsBySecretPath(
{ projectId, secretPath, environment },
req.permission
);
secretSyncs.forEach((sync) => {
usedBySecretSyncs.push({
name: sync.name,
destination: sync.destination,
environment,
id: sync.id,
path: sync.folder?.path || "/"
});
});
}
return {
folders,
dynamicSecrets,
@@ -512,6 +541,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
totalSecretCount,
totalSecretRotationCount,
importedByEnvs,
usedBySecretSyncs,
totalCount:
(totalFolderCount ?? 0) +
(totalDynamicSecretCount ?? 0) +
@@ -611,6 +641,16 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
totalFolderCount: z.number().optional(),
totalDynamicSecretCount: z.number().optional(),
totalSecretCount: z.number().optional(),
usedBySecretSyncs: z
.object({
name: z.string(),
destination: z.string(),
environment: z.string(),
id: z.string(),
path: z.string()
})
.array()
.optional(),
importedBy: z
.object({
environment: z.object({
@@ -624,7 +664,8 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
secrets: z
.object({
secretId: z.string(),
referencedSecretKey: z.string()
referencedSecretKey: z.string(),
referencedSecretEnv: z.string()
})
.array()
.optional()
@@ -904,6 +945,18 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
secrets
});
const secretSyncs = await server.services.secretSync.listSecretSyncsBySecretPath(
{ projectId, secretPath, environment },
req.permission
);
const usedBySecretSyncs = secretSyncs.map((sync) => ({
name: sync.name,
destination: sync.destination,
environment: sync.environment?.name || environment,
id: sync.id,
path: sync.folder?.path || "/"
}));
if (secrets?.length || secretRotations?.length) {
const secretCount =
(secrets?.length ?? 0) +
@@ -950,6 +1003,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
totalSecretCount,
totalSecretRotationCount,
importedBy,
usedBySecretSyncs,
totalCount:
(totalImportCount ?? 0) +
(totalFolderCount ?? 0) +
@@ -1,3 +1,4 @@
import RE2 from "re2";
import { z } from "zod";
import {
@@ -263,7 +264,18 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
enforceMfa: z.boolean().optional(),
selectedMfaMethod: z.nativeEnum(MfaMethod).optional(),
allowSecretSharingOutsideOrganization: z.boolean().optional(),
bypassOrgAuthEnabled: z.boolean().optional()
bypassOrgAuthEnabled: z.boolean().optional(),
userTokenExpiration: z
.string()
.refine((val) => new RE2(/^\d+[mhdw]$/).test(val), "Must be a number followed by m, h, d, or w")
.refine(
(val) => {
const numericPart = val.slice(0, -1);
return parseInt(numericPart, 10) >= 1;
},
{ message: "Duration value must be at least 1" }
)
.optional()
}),
response: {
200: z.object({
+32 -19
View File
@@ -9,9 +9,9 @@
import { Authenticator } from "@fastify/passport";
import fastifySession from "@fastify/session";
import RedisStore from "connect-redis";
import { Strategy as GitHubStrategy } from "passport-github";
import { Strategy as GitLabStrategy } from "passport-gitlab2";
import { Strategy as GoogleStrategy } from "passport-google-oauth20";
import { Strategy as OAuth2Strategy } from "passport-oauth2";
import { z } from "zod";
import { INFISICAL_PROVIDER_GITHUB_ACCESS_TOKEN } from "@app/lib/config/const";
@@ -19,7 +19,7 @@ import { getConfig } from "@app/lib/config/env";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { ms } from "@app/lib/ms";
import { fetchGithubEmails } from "@app/lib/requests/github";
import { fetchGithubEmails, fetchGithubUser } from "@app/lib/requests/github";
import { authRateLimit } from "@app/server/config/rateLimiter";
import { AuthMethod } from "@app/services/auth/auth-type";
import { OrgAuthMethod } from "@app/services/org/org-types";
@@ -44,6 +44,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
});
await server.register(passport.initialize());
await server.register(passport.secureSession());
// passport oauth strategy for Google
const isGoogleOauthActive = Boolean(appCfg.CLIENT_ID_GOOGLE_LOGIN && appCfg.CLIENT_SECRET_GOOGLE_LOGIN);
if (isGoogleOauthActive) {
@@ -54,8 +55,9 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
clientID: appCfg.CLIENT_ID_GOOGLE_LOGIN as string,
clientSecret: appCfg.CLIENT_SECRET_GOOGLE_LOGIN as string,
callbackURL: `${appCfg.SITE_URL}/api/v1/sso/google`,
scope: ["profile", " email"],
state: true
scope: ["profile", "email"],
state: true,
pkce: true
},
// eslint-disable-next-line
async (req, _accessToken, _refreshToken, profile, cb) => {
@@ -91,34 +93,44 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
const isGithubOauthActive = Boolean(appCfg.CLIENT_SECRET_GITHUB_LOGIN && appCfg.CLIENT_ID_GITHUB_LOGIN);
if (isGithubOauthActive) {
passport.use(
new GitHubStrategy(
"github",
new OAuth2Strategy(
{
passReqToCallback: true,
clientID: appCfg.CLIENT_ID_GITHUB_LOGIN as string,
clientSecret: appCfg.CLIENT_SECRET_GITHUB_LOGIN as string,
authorizationURL: "https://github.com/login/oauth/authorize",
tokenURL: "https://github.com/login/oauth/access_token",
clientID: appCfg.CLIENT_ID_GITHUB_LOGIN!,
clientSecret: appCfg.CLIENT_SECRET_GITHUB_LOGIN!,
callbackURL: `${appCfg.SITE_URL}/api/v1/sso/github`,
scope: ["user:email", "read:org"],
// akhilmhdh: because the ts type for this is outdated by the maintainer
state: true as unknown as string
state: true,
pkce: true,
passReqToCallback: true
},
// eslint-disable-next-line
async (req, accessToken, _refreshToken, profile, cb) => {
// @ts-expect-error this is because this is express type and not fastify
const callbackPort = req.session.get("callbackPort");
async (req: any, accessToken: string, _refreshToken: string, _profile: any, done: Function) => {
try {
const ghEmails = await fetchGithubEmails(accessToken);
const { email } = ghEmails.filter((gitHubEmail) => gitHubEmail.primary)[0];
if (!email) throw new Error("No primary email found");
// profile does not get automatically populated so we need to manually fetch user info
const user = await fetchGithubUser(accessToken);
const callbackPort = req.session.get("callbackPort");
const { isUserCompleted, providerAuthToken } = await server.services.login.oauth2Login({
email,
firstName: profile.displayName || profile.username || "",
firstName: user.name || user.login,
lastName: "",
authMethod: AuthMethod.GITHUB,
callbackPort
});
return cb(null, { isUserCompleted, providerAuthToken, externalProviderAccessToken: accessToken });
} catch (error) {
logger.error(error);
cb(error as Error, false);
done(null, { isUserCompleted, providerAuthToken, externalProviderAccessToken: accessToken });
} catch (err) {
logger.error(err);
done(err as Error, false);
}
}
)
@@ -138,7 +150,8 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
clientSecret: appCfg.CLIENT_SECRET_GITLAB_LOGIN,
callbackURL: `${appCfg.SITE_URL}/api/v1/sso/gitlab`,
baseURL: appCfg.CLIENT_GITLAB_LOGIN_URL,
state: true
state: true,
pkce: true
},
async (req: any, _accessToken: string, _refreshToken: string, profile: any, cb: any) => {
try {