Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions backend/src/ee/routes/v1/pam-session-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { PolicyRulesResponseSchema } from "@app/ee/services/pam-account-policy";
import { KubernetesSessionCredentialsSchema } from "@app/ee/services/pam-resource/kubernetes/kubernetes-resource-schemas";
import { MongoDBSessionCredentialsSchema } from "@app/ee/services/pam-resource/mongodb/mongodb-resource-schemas";
import { MsSQLSessionCredentialsSchema } from "@app/ee/services/pam-resource/mssql/mssql-resource-schemas";
import { MySQLSessionCredentialsSchema } from "@app/ee/services/pam-resource/mysql/mysql-resource-schemas";
import { OracleSessionCredentialsSchema } from "@app/ee/services/pam-resource/oracle/oracle-resource-schemas";
import { PostgresSessionCredentialsSchema } from "@app/ee/services/pam-resource/postgres/postgres-resource-schemas";
Expand All @@ -26,15 +27,17 @@ import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";

// Schemas with distinguishing fields must precede simpler ones — Zod strips unrecognized keys on first match.
const SessionCredentialsSchema = z.union([
MsSQLSessionCredentialsSchema,
SSHSessionCredentialsSchema,
WindowsSessionCredentialsSchema,
KubernetesSessionCredentialsSchema,
MongoDBSessionCredentialsSchema,
PostgresSessionCredentialsSchema,
MySQLSessionCredentialsSchema,
OracleSessionCredentialsSchema,
MongoDBSessionCredentialsSchema,
KubernetesSessionCredentialsSchema,
RedisSessionCredentialsSchema,
WindowsSessionCredentialsSchema
RedisSessionCredentialsSchema
]);

export const registerPamSessionRouter = async (server: FastifyZodProvider) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum MsSqlAuthMethod {
SqlLogin = "sql-login",
Ntlm = "ntlm"
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,31 @@
BaseUpdateGatewayPamResourceSchema,
BaseUpdatePamAccountSchema
} from "../pam-resource-schemas";
import {
BaseSqlAccountCredentialsSchema,
BaseSqlResourceConnectionDetailsSchema
} from "../shared/sql/sql-resource-schemas";
import { BaseSqlResourceConnectionDetailsSchema } from "../shared/sql/sql-resource-schemas";
import { MsSqlAuthMethod } from "./mssql-resource-enums";

export { MsSqlAuthMethod };

// Resources
export const MsSQLResourceConnectionDetailsSchema = BaseSqlResourceConnectionDetailsSchema;
export const MsSQLAccountCredentialsSchema = BaseSqlAccountCredentialsSchema;

const MsSQLSqlLoginCredentialsSchema = z.object({
authMethod: z.literal(MsSqlAuthMethod.SqlLogin),
username: z.string().trim().min(1).max(63),
password: z.string().trim().min(1).max(256)
});

const MsSQLNtlmCredentialsSchema = z.object({
authMethod: z.literal(MsSqlAuthMethod.Ntlm),
username: z.string().trim().min(1).max(63),

Check failure on line 29 in backend/src/ee/services/pam-resource/mssql/mssql-resource-schemas.ts

View check run for this annotation

Claude / Claude Code Review

Existing MSSQL accounts break after upgrade — no authMethod backfill

Pre-existing MSSQL PAM accounts will break after this PR ships: their stored credentials are `{username, password}` with no `authMethod` field, but every endpoint now serializes through a `z.discriminatedUnion("authMethod", ...)` (`SanitizedMsSQLCredentialsSchema`, `MsSQLAccountCredentialsSchema`, `MsSQLSessionCredentialsSchema`). Fastify-zod's serializerCompiler throws `ResponseValidationError`, so account list/get, GET /credentials, and the gateway session-credentials endpoint will all return
Comment thread
saifsmailbox98 marked this conversation as resolved.
password: z.string().trim().min(1).max(256),
domain: z.string().trim().min(1, "Domain is required for NTLM authentication").max(255)
});

export const MsSQLAccountCredentialsSchema = z.discriminatedUnion("authMethod", [
MsSQLSqlLoginCredentialsSchema,
MsSQLNtlmCredentialsSchema
]);

const BaseMsSQLResourceSchema = BasePamResourceSchema.extend({ resourceType: z.literal(PamResource.MsSQL) });

Expand All @@ -26,13 +43,14 @@
rotationAccountCredentials: MsSQLAccountCredentialsSchema.nullable().optional()
});

const SanitizedMsSQLCredentialsSchema = z.discriminatedUnion("authMethod", [
z.object({ authMethod: z.literal(MsSqlAuthMethod.SqlLogin), username: z.string() }),
z.object({ authMethod: z.literal(MsSqlAuthMethod.Ntlm), username: z.string(), domain: z.string() })
]);

export const SanitizedMsSQLResourceSchema = BaseMsSQLResourceSchema.extend({
connectionDetails: MsSQLResourceConnectionDetailsSchema,
rotationAccountCredentials: MsSQLAccountCredentialsSchema.pick({
username: true
})
.nullable()
.optional()
rotationAccountCredentials: SanitizedMsSQLCredentialsSchema.nullable().optional()
});

