feat(infisical-pg): fixed radix issue, bugs in oauth and tag cascade on

This commit is contained in:
Akhil Mohan
2024-01-16 18:22:50 +05:30
parent 92f7b45e01
commit c880a48749
14 changed files with 4815 additions and 44627 deletions

View File

@@ -59,7 +59,7 @@ declare module "fastify" {
};
// passport data
passportUser: {
isCompleted: string;
isUserCompleted: string;
providerAuthToken: string;
};
auditLogInfo: Pick<TCreateAuditLogDTO, "userAgent" | "userAgentType" | "ipAddress" | "actor">;

View File

@@ -12,8 +12,8 @@ export const createJunctionTable = (
table.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
table.uuid(`${table1Name}Id`).unsigned().notNullable(); // Foreign key for table1
table.uuid(`${table2Name}Id`).unsigned().notNullable(); // Foreign key for table2
table.foreign(`${table1Name}Id`).references("id").inTable(table1Name);
table.foreign(`${table2Name}Id`).references("id").inTable(table2Name);
table.foreign(`${table1Name}Id`).references("id").inTable(table1Name).onDelete("CASCADE");
table.foreign(`${table2Name}Id`).references("id").inTable(table2Name).onDelete("CASCADE");
});
// one time logic

View File

@@ -28,7 +28,8 @@ import {
projectAdminPermissions,
projectMemberPermissions,
projectNoAccessPermissions,
ProjectPermissionSet
ProjectPermissionSet,
projectViewerPermission
} from "./project-permission";
type TPermissionServiceFactoryDep = {
@@ -74,6 +75,8 @@ export const permissionServiceFactory = ({
return projectAdminPermissions;
case ProjectMembershipRole.Member:
return projectMemberPermissions;
case ProjectMembershipRole.Viewer:
return projectViewerPermission;
case ProjectMembershipRole.NoAccess:
return projectNoAccessPermissions;
case ProjectMembershipRole.Custom:

View File

@@ -114,6 +114,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
authMethod: AuthMethod.GITLAB,
callbackPort: req.query.state as string
});
console.log({ isUserCompleted, providerAuthToken });
return cb(null, { isUserCompleted, providerAuthToken });
}
@@ -152,7 +153,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
// this is due to zod type difference
}) as any,
handler: (req, res) => {
if (req.passportUser.isCompleted) {
if (req.passportUser.isUserCompleted) {
return res.redirect(
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(
req.passportUser.providerAuthToken
@@ -197,7 +198,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
// this is due to zod type difference
}) as any,
handler: (req, res) => {
if (req.passportUser.isCompleted) {
if (req.passportUser.isUserCompleted) {
return res.redirect(
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(
req.passportUser.providerAuthToken
@@ -242,7 +243,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
// this is due to zod type difference
}) as any,
handler: (req, res) => {
if (req.passportUser.isCompleted) {
if (req.passportUser.isUserCompleted) {
return res.redirect(
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(
req.passportUser.providerAuthToken

View File

@@ -66,6 +66,7 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {
email: req.body.email,
ip: req.realIp,
userAgent,
providerAuthToken: req.body.providerAuthToken,
clientProof: req.body.clientProof
});

View File

@@ -86,7 +86,8 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
await server.services.signup.completeEmailAccountSignup({
...req.body,
ip: req.realIp,
userAgent
userAgent,
authorization: req.headers.authorization
});
res.setCookie("jid", refreshToken, {

View File

@@ -0,0 +1,50 @@
import { getConfig } from "@app/lib/config/env";
import jwt from "jsonwebtoken";
import {
AuthModeProviderJwtTokenPayload,
AuthModeProviderSignUpTokenPayload,
AuthTokenType
} from "./auth-type";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
export const validateProviderAuthToken = (providerToken: string, email: string) => {
if (!providerToken) throw new UnauthorizedError();
const appCfg = getConfig();
const decodedToken = jwt.verify(
providerToken,
appCfg.JWT_AUTH_SECRET
) as AuthModeProviderJwtTokenPayload;
console.log(decodedToken);
if (decodedToken.authTokenType !== AuthTokenType.PROVIDER_TOKEN) throw new UnauthorizedError();
if (decodedToken.email !== email) throw new Error("Invalid auth credentials");
};
export const validateSignUpAuthorization = async (token: string, userId: string) => {
const appCfg = getConfig();
const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>token?.split(" ", 2) ?? [
null,
null
];
if (AUTH_TOKEN_TYPE === null) {
throw new BadRequestError({ message: "Missing Authorization Header in the request header." });
}
if (AUTH_TOKEN_TYPE.toLowerCase() !== "bearer") {
throw new BadRequestError({
message: `The provided authentication type '${AUTH_TOKEN_TYPE}' is not supported.`
});
}
if (AUTH_TOKEN_VALUE === null) {
throw new BadRequestError({
message: "Missing Authorization Body in the request header"
});
}
const decodedToken = jwt.verify(
AUTH_TOKEN_VALUE,
appCfg.JWT_AUTH_SECRET
) as AuthModeProviderSignUpTokenPayload;
if (decodedToken.authTokenType !== AuthTokenType.SIGNUP_TOKEN) throw new UnauthorizedError();
if (decodedToken.userId !== userId) throw new UnauthorizedError();
};

View File

@@ -16,15 +16,7 @@ import {
TVerifyMfaTokenDTO
} from "./auth-login-type";
import { AuthMethod, AuthTokenType } from "./auth-type";
const isValidProviderAuthToken = (email: string, jwtSecret: string, providerAuthToken?: string) => {
if (!providerAuthToken) return false;
const decodedToken = jwt.verify(providerAuthToken, jwtSecret) as jwt.JwtPayload;
if (decodedToken.authTokenType !== AuthTokenType.PROVIDER_TOKEN) return false;
if (decodedToken.email !== email) return false;
return true;
};
import { validateProviderAuthToken } from "./auth-fns";
type TAuthLoginServiceFactoryDep = {
userDal: TUserDalFactory;
@@ -136,13 +128,10 @@ export const authLoginServiceFactory = ({
if (!userEnc || (userEnc && !userEnc.isAccepted)) {
throw new Error("Failed to find user");
}
const cfg = getConfig();
if (
!userEnc.authMethods?.includes(AuthMethod.EMAIL) &&
!isValidProviderAuthToken(email, cfg.JWT_AUTH_SECRET, providerAuthToken)
) {
throw new Error("Invalid authorization request");
if (!userEnc.authMethods?.includes(AuthMethod.EMAIL)) {
validateProviderAuthToken(providerAuthToken as string, email);
}
const serverSrpKey = await generateSrpServerKey(userEnc.salt, userEnc.verifier);
const userEncKeys = await userDal.updateUserEncryptionByUserId(userEnc.userId, {
clientPublicKey,
@@ -166,11 +155,8 @@ export const authLoginServiceFactory = ({
if (!userEnc) throw new Error("Failed to find user");
const cfg = getConfig();
if (
!userEnc.authMethods?.includes(AuthMethod.EMAIL) &&
!isValidProviderAuthToken(email, cfg.JWT_AUTH_SECRET, providerAuthToken)
) {
throw new Error("Invalid authorization request");
if (!userEnc.authMethods?.includes(AuthMethod.EMAIL)) {
validateProviderAuthToken(providerAuthToken as string, email);
}
if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey)
@@ -252,7 +238,6 @@ export const authLoginServiceFactory = ({
}
const isLinkingRequired = !user?.authMethods?.includes(authMethod);
const isUserCompleted = user.isAccepted;
const providerAuthToken = jwt.sign(
{
authTokenType: AuthTokenType.PROVIDER_TOKEN,

View File

@@ -15,6 +15,7 @@ import { TUserDalFactory } from "../user/user-dal";
import { TAuthDalFactory } from "./auth-dal";
import { TCompleteAccountInviteDTO, TCompleteAccountSignupDTO } from "./auth-signup-type";
import { AuthMethod, AuthTokenType } from "./auth-type";
import { validateProviderAuthToken, validateSignUpAuthorization } from "./auth-fns";
type TAuthSignupDep = {
authDal: TAuthDalFactory;
@@ -98,7 +99,7 @@ export const authSignupServiceFactory = ({
email,
firstName,
lastName,
// providerAuthToken,
providerAuthToken,
salt,
verifier,
publicKey,
@@ -111,13 +112,20 @@ export const authSignupServiceFactory = ({
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
ip,
userAgent
userAgent,
authorization
}: TCompleteAccountSignupDTO) => {
const user = await userDal.findUserByEmail(email);
if (!user || (user && user.isAccepted)) {
throw new Error("Failed to complete account for complete user");
}
if (providerAuthToken) {
validateProviderAuthToken(providerAuthToken, user.email);
} else {
validateSignUpAuthorization(authorization, user.id);
}
const updateduser = await authDal.transaction(async (tx) => {
const us = await userDal.updateById(user.id, { firstName, lastName, isAccepted: true }, tx);
if (!us) throw new Error("User not found");

View File

@@ -16,6 +16,7 @@ export type TCompleteAccountSignupDTO = {
attributionSource?: string | undefined;
ip: string;
userAgent: string;
authorization: string;
};
export type TCompleteAccountInviteDTO = {

View File

@@ -47,3 +47,13 @@ export type AuthModeRefreshJwtTokenPayload = {
tokenVersionId: string;
refreshVersion: number;
};
export type AuthModeProviderJwtTokenPayload = {
authTokenType: AuthTokenType.PROVIDER_TOKEN;
email: string;
};
export type AuthModeProviderSignUpTokenPayload = {
authTokenType: AuthTokenType.SIGNUP_TOKEN;
userId: string;
};

49283
frontend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -12,9 +12,6 @@
"storybook": "storybook dev -p 6006 -s ./public",
"build-storybook": "storybook build"
},
"overrides": {
"@radix-ui/react-focus-scope": "1.0.4"
},
"dependencies": {
"@casl/ability": "^6.5.0",
"@casl/react": "^3.1.0",
@@ -34,19 +31,19 @@
"@octokit/rest": "^19.0.7",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-checkbox": "^1.0.1",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-hover-card": "^1.0.3",
"@radix-ui/react-label": "^2.0.0",
"@radix-ui/react-popover": "^1.0.3",
"@radix-ui/react-popper": "^1.1.1",
"@radix-ui/react-progress": "^1.0.1",
"@radix-ui/react-hover-card": "^1.0.7",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-popper": "^1.1.3",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-switch": "^1.0.1",
"@radix-ui/react-tabs": "^1.0.2",
"@radix-ui/react-toast": "^1.1.2",
"@radix-ui/react-tooltip": "^1.0.4",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toast": "^1.1.5",
"@radix-ui/react-tooltip": "^1.0.7",
"@reduxjs/toolkit": "^1.8.3",
"@stripe/react-stripe-js": "^1.16.3",
"@stripe/stripe-js": "^1.46.0",

View File

@@ -6,14 +6,22 @@ export const createJunctionTable = (
knex: Knex,
tableName: TableName,
table1Name: TableName,
table2Name: TableName
table2Name: TableName,
) =>
knex.schema.createTable(tableName, (table) => {
table.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
table.uuid(`${table1Name}Id`).unsigned().notNullable(); // Foreign key for table1
table.uuid(`${table2Name}Id`).unsigned().notNullable(); // Foreign key for table2
table.foreign(`${table1Name}Id`).references("id").inTable(table1Name);
table.foreign(`${table2Name}Id`).references("id").inTable(table2Name);
table
.foreign(`${table1Name}Id`)
.references("id")
.inTable(table1Name)
.onDelete("CASCADE");
table
.foreign(`${table2Name}Id`)
.references("id")
.inTable(table2Name)
.onDelete("CASCADE");
});
// one time logic