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

This commit is contained in:
Akhil Mohan
2024-01-27 12:38:23 +05:30
parent 92f7b45e01
commit c880a48749
14 changed files with 4815 additions and 44627 deletions
+1 -1
View File
@@ -59,7 +59,7 @@ declare module "fastify" {
}; };
// passport data // passport data
passportUser: { passportUser: {
isCompleted: string; isUserCompleted: string;
providerAuthToken: string; providerAuthToken: string;
}; };
auditLogInfo: Pick<TCreateAuditLogDTO, "userAgent" | "userAgentType" | "ipAddress" | "actor">; auditLogInfo: Pick<TCreateAuditLogDTO, "userAgent" | "userAgentType" | "ipAddress" | "actor">;
+2 -2
View File
@@ -12,8 +12,8 @@ export const createJunctionTable = (
table.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); table.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
table.uuid(`${table1Name}Id`).unsigned().notNullable(); // Foreign key for table1 table.uuid(`${table1Name}Id`).unsigned().notNullable(); // Foreign key for table1
table.uuid(`${table2Name}Id`).unsigned().notNullable(); // Foreign key for table2 table.uuid(`${table2Name}Id`).unsigned().notNullable(); // Foreign key for table2
table.foreign(`${table1Name}Id`).references("id").inTable(table1Name); table.foreign(`${table1Name}Id`).references("id").inTable(table1Name).onDelete("CASCADE");
table.foreign(`${table2Name}Id`).references("id").inTable(table2Name); table.foreign(`${table2Name}Id`).references("id").inTable(table2Name).onDelete("CASCADE");
}); });
// one time logic // one time logic
@@ -28,7 +28,8 @@ import {
projectAdminPermissions, projectAdminPermissions,
projectMemberPermissions, projectMemberPermissions,
projectNoAccessPermissions, projectNoAccessPermissions,
ProjectPermissionSet ProjectPermissionSet,
projectViewerPermission
} from "./project-permission"; } from "./project-permission";
type TPermissionServiceFactoryDep = { type TPermissionServiceFactoryDep = {
@@ -74,6 +75,8 @@ export const permissionServiceFactory = ({
return projectAdminPermissions; return projectAdminPermissions;
case ProjectMembershipRole.Member: case ProjectMembershipRole.Member:
return projectMemberPermissions; return projectMemberPermissions;
case ProjectMembershipRole.Viewer:
return projectViewerPermission;
case ProjectMembershipRole.NoAccess: case ProjectMembershipRole.NoAccess:
return projectNoAccessPermissions; return projectNoAccessPermissions;
case ProjectMembershipRole.Custom: case ProjectMembershipRole.Custom:
@@ -114,6 +114,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
authMethod: AuthMethod.GITLAB, authMethod: AuthMethod.GITLAB,
callbackPort: req.query.state as string callbackPort: req.query.state as string
}); });
console.log({ isUserCompleted, providerAuthToken });
return cb(null, { isUserCompleted, providerAuthToken }); return cb(null, { isUserCompleted, providerAuthToken });
} }
@@ -152,7 +153,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
// this is due to zod type difference // this is due to zod type difference
}) as any, }) as any,
handler: (req, res) => { handler: (req, res) => {
if (req.passportUser.isCompleted) { if (req.passportUser.isUserCompleted) {
return res.redirect( return res.redirect(
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent( `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(
req.passportUser.providerAuthToken req.passportUser.providerAuthToken
@@ -197,7 +198,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
// this is due to zod type difference // this is due to zod type difference
}) as any, }) as any,
handler: (req, res) => { handler: (req, res) => {
if (req.passportUser.isCompleted) { if (req.passportUser.isUserCompleted) {
return res.redirect( return res.redirect(
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent( `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(
req.passportUser.providerAuthToken req.passportUser.providerAuthToken
@@ -242,7 +243,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
// this is due to zod type difference // this is due to zod type difference
}) as any, }) as any,
handler: (req, res) => { handler: (req, res) => {
if (req.passportUser.isCompleted) { if (req.passportUser.isUserCompleted) {
return res.redirect( return res.redirect(
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent( `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(
req.passportUser.providerAuthToken req.passportUser.providerAuthToken
@@ -66,6 +66,7 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {
email: req.body.email, email: req.body.email,
ip: req.realIp, ip: req.realIp,
userAgent, userAgent,
providerAuthToken: req.body.providerAuthToken,
clientProof: req.body.clientProof clientProof: req.body.clientProof
}); });
@@ -86,7 +86,8 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
await server.services.signup.completeEmailAccountSignup({ await server.services.signup.completeEmailAccountSignup({
...req.body, ...req.body,
ip: req.realIp, ip: req.realIp,
userAgent userAgent,
authorization: req.headers.authorization
}); });
res.setCookie("jid", refreshToken, { res.setCookie("jid", refreshToken, {
+50
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();
};
@@ -16,15 +16,7 @@ import {
TVerifyMfaTokenDTO TVerifyMfaTokenDTO
} from "./auth-login-type"; } from "./auth-login-type";
import { AuthMethod, AuthTokenType } from "./auth-type"; import { AuthMethod, AuthTokenType } from "./auth-type";
import { validateProviderAuthToken } from "./auth-fns";
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;
};
type TAuthLoginServiceFactoryDep = { type TAuthLoginServiceFactoryDep = {
userDal: TUserDalFactory; userDal: TUserDalFactory;
@@ -136,13 +128,10 @@ export const authLoginServiceFactory = ({
if (!userEnc || (userEnc && !userEnc.isAccepted)) { if (!userEnc || (userEnc && !userEnc.isAccepted)) {
throw new Error("Failed to find user"); throw new Error("Failed to find user");
} }
const cfg = getConfig(); if (!userEnc.authMethods?.includes(AuthMethod.EMAIL)) {
if ( validateProviderAuthToken(providerAuthToken as string, email);
!userEnc.authMethods?.includes(AuthMethod.EMAIL) &&
!isValidProviderAuthToken(email, cfg.JWT_AUTH_SECRET, providerAuthToken)
) {
throw new Error("Invalid authorization request");
} }
const serverSrpKey = await generateSrpServerKey(userEnc.salt, userEnc.verifier); const serverSrpKey = await generateSrpServerKey(userEnc.salt, userEnc.verifier);
const userEncKeys = await userDal.updateUserEncryptionByUserId(userEnc.userId, { const userEncKeys = await userDal.updateUserEncryptionByUserId(userEnc.userId, {
clientPublicKey, clientPublicKey,
@@ -166,11 +155,8 @@ export const authLoginServiceFactory = ({
if (!userEnc) throw new Error("Failed to find user"); if (!userEnc) throw new Error("Failed to find user");
const cfg = getConfig(); const cfg = getConfig();
if ( if (!userEnc.authMethods?.includes(AuthMethod.EMAIL)) {
!userEnc.authMethods?.includes(AuthMethod.EMAIL) && validateProviderAuthToken(providerAuthToken as string, email);
!isValidProviderAuthToken(email, cfg.JWT_AUTH_SECRET, providerAuthToken)
) {
throw new Error("Invalid authorization request");
} }
if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey)
@@ -252,7 +238,6 @@ export const authLoginServiceFactory = ({
} }
const isLinkingRequired = !user?.authMethods?.includes(authMethod); const isLinkingRequired = !user?.authMethods?.includes(authMethod);
const isUserCompleted = user.isAccepted; const isUserCompleted = user.isAccepted;
const providerAuthToken = jwt.sign( const providerAuthToken = jwt.sign(
{ {
authTokenType: AuthTokenType.PROVIDER_TOKEN, authTokenType: AuthTokenType.PROVIDER_TOKEN,
@@ -15,6 +15,7 @@ import { TUserDalFactory } from "../user/user-dal";
import { TAuthDalFactory } from "./auth-dal"; import { TAuthDalFactory } from "./auth-dal";
import { TCompleteAccountInviteDTO, TCompleteAccountSignupDTO } from "./auth-signup-type"; import { TCompleteAccountInviteDTO, TCompleteAccountSignupDTO } from "./auth-signup-type";
import { AuthMethod, AuthTokenType } from "./auth-type"; import { AuthMethod, AuthTokenType } from "./auth-type";
import { validateProviderAuthToken, validateSignUpAuthorization } from "./auth-fns";
type TAuthSignupDep = { type TAuthSignupDep = {
authDal: TAuthDalFactory; authDal: TAuthDalFactory;
@@ -98,7 +99,7 @@ export const authSignupServiceFactory = ({
email, email,
firstName, firstName,
lastName, lastName,
// providerAuthToken, providerAuthToken,
salt, salt,
verifier, verifier,
publicKey, publicKey,
@@ -111,13 +112,20 @@ export const authSignupServiceFactory = ({
encryptedPrivateKeyIV, encryptedPrivateKeyIV,
encryptedPrivateKeyTag, encryptedPrivateKeyTag,
ip, ip,
userAgent userAgent,
authorization
}: TCompleteAccountSignupDTO) => { }: TCompleteAccountSignupDTO) => {
const user = await userDal.findUserByEmail(email); const user = await userDal.findUserByEmail(email);
if (!user || (user && user.isAccepted)) { if (!user || (user && user.isAccepted)) {
throw new Error("Failed to complete account for complete user"); 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 updateduser = await authDal.transaction(async (tx) => {
const us = await userDal.updateById(user.id, { firstName, lastName, isAccepted: true }, tx); const us = await userDal.updateById(user.id, { firstName, lastName, isAccepted: true }, tx);
if (!us) throw new Error("User not found"); if (!us) throw new Error("User not found");
@@ -16,6 +16,7 @@ export type TCompleteAccountSignupDTO = {
attributionSource?: string | undefined; attributionSource?: string | undefined;
ip: string; ip: string;
userAgent: string; userAgent: string;
authorization: string;
}; };
export type TCompleteAccountInviteDTO = { export type TCompleteAccountInviteDTO = {
+10
View File
@@ -47,3 +47,13 @@ export type AuthModeRefreshJwtTokenPayload = {
tokenVersionId: string; tokenVersionId: string;
refreshVersion: number; refreshVersion: number;
}; };
export type AuthModeProviderJwtTokenPayload = {
authTokenType: AuthTokenType.PROVIDER_TOKEN;
email: string;
};
export type AuthModeProviderSignUpTokenPayload = {
authTokenType: AuthTokenType.SIGNUP_TOKEN;
userId: string;
};
+4703 -44580
View File
File diff suppressed because it is too large Load Diff
+10 -13
View File
@@ -12,9 +12,6 @@
"storybook": "storybook dev -p 6006 -s ./public", "storybook": "storybook dev -p 6006 -s ./public",
"build-storybook": "storybook build" "build-storybook": "storybook build"
}, },
"overrides": {
"@radix-ui/react-focus-scope": "1.0.4"
},
"dependencies": { "dependencies": {
"@casl/ability": "^6.5.0", "@casl/ability": "^6.5.0",
"@casl/react": "^3.1.0", "@casl/react": "^3.1.0",
@@ -34,19 +31,19 @@
"@octokit/rest": "^19.0.7", "@octokit/rest": "^19.0.7",
"@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.5", "@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-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-hover-card": "^1.0.3", "@radix-ui/react-hover-card": "^1.0.7",
"@radix-ui/react-label": "^2.0.0", "@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.3", "@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-popper": "^1.1.1", "@radix-ui/react-popper": "^1.1.3",
"@radix-ui/react-progress": "^1.0.1", "@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-select": "^2.0.0", "@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-switch": "^1.0.1", "@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.2", "@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toast": "^1.1.2", "@radix-ui/react-toast": "^1.1.5",
"@radix-ui/react-tooltip": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.7",
"@reduxjs/toolkit": "^1.8.3", "@reduxjs/toolkit": "^1.8.3",
"@stripe/react-stripe-js": "^1.16.3", "@stripe/react-stripe-js": "^1.16.3",
"@stripe/stripe-js": "^1.46.0", "@stripe/stripe-js": "^1.46.0",
+11 -3
View File
@@ -6,14 +6,22 @@ export const createJunctionTable = (
knex: Knex, knex: Knex,
tableName: TableName, tableName: TableName,
table1Name: TableName, table1Name: TableName,
table2Name: TableName table2Name: TableName,
) => ) =>
knex.schema.createTable(tableName, (table) => { knex.schema.createTable(tableName, (table) => {
table.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); table.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
table.uuid(`${table1Name}Id`).unsigned().notNullable(); // Foreign key for table1 table.uuid(`${table1Name}Id`).unsigned().notNullable(); // Foreign key for table1
table.uuid(`${table2Name}Id`).unsigned().notNullable(); // Foreign key for table2 table.uuid(`${table2Name}Id`).unsigned().notNullable(); // Foreign key for table2
table.foreign(`${table1Name}Id`).references("id").inTable(table1Name); table
table.foreign(`${table2Name}Id`).references("id").inTable(table2Name); .foreign(`${table1Name}Id`)
.references("id")
.inTable(table1Name)
.onDelete("CASCADE");
table
.foreign(`${table2Name}Id`)
.references("id")
.inTable(table2Name)
.onDelete("CASCADE");
}); });
// one time logic // one time logic