fix rendering issues on resource unauthorized

This commit is contained in:
Milo Schwartz 2024-11-29 21:48:48 -05:00
parent 5bbf32f6a6
commit 500a81aa42
No known key found for this signature in database
8 changed files with 58 additions and 20 deletions

View file

@ -4,7 +4,7 @@
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "ENVIRONMENT=dev tsx watch server/index.ts", "dev": "NODE_ENV=development ENVIRONMENT=dev tsx watch server/index.ts",
"db:generate": "drizzle-kit generate", "db:generate": "drizzle-kit generate",
"db:push": "npx tsx server/db/migrate.ts", "db:push": "npx tsx server/db/migrate.ts",
"db:hydrate": "npx tsx scripts/hydrate.ts", "db:hydrate": "npx tsx scripts/hydrate.ts",

View file

@ -51,7 +51,7 @@ app.prepare().then(() => {
externalServer.use(logIncomingMiddleware); externalServer.use(logIncomingMiddleware);
externalServer.use(prefix, unauthenticated); externalServer.use(prefix, unauthenticated);
externalServer.use(prefix, authenticated); externalServer.use(prefix, authenticated);
externalServer.use(`${prefix}/ws`, wsRouter); // externalServer.use(`${prefix}/ws`, wsRouter);
externalServer.use(notFoundMiddleware); externalServer.use(notFoundMiddleware);
@ -68,7 +68,7 @@ app.prepare().then(() => {
); );
}); });
handleWSUpgrade(httpServer); // handleWSUpgrade(httpServer);
externalServer.use(errorHandlerMiddleware); externalServer.use(errorHandlerMiddleware);

View file

