mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Swap to a single service function with a callback
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
|
import { Cluster, Redis } from "ioredis";
|
||||||
|
|
||||||
import { buildRedisFromConfig, TRedisConfigKeys } from "@app/lib/config/redis";
|
import { buildRedisFromConfig, TRedisConfigKeys } from "@app/lib/config/redis";
|
||||||
import { pgAdvisoryLockHashText } from "@app/lib/crypto/hashtext";
|
import { pgAdvisoryLockHashText } from "@app/lib/crypto/hashtext";
|
||||||
import { applyJitter } from "@app/lib/dates";
|
import { applyJitter } from "@app/lib/dates";
|
||||||
import { delay as delayMs } from "@app/lib/delay";
|
import { delay as delayMs } from "@app/lib/delay";
|
||||||
import { ExecutionResult, Redlock, Settings } from "@app/lib/red-lock";
|
import { ExecutionResult, Redlock, Settings } from "@app/lib/red-lock";
|
||||||
import { Redis, Cluster } from "ioredis";
|
|
||||||
|
|
||||||
export const PgSqlLock = {
|
export const PgSqlLock = {
|
||||||
BootUpMigration: 2023,
|
BootUpMigration: 2023,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import { Authenticator } from "@fastify/passport";
|
import { Authenticator } from "@fastify/passport";
|
||||||
import fastifySession from "@fastify/session";
|
import fastifySession from "@fastify/session";
|
||||||
import { FastifyRequest } from "fastify";
|
import { FastifyReply, FastifyRequest } from "fastify";
|
||||||
import { IncomingMessage } from "http";
|
import { IncomingMessage } from "http";
|
||||||
import LdapStrategy from "passport-ldapauth";
|
import LdapStrategy from "passport-ldapauth";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -136,40 +136,23 @@ export const registerIdentityLdapAuthRouter = async (server: FastifyZodProvider)
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
preValidation: [
|
preValidation: [
|
||||||
async (req, res) => {
|
(req, res) => {
|
||||||
const { lock } = await server.services.identityLdapAuth.checkLdapLockout({
|
const passportAuth = (request: FastifyRequest, reply: FastifyReply) =>
|
||||||
identityId: req.body.identityId,
|
(
|
||||||
username: req.body.username
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const passportRes = await (
|
|
||||||
passport.authenticate("ldapauth", {
|
passport.authenticate("ldapauth", {
|
||||||
failWithError: true,
|
failWithError: true,
|
||||||
session: false
|
session: false
|
||||||
}) as any
|
}) as any
|
||||||
)(req, res);
|
)(request, reply);
|
||||||
|
|
||||||
await server.services.identityLdapAuth.resetLdapLockoutCounter({
|
const { identityId, username } = req.body;
|
||||||
identityId: req.body.identityId,
|
return server.services.identityLdapAuth.withLdapLockout(
|
||||||
username: req.body.username
|
{
|
||||||
});
|
identityId,
|
||||||
|
username
|
||||||
return passportRes;
|
},
|
||||||
} catch (error) {
|
() => passportAuth(req, res)
|
||||||
if ((error as any).status === 401) {
|
);
|
||||||
await server.services.identityLdapAuth.incrementLdapLockout({
|
|
||||||
identityId: req.body.identityId,
|
|
||||||
username: req.body.username
|
|
||||||
});
|
|
||||||
|
|
||||||
throw new UnauthorizedError({ message: "Invalid credentials" });
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
await lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
|
|||||||
@@ -43,9 +43,7 @@ import {
|
|||||||
TCheckLdapAuthLockoutDTO,
|
TCheckLdapAuthLockoutDTO,
|
||||||
TClearLdapAuthLockoutsDTO,
|
TClearLdapAuthLockoutsDTO,
|
||||||
TGetLdapAuthDTO,
|
TGetLdapAuthDTO,
|
||||||
TIncrementLdapAuthLockoutDTO,
|
|
||||||
TLoginLdapAuthDTO,
|
TLoginLdapAuthDTO,
|
||||||
TResetLdapAuthLockoutCounterDTO,
|
|
||||||
TRevokeLdapAuthDTO,
|
TRevokeLdapAuthDTO,
|
||||||
TUpdateLdapAuthDTO
|
TUpdateLdapAuthDTO
|
||||||
} from "./identity-ldap-auth-types";
|
} from "./identity-ldap-auth-types";
|
||||||
@@ -653,7 +651,10 @@ export const identityLdapAuthServiceFactory = ({
|
|||||||
return revokedIdentityLdapAuth;
|
return revokedIdentityLdapAuth;
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkLdapLockout = async ({ identityId, username }: TCheckLdapAuthLockoutDTO) => {
|
const withLdapLockout = async <T>(
|
||||||
|
{ identityId, username }: TCheckLdapAuthLockoutDTO,
|
||||||
|
authFn: () => Promise<T>
|
||||||
|
): Promise<T> => {
|
||||||
const LOCKOUT_KEY = `lockout:identity:${identityId}:${IdentityAuthMethod.LDAP_AUTH}:${username.trim().toLowerCase()}`;
|
const LOCKOUT_KEY = `lockout:identity:${identityId}:${IdentityAuthMethod.LDAP_AUTH}:${username.trim().toLowerCase()}`;
|
||||||
|
|
||||||
let lock: Awaited<ReturnType<typeof keyStore.acquireLock>>;
|
let lock: Awaited<ReturnType<typeof keyStore.acquireLock>>;
|
||||||
@@ -667,65 +668,64 @@ export const identityLdapAuthServiceFactory = ({
|
|||||||
logger.info(
|
logger.info(
|
||||||
`identity login failed to acquire lock [identityId=${identityId}] [authMethod=${IdentityAuthMethod.LDAP_AUTH}]`
|
`identity login failed to acquire lock [identityId=${identityId}] [authMethod=${IdentityAuthMethod.LDAP_AUTH}]`
|
||||||
);
|
);
|
||||||
throw new RateLimitError({ message: "Rate limit exceeded" });
|
throw new RateLimitError({ message: "Failed to acquire lock: rate limit exceeded" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const lockoutRaw = await keyStore.getItem(LOCKOUT_KEY);
|
try {
|
||||||
|
|
||||||
if (lockoutRaw) {
|
|
||||||
const lockout = JSON.parse(lockoutRaw) as LockoutObject;
|
|
||||||
|
|
||||||
if (lockout.lockedOut) {
|
|
||||||
await lock.release();
|
|
||||||
throw new UnauthorizedError({
|
|
||||||
message: "This identity auth method is temporarily locked, please try again later"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { lock };
|
|
||||||
};
|
|
||||||
|
|
||||||
const incrementLdapLockout = async ({ identityId, username }: TIncrementLdapAuthLockoutDTO) => {
|
|
||||||
const identityLdapAuth = await identityLdapAuthDAL.findOne({ identityId });
|
|
||||||
if (!identityLdapAuth) {
|
|
||||||
throw new UnauthorizedError({
|
|
||||||
message: "Invalid credentials"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (identityLdapAuth.lockoutEnabled) {
|
|
||||||
const LOCKOUT_KEY = `lockout:identity:${identityId}:${IdentityAuthMethod.LDAP_AUTH}:${username.trim().toLowerCase()}`;
|
|
||||||
|
|
||||||
let lockout: LockoutObject = {
|
|
||||||
lockedOut: false,
|
|
||||||
failedAttempts: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const lockoutRaw = await keyStore.getItem(LOCKOUT_KEY);
|
const lockoutRaw = await keyStore.getItem(LOCKOUT_KEY);
|
||||||
if (lockoutRaw) {
|
if (lockoutRaw) {
|
||||||
lockout = JSON.parse(lockoutRaw) as LockoutObject;
|
const lockout = JSON.parse(lockoutRaw) as LockoutObject;
|
||||||
|
if (lockout.lockedOut) {
|
||||||
|
throw new UnauthorizedError({
|
||||||
|
message: "This identity auth method is temporarily locked, please try again later"
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lockout.failedAttempts += 1;
|
const result = await authFn();
|
||||||
if (lockout.failedAttempts >= identityLdapAuth.lockoutThreshold) {
|
|
||||||
lockout.lockedOut = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
await keyStore.setItemWithExpiry(
|
await keyStore.deleteItem(LOCKOUT_KEY);
|
||||||
LOCKOUT_KEY,
|
|
||||||
lockout.lockedOut ? identityLdapAuth.lockoutDurationSeconds : identityLdapAuth.lockoutCounterResetSeconds,
|
return result;
|
||||||
JSON.stringify(lockout)
|
} catch (error) {
|
||||||
);
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
||||||
|
if ((error as any).status === 401) {
|
||||||
|
const identityLdapAuth = await identityLdapAuthDAL.findOne({ identityId });
|
||||||
|
if (!identityLdapAuth) {
|
||||||
|
throw new UnauthorizedError({ message: "Invalid credentials" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identityLdapAuth.lockoutEnabled) {
|
||||||
|
let lockout: LockoutObject = {
|
||||||
|
lockedOut: false,
|
||||||
|
failedAttempts: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
const lockoutRaw = await keyStore.getItem(LOCKOUT_KEY);
|
||||||
|
if (lockoutRaw) {
|
||||||
|
lockout = JSON.parse(lockoutRaw) as LockoutObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
lockout.failedAttempts += 1;
|
||||||
|
if (lockout.failedAttempts >= identityLdapAuth.lockoutThreshold) {
|
||||||
|
lockout.lockedOut = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
await keyStore.setItemWithExpiry(
|
||||||
|
LOCKOUT_KEY,
|
||||||
|
lockout.lockedOut ? identityLdapAuth.lockoutDurationSeconds : identityLdapAuth.lockoutCounterResetSeconds,
|
||||||
|
JSON.stringify(lockout)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UnauthorizedError({ message: "Invalid credentials" });
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
await lock.release();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetLdapLockoutCounter = async ({ identityId, username }: TResetLdapAuthLockoutCounterDTO) => {
|
|
||||||
await keyStore.deleteItem(
|
|
||||||
`lockout:identity:${identityId}:${IdentityAuthMethod.LDAP_AUTH}:${username.trim().toLowerCase()}`
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const clearLdapAuthLockouts = async ({
|
const clearLdapAuthLockouts = async ({
|
||||||
identityId,
|
identityId,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -765,9 +765,7 @@ export const identityLdapAuthServiceFactory = ({
|
|||||||
login,
|
login,
|
||||||
revokeIdentityLdapAuth,
|
revokeIdentityLdapAuth,
|
||||||
getLdapAuth,
|
getLdapAuth,
|
||||||
checkLdapLockout,
|
withLdapLockout,
|
||||||
incrementLdapLockout,
|
|
||||||
resetLdapLockoutCounter,
|
|
||||||
clearLdapAuthLockouts
|
clearLdapAuthLockouts
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -73,13 +73,3 @@ export type TCheckLdapAuthLockoutDTO = {
|
|||||||
identityId: string;
|
identityId: string;
|
||||||
username: string;
|
username: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TIncrementLdapAuthLockoutDTO = {
|
|
||||||
identityId: string;
|
|
||||||
username: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TResetLdapAuthLockoutCounterDTO = {
|
|
||||||
identityId: string;
|
|
||||||
username: string;
|
|
||||||
};
|
|
||||||
|
|||||||
Reference in New Issue
Block a user