export const MsSQLResourceListItemSchema = z.object({
Expand Down Expand Up @@ -65,10 +83,11 @@

export const SanitizedMsSQLAccountWithResourceSchema = BasePamAccountSchemaWithResource.extend({
parentType: z.literal(PamResource.MsSQL),
credentials: MsSQLAccountCredentialsSchema.pick({
username: true
})
credentials: SanitizedMsSQLCredentialsSchema
});

// Sessions
export const MsSQLSessionCredentialsSchema = MsSQLResourceConnectionDetailsSchema.and(MsSQLAccountCredentialsSchema);
export const MsSQLSessionCredentialsSchema = z.union([
MsSQLResourceConnectionDetailsSchema.and(MsSQLSqlLoginCredentialsSchema),
MsSQLResourceConnectionDetailsSchema.and(MsSQLNtlmCredentialsSchema)
]);
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
resourceType: PamResource;
username?: string;
password?: string;
authMethod?: string; // MSSQL-only: "ntlm" triggers Windows auth via Tedious
domain?: string; // MSSQL-only: AD domain for NTLM authentication
}
): SqlResourceConnection => {
const { connectionDetails, resourceType, username, password } = config;
Expand Down Expand Up @@ -191,22 +193,35 @@
encrypt: true,
trustServerCertificate: !sslRejectUnauthorized,
cryptoCredentialsDetails: sslCertificate ? { ca: sslCertificate } : {},
// serverName tells tedious to use this hostname for TLS SNI and certificate validation
// instead of the server/host value used for the TCP connection
serverName: host
}
: { encrypt: false };

const isNtlm = config.authMethod === "ntlm";

if (isNtlm && !config.domain) {
throw new BadRequestError({ message: "Domain is required for NTLM authentication" });
}

const client = knex({
client: "mssql",
connection: {
server: "localhost",
port: proxyPort,
user: actualUsername,
password: actualPassword,
database: connectionDetails.database,
requestTimeout: EXTERNAL_REQUEST_TIMEOUT,
// mssqlOptions is passed to tedious driver
// Knex MSSQL dialect maps these flat fields into Tedious's authentication object
...(isNtlm
? {
type: "ntlm",
userName: actualUsername,
password: actualPassword,
domain: config.domain
}
: {
user: actualUsername,
password: actualPassword
}),
// ref: https://github.com/knex/knex/blob/b6507a7129d2b9fafebf5f831494431e64c6a8a0/lib/dialects/mssql/index.js#L66
options: mssqlOptions
}
Expand Down Expand Up @@ -253,6 +268,8 @@
gatewayId: string;
username?: string;
password?: string;
authMethod?: string; // MSSQL-only: "ntlm" triggers Windows auth via Tedious
domain?: string; // MSSQL-only: AD domain for NTLM authentication
},
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">,
operation: (connection: SqlResourceConnection) => Promise<T>
Expand Down Expand Up @@ -333,7 +350,9 @@
gatewayId,
resourceType,
username: credentials.username,
password: credentials.password
password: credentials.password,
authMethod: "authMethod" in credentials ? (credentials.authMethod as string) : undefined,
domain: "domain" in credentials ? (credentials.domain as string) : undefined

Check failure on line 355 in backend/src/ee/services/pam-resource/shared/sql/sql-resource-factory.ts

View workflow job for this annotation

GitHub Actions / Lint

This assertion is unnecessary since it does not change the type of the expression
},
gatewayV2Service,
async (client) => {
Comment thread
saifsmailbox98 marked this conversation as resolved.
Expand Down
24 changes: 22 additions & 2 deletions frontend/src/hooks/api/pam/types/mssql-resource.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,34 @@
import { PamResourceType } from "../enums";
import { TBaseSqlConnectionDetails, TBaseSqlCredentials } from "./shared/sql-resource";
import { TBaseSqlConnectionDetails } from "./shared/sql-resource";
import { TBasePamAccount } from "./base-account";
import { TBasePamResource } from "./base-resource";

export enum MsSqlAuthMethod {
SqlLogin = "sql-login",
Ntlm = "ntlm"
}

export type TMsSQLSqlLoginCredentials = {
authMethod: MsSqlAuthMethod.SqlLogin;
username: string;
password: string;
};

export type TMsSQLNtlmCredentials = {
authMethod: MsSqlAuthMethod.Ntlm;
username: string;
password: string;
domain: string;
};

export type TMsSQLCredentials = TMsSQLSqlLoginCredentials | TMsSQLNtlmCredentials;

// Resources
export type TMsSQLResource = TBasePamResource & { resourceType: PamResourceType.MsSQL } & {
connectionDetails: TBaseSqlConnectionDetails;
};

// Accounts
export type TMsSQLAccount = TBasePamAccount & {
credentials: TBaseSqlCredentials;
credentials: TMsSQLCredentials;
};
Loading
Loading