@ -10,6 +10,7 @@ import {
resourcePassword, resourcePassword,
resourcePincode, resourcePincode,
resources, resources,
User,
userOrgs, userOrgs,
} from "@server/db/schema"; } from "@server/db/schema";
import { and, eq } from "drizzle-orm"; import { and, eq } from "drizzle-orm";
@ -106,7 +107,7 @@ export async function verifyResourceSession(
const { session, user } = await validateSessionToken(sessionToken); const { session, user } = await validateSessionToken(sessionToken);
if (session && user) { if (session && user) {
const isAllowed = await isUserAllowedToAccessResource( const isAllowed = await isUserAllowedToAccessResource(
user.userId, user,
resource, resource,
); );
@ -191,15 +192,19 @@ function allowed(res: Response) {
} }
async function isUserAllowedToAccessResource( async function isUserAllowedToAccessResource(
userId: string, user: User,
resource: Resource, resource: Resource,
) { ): Promise<boolean> {
if (config.flags?.require_email_verification && !user.emailVerified) {
return false;
}
const userOrgRole = await db const userOrgRole = await db
.select() .select()
.from(userOrgs) .from(userOrgs)
.where( .where(
and( and(
eq(userOrgs.userId, userId), eq(userOrgs.userId, user.userId),
eq(userOrgs.orgId, resource.orgId), eq(userOrgs.orgId, resource.orgId),
), ),
) )
@ -229,7 +234,7 @@ async function isUserAllowedToAccessResource(
.from(userResources) .from(userResources)
.where( .where(
and( and(
eq(userResources.userId, userId), eq(userResources.userId, user.userId),
eq(userResources.resourceId, resource.resourceId), eq(userResources.resourceId, resource.resourceId),
), ),
) )

View file

@ -1,3 +1,6 @@
"use client";
import { Button } from "@app/components/ui/button";
import { import {
Card, Card,
CardContent, CardContent,
@ -5,8 +8,9 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@app/components/ui/card"; } from "@app/components/ui/card";
import Link from "next/link";
export default async function ResourceAccessDenied() { export default function ResourceAccessDenied() {
return ( return (
<Card className="w-full max-w-md"> <Card className="w-full max-w-md">
<CardHeader> <CardHeader>
@ -17,6 +21,11 @@ export default async function ResourceAccessDenied() {
<CardContent> <CardContent>
You're not alowed to access this resource. If this is a mistake, You're not alowed to access this resource. If this is a mistake,
please contact the administrator. please contact the administrator.
<div className="text-center mt-4">
<Button>
<Link href="/">Go Home</Link>
</Button>
</div>
</CardContent> </CardContent>
</Card> </Card>
); );

View file

@ -1,6 +1,6 @@
"use client"; "use client";
import { useState } from "react"; import { useEffect, useState } from "react";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import * as z from "zod"; import * as z from "zod";
@ -23,7 +23,7 @@ import {
FormLabel, FormLabel,
FormMessage, FormMessage,
} from "@/components/ui/form"; } from "@/components/ui/form";
import { LockIcon, UserIcon, Binary, Key, User } from "lucide-react"; import { LockIcon, Binary, Key, User } from "lucide-react";
import { import {
InputOTP, InputOTP,
InputOTPGroup, InputOTPGroup,
@ -34,11 +34,10 @@ import { useRouter } from "next/navigation";
import { Alert, AlertDescription } from "@app/components/ui/alert"; import { Alert, AlertDescription } from "@app/components/ui/alert";
import { formatAxiosError } from "@app/lib/utils"; import { formatAxiosError } from "@app/lib/utils";
import { AxiosResponse } from "axios"; import { AxiosResponse } from "axios";
import { LoginResponse } from "@server/routers/auth";
import ResourceAccessDenied from "./ResourceAccessDenied";
import LoginForm from "@app/components/LoginForm"; import LoginForm from "@app/components/LoginForm";
import { AuthWithPasswordResponse } from "@server/routers/resource"; import { AuthWithPasswordResponse } from "@server/routers/resource";
import { redirect } from "next/dist/server/api-utils"; import { redirect } from "next/dist/server/api-utils";
import ResourceAccessDenied from "./ResourceAccessDenied";
const pinSchema = z.object({ const pinSchema = z.object({
pin: z pin: z
@ -159,13 +158,15 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) {
}; };
async function handleSSOAuth() { async function handleSSOAuth() {
let isAllowed = false;
try { try {
await api.get(`/resource/${props.resource.id}`); await api.get(`/resource/${props.resource.id}`);
isAllowed = true;
} catch (e) { } catch (e) {
setAccessDenied(true); setAccessDenied(true);
} }
if (!accessDenied) { if (isAllowed) {
window.location.href = props.redirect; window.location.href = props.redirect;
} }
} }
@ -174,6 +175,11 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) {
<div> <div>
{!accessDenied ? ( {!accessDenied ? (
<div> <div>
<div className="text-center mb-2">
<span className="text-sm text-muted-foreground">
Powered by Fossorial
</span>
</div>
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle>Authentication Required</CardTitle> <CardTitle>Authentication Required</CardTitle>
@ -365,7 +371,11 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) {
className={`${numMethods <= 1 ? "mt-0" : ""}`} className={`${numMethods <= 1 ? "mt-0" : ""}`}
> >
<LoginForm <LoginForm
redirect={window.location.href} redirect={
typeof window !== "undefined"
? window.location.href
: ""
}
onLogin={async () => onLogin={async () =>
await handleSSOAuth() await handleSSOAuth()
} }

View file

@ -1,3 +1,4 @@
import { Button } from "@app/components/ui/button";
import { import {
Card, Card,
CardContent, CardContent,
@ -5,6 +6,7 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@app/components/ui/card"; } from "@app/components/ui/card";
import Link from "next/link";
export default async function ResourceNotFound() { export default async function ResourceNotFound() {
return ( return (
@ -15,7 +17,12 @@ export default async function ResourceNotFound() {
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
The resource you're trying to access does not exist The resource you're trying to access does not exist.
<div className="text-center mt-4">
<Button>
<Link href="/">Go Home</Link>
</Button>
</div>
</CardContent> </CardContent>
</Card> </Card>
); );

View file

@ -33,7 +33,7 @@ export default async function ResourceAuthPage(props: {
} catch (e) {} } catch (e) {}
const getUser = cache(verifySession); const getUser = cache(verifySession);
const user = await getUser(); const user = await getUser({ skipCheckVerifyEmail: true });
if (!authInfo) { if (!authInfo) {
return ( return (
@ -48,6 +48,16 @@ export default async function ResourceAuthPage(props: {
const redirectUrl = searchParams.redirect || authInfo.url; const redirectUrl = searchParams.redirect || authInfo.url;
if (
user &&
!user.emailVerified &&
process.env.FLAGS_EMAIL_VERIFICATION_REQUIRED === "true"
) {
redirect(
`/auth/verify-email?redirect=/auth/resource/${authInfo.resourceId}`,
);
}
const allCookies = await cookies(); const allCookies = await cookies();
const cookieName = const cookieName =
process.env.RESOURCE_SESSION_COOKIE_NAME + `_${params.resourceId}`; process.env.RESOURCE_SESSION_COOKIE_NAME + `_${params.resourceId}`;

View file

@ -72,14 +72,11 @@ export default function LoginForm({ redirect, onLogin }: LoginFormProps) {
); );
}); });
console.log(res);
if (res && res.status === 200) { if (res && res.status === 200) {
setError(null); setError(null);
if (res.data?.data?.emailVerificationRequired) { if (res.data?.data?.emailVerificationRequired) {
if (redirect) { if (redirect) {
console.log("here", redirect)
router.push(`/auth/verify-email?redirect=${redirect}`); router.push(`/auth/verify-email?redirect=${redirect}`);
} else { } else {
router.push("/auth/verify-email"); router.push("/auth/verify-email");