From 2e443db3621dc9d97fffecb77b2d633174c857d3 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Tue, 1 Oct 2024 14:13:49 +0200 Subject: [PATCH 01/26] chore: comanyName is now optional and added confirmPassword field --- auth.config.ts | 22 +++++++++++----------- types/authFormSchema.ts | 5 ++++- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/auth.config.ts b/auth.config.ts index 55a81edcca..105a6934c7 100644 --- a/auth.config.ts +++ b/auth.config.ts @@ -32,17 +32,17 @@ export const authConfig = { newUser: "/sign-up", }, callbacks: { - authorized({ auth, request: { nextUrl } }) { - const isLoggedIn = !!auth?.user; - const isOnDashboard = nextUrl.pathname.startsWith("/"); - if (isOnDashboard) { - if (isLoggedIn) return true; - return false; // Redirect unauthenticated users to login page - } else if (isLoggedIn) { - return Response.redirect(new URL("/", nextUrl)); - } - return true; - }, + // authorized({ auth, request: { nextUrl } }) { + // const isLoggedIn = !!auth?.user; + // const isOnDashboard = nextUrl.pathname.startsWith("/"); + // if (isOnDashboard) { + // if (isLoggedIn) return true; + // return false; // Redirect unauthenticated users to login page + // } else if (isLoggedIn) { + // return Response.redirect(new URL("/", nextUrl)); + // } + // return true; + // }, jwt({ token, user }) { if (user) { diff --git a/types/authFormSchema.ts b/types/authFormSchema.ts index 3cd577de69..89c8904f56 100644 --- a/types/authFormSchema.ts +++ b/types/authFormSchema.ts @@ -3,7 +3,8 @@ import { z } from "zod"; export const authFormSchema = (type: string) => z.object({ // Sign Up - companyName: type === "sign-in" ? z.string().optional() : z.string().min(3), + companyName: + type === "sign-in" ? z.string().optional() : z.string().min(3).optional(), firstName: type === "sign-in" ? z.string().optional() @@ -13,6 +14,8 @@ export const authFormSchema = (type: string) => message: "The name must be at least 3 characters.", }) .max(20), + confirmPassword: + type === "sign-in" ? z.string().optional() : z.string().min(6), termsAndConditions: type === "sign-in" ? z.enum(["true"]).optional() From ceebfc9aca4e88e02d45c60e923490d24f33332f Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Tue, 1 Oct 2024 14:14:26 +0200 Subject: [PATCH 02/26] chore: remove unused dependency --- app/(auth)/sign-in/page.tsx | 2 -- app/(auth)/sign-up/page.tsx | 2 -- 2 files changed, 4 deletions(-) diff --git a/app/(auth)/sign-in/page.tsx b/app/(auth)/sign-in/page.tsx index f012c3f358..0f8ebffd55 100644 --- a/app/(auth)/sign-in/page.tsx +++ b/app/(auth)/sign-in/page.tsx @@ -1,5 +1,3 @@ -import React from "react"; - import { AuthForm } from "@/components/auth"; const SignIn = () => { diff --git a/app/(auth)/sign-up/page.tsx b/app/(auth)/sign-up/page.tsx index dfe04cadeb..8f7954ba34 100644 --- a/app/(auth)/sign-up/page.tsx +++ b/app/(auth)/sign-up/page.tsx @@ -1,5 +1,3 @@ -import React from "react"; - import { AuthForm } from "@/components/auth"; const SignUp = () => { From 650b95c4f1f3400c691104bb82e2eda5589906a6 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Tue, 1 Oct 2024 14:15:05 +0200 Subject: [PATCH 03/26] chore: add confirmPassword input in sign-up page --- components/auth/AuthForm.tsx | 30 ++++-------------- components/ui/custom/CustomInput.tsx | 46 +++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/components/auth/AuthForm.tsx b/components/auth/AuthForm.tsx index b1bbcb866a..c0316dcd4a 100644 --- a/components/auth/AuthForm.tsx +++ b/components/auth/AuthForm.tsx @@ -97,7 +97,6 @@ export const AuthForm = ({ type }: { type: string }) => {
{type === "sign-up" && ( @@ -115,6 +114,7 @@ export const AuthForm = ({ type }: { type: string }) => { type="text" label="Company Name" placeholder="Enter your company name" + isRequired={false} /> )} @@ -138,35 +138,17 @@ export const AuthForm = ({ type }: { type: string }) => { )} - {/* - {isConfirmVisible ? ( - - ) : ( - - )} - - } - label="Confirm Password" - name="confirmPassword" - placeholder="Confirm your password" - type={isConfirmVisible ? "text" : "password"} - variant="bordered" - /> */} {type === "sign-up" && ( ( <> + { type?: string; placeholder?: string; password?: boolean; + confirmPassword?: boolean; isRequired?: boolean; isInvalid?: boolean; } @@ -28,24 +29,53 @@ export const CustomInput = ({ labelPlacement = "inside", placeholder, variant = "bordered", + confirmPassword = false, password = false, isRequired = true, isInvalid, }: CustomInputProps) => { - const [isVisible, setIsVisible] = useState(false); + const [isPasswordVisible, setIsPasswordVisible] = useState(false); + const [isConfirmPasswordVisible, setIsConfirmPasswordVisible] = + useState(false); - const toggleVisibility = () => setIsVisible(!isVisible); + const inputLabel = confirmPassword + ? "Confirm Password" + : password + ? "Password" + : label; - const inputLabel = password ? "Password" : label; - const inputType = password ? (isVisible ? "text" : "password") : type; - const inputPlaceholder = password ? "Enter your password" : placeholder; - const inputIsRequired = password ? true : isRequired; + const inputPlaceholder = confirmPassword + ? "Confirm Password" + : password + ? "Password" + : placeholder; - const endContent = password && ( + const inputType = + password || confirmPassword + ? isPasswordVisible || isConfirmPasswordVisible + ? "text" + : "password" + : type; + const inputIsRequired = password || confirmPassword ? true : isRequired; + + const toggleVisibility = () => { + if (password) { + setIsPasswordVisible(!isPasswordVisible); + } else if (confirmPassword) { + setIsConfirmPasswordVisible(!isConfirmPasswordVisible); + } + }; + + const endContent = (password || confirmPassword) && ( ); From 76b1c83add58d0acffc12443850af47ba525a652 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Tue, 1 Oct 2024 14:29:58 +0200 Subject: [PATCH 04/26] chore: tweaks authFormSchema using zod validation for client side --- types/authFormSchema.ts | 65 ++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/types/authFormSchema.ts b/types/authFormSchema.ts index 89c8904f56..e9190fa75b 100644 --- a/types/authFormSchema.ts +++ b/types/authFormSchema.ts @@ -1,30 +1,43 @@ import { z } from "zod"; export const authFormSchema = (type: string) => - z.object({ - // Sign Up - companyName: - type === "sign-in" ? z.string().optional() : z.string().min(3).optional(), - firstName: - type === "sign-in" - ? z.string().optional() - : z - .string() - .min(3, { - message: "The name must be at least 3 characters.", - }) - .max(20), - confirmPassword: - type === "sign-in" ? z.string().optional() : z.string().min(6), - termsAndConditions: - type === "sign-in" - ? z.enum(["true"]).optional() - : z.enum(["true"], { - errorMap: () => ({ - message: "You must accept the terms and conditions.", + z + .object({ + // Sign Up + companyName: + type === "sign-in" + ? z.string().optional() + : z.string().min(3).optional(), + firstName: + type === "sign-in" + ? z.string().optional() + : z + .string() + .min(3, { + message: "The name must be at least 3 characters.", + }) + .max(20), + confirmPassword: + type === "sign-in" + ? z.string().optional() + : z.string().min(3, { + message: "It must contain at least 12 characters.", }), - }), - // both - email: z.string().email(), - password: z.string().min(6), - }); + termsAndConditions: + type === "sign-in" + ? z.enum(["true"]).optional() + : z.enum(["true"], { + errorMap: () => ({ + message: "You must accept the terms and conditions.", + }), + }), + // Fields for Sign In and Sign Up + email: z.string().email(), + password: z.string().min(3, { + message: "It must contain at least 12 characters.", + }), + }) + .refine((data) => data.password === data.confirmPassword, { + message: "The password must match", + path: ["confirmPassword"], + }); From 9e56a4a10d63329256a62f28cd566c3566015100 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Wed, 2 Oct 2024 06:25:35 +0200 Subject: [PATCH 05/26] chore: add id attibute to the customInput component to make unique fields --- components/ui/custom/CustomInput.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/components/ui/custom/CustomInput.tsx b/components/ui/custom/CustomInput.tsx index 6697dd1b50..26cc17cd9b 100644 --- a/components/ui/custom/CustomInput.tsx +++ b/components/ui/custom/CustomInput.tsx @@ -88,6 +88,7 @@ export const CustomInput = ({ <> Date: Wed, 2 Oct 2024 06:37:29 +0200 Subject: [PATCH 06/26] fix: apply password match validation only on sign-up form --- types/authFormSchema.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/types/authFormSchema.ts b/types/authFormSchema.ts index e9190fa75b..7705b2e19f 100644 --- a/types/authFormSchema.ts +++ b/types/authFormSchema.ts @@ -37,7 +37,10 @@ export const authFormSchema = (type: string) => message: "It must contain at least 12 characters.", }), }) - .refine((data) => data.password === data.confirmPassword, { - message: "The password must match", - path: ["confirmPassword"], - }); + .refine( + (data) => type === "sign-in" || data.password === data.confirmPassword, + { + message: "The password must match", + path: ["confirmPassword"], + }, + ); From 6e37d8d850f0e45bcca293d293db5860d0442404 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Wed, 2 Oct 2024 10:00:54 +0200 Subject: [PATCH 07/26] chore: update all providers API requests --- actions/providers.ts | 42 +++++++++++------------------------- auth.config.ts | 36 +++++++++++++++++++++---------- components/auth/AuthForm.tsx | 13 ++++++++--- 3 files changed, 47 insertions(+), 44 deletions(-) diff --git a/actions/providers.ts b/actions/providers.ts index a35bbb1d98..8b06cb9b5c 100644 --- a/actions/providers.ts +++ b/actions/providers.ts @@ -3,21 +3,15 @@ import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; -import { auth } from "@/auth.config"; import { parseStringify } from "@/lib"; -// Credentials for basic auth -const username = process.env.API_USERNAME; -const password = process.env.API_PASSWORD; - export const getProviders = async ({ page = 1, query = "", sort = "", filters = {}, }) => { - const session = await auth(); - const tenantId = session?.user.tenantId; + // const session = await auth(); if (isNaN(Number(page)) || page < 1) redirect("/providers"); @@ -38,8 +32,7 @@ export const getProviders = async ({ try { const providers = await fetch(url.toString(), { headers: { - "X-Tenant-ID": `${tenantId}`, - Authorization: "Basic " + btoa(`${username}:${password}`), + Accept: "application/vnd.api+json", }, }); const data = await providers.json(); @@ -53,8 +46,7 @@ export const getProviders = async ({ }; export const getProvider = async (formData: FormData) => { - const session = await auth(); - const tenantId = session?.user.tenantId; + // const session = await auth(); const providerId = formData.get("id"); const keyServer = process.env.API_BASE_URL; @@ -63,8 +55,7 @@ export const getProvider = async (formData: FormData) => { try { const providers = await fetch(url.toString(), { headers: { - "X-Tenant-ID": `${tenantId}`, - Authorization: "Basic " + btoa(`${username}:${password}`), + Accept: "application/vnd.api+json", }, }); const data = await providers.json(); @@ -78,10 +69,9 @@ export const getProvider = async (formData: FormData) => { }; export const updateProvider = async (formData: FormData) => { - const session = await auth(); + // const session = await auth(); const keyServer = process.env.API_BASE_URL; - const tenantId = session?.user.tenantId; const providerId = formData.get("providerId"); const providerAlias = formData.get("alias"); @@ -91,9 +81,8 @@ export const updateProvider = async (formData: FormData) => { const response = await fetch(url.toString(), { method: "PATCH", headers: { - "X-Tenant-ID": `${tenantId}`, - Authorization: "Basic " + btoa(`${username}:${password}`), "Content-Type": "application/vnd.api+json", + Accept: "application/vnd.api+json", }, body: JSON.stringify({ data: { @@ -117,11 +106,9 @@ export const updateProvider = async (formData: FormData) => { }; export const addProvider = async (formData: FormData) => { - const session = await auth(); + // const session = await auth(); const keyServer = process.env.API_BASE_URL; - const tenantId = session?.user.tenantId; - const providerType = formData.get("providerType"); const providerId = formData.get("providerId"); const providerAlias = formData.get("providerAlias"); @@ -132,9 +119,8 @@ export const addProvider = async (formData: FormData) => { const response = await fetch(url.toString(), { method: "POST", headers: { - "X-Tenant-ID": `${tenantId}`, - Authorization: "Basic " + btoa(`${username}:${password}`), "Content-Type": "application/vnd.api+json", + Accept: "application/vnd.api+json", }, body: JSON.stringify({ data: { @@ -159,9 +145,8 @@ export const addProvider = async (formData: FormData) => { }; export const checkConnectionProvider = async (formData: FormData) => { - const session = await auth(); + // const session = await auth(); const keyServer = process.env.API_BASE_URL; - const tenantId = session?.user.tenantId; const providerId = formData.get("id"); @@ -171,8 +156,7 @@ export const checkConnectionProvider = async (formData: FormData) => { const response = await fetch(url.toString(), { method: "POST", headers: { - "X-Tenant-ID": `${tenantId}`, - Authorization: "Basic " + btoa(`${username}:${password}`), + Accept: "application/vnd.api+json", }, }); const data = await response.json(); @@ -186,9 +170,8 @@ export const checkConnectionProvider = async (formData: FormData) => { }; export const deleteProvider = async (formData: FormData) => { - const session = await auth(); + // const session = await auth(); const keyServer = process.env.API_BASE_URL; - const tenantId = session?.user.tenantId; const providerId = formData.get("id"); const url = new URL(`${keyServer}/providers/${providerId}`); @@ -197,8 +180,7 @@ export const deleteProvider = async (formData: FormData) => { const response = await fetch(url.toString(), { method: "DELETE", headers: { - "X-Tenant-ID": `${tenantId}`, - Authorization: "Basic " + btoa(`${username}:${password}`), + Accept: "application/vnd.api+json", }, }); const data = await response.json(); diff --git a/auth.config.ts b/auth.config.ts index 105a6934c7..2756263fd1 100644 --- a/auth.config.ts +++ b/auth.config.ts @@ -5,6 +5,13 @@ import { z } from "zod"; import { userMockData } from "./lib"; +// const key = new TextEncoder().encode(process.env.AUTH_SECRET); +// const SALT_ROUNDS = 10; + +// export async function hashPassword(password: string) { +// return hash(password, SALT_ROUNDS); +// } + async function getUser(email: string, password: string): Promise { // Check if the user exists in the userMockData array. const user = userMockData.find((user) => user.email === email); @@ -32,17 +39,22 @@ export const authConfig = { newUser: "/sign-up", }, callbacks: { - // authorized({ auth, request: { nextUrl } }) { - // const isLoggedIn = !!auth?.user; - // const isOnDashboard = nextUrl.pathname.startsWith("/"); - // if (isOnDashboard) { - // if (isLoggedIn) return true; - // return false; // Redirect unauthenticated users to login page - // } else if (isLoggedIn) { - // return Response.redirect(new URL("/", nextUrl)); - // } - // return true; - // }, + authorized({ auth, request: { nextUrl } }) { + const isLoggedIn = !!auth?.user; + const isOnDashboard = nextUrl.pathname.startsWith("/"); + const isSignUpPage = nextUrl.pathname === "/sign-up"; + + // Permitir acceso a /sign-up incluso si no está autenticado + if (isSignUpPage) return true; + + if (isOnDashboard) { + if (isLoggedIn) return true; + return false; // Redirigir usuarios no autenticados a la página de login + } else if (isLoggedIn) { + return Response.redirect(new URL("/", nextUrl)); + } + return true; + }, jwt({ token, user }) { if (user) { @@ -75,6 +87,8 @@ export const authConfig = { return null; } const { email, password } = parsedCredentials.data; + console.log("email", email); + console.log("password", password); const user = await getUser(email, password); if (!user) return null; diff --git a/components/auth/AuthForm.tsx b/components/auth/AuthForm.tsx index c0316dcd4a..2d01e39320 100644 --- a/components/auth/AuthForm.tsx +++ b/components/auth/AuthForm.tsx @@ -31,6 +31,11 @@ export const AuthForm = ({ type }: { type: string }) => { defaultValues: { email: "", password: "", + ...(type === "sign-up" && { + firstName: "", + companyName: "", + confirmPassword: "", + }), }, }); @@ -48,14 +53,16 @@ export const AuthForm = ({ type }: { type: string }) => { try { // Sign-up logic will be here. if (type === "sign-in") { + console.log(data); dispatch({ email: data.email.toLowerCase(), password: data.password, }); } - // if (type === "sign-up") { - // const newUser = await signUp(data); - // } + if (type === "sign-up") { + console.log(data); + // const newUser = await signUp(data); + } } catch (error) { // eslint-disable-next-line no-console console.error(error); From a72b33597d8f5c22b80756bc20b36ea6d8adc859 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Wed, 2 Oct 2024 16:09:26 +0200 Subject: [PATCH 08/26] WIP --- actions/auth.ts | 55 ---------- actions/auth/auth.ts | 102 ++++++++++++++++++ actions/auth/index.ts | 1 + actions/{ => providers}/index.ts | 1 - actions/{ => providers}/providers.ts | 7 -- app/(prowler)/providers/page.tsx | 2 +- auth.config.ts | 40 ++----- components/auth/AuthForm.tsx | 4 +- .../providers/CheckConnectionProvider.tsx | 2 +- components/providers/forms/add-form.tsx | 2 +- components/providers/forms/delete-form.tsx | 2 +- components/providers/forms/edit-form.tsx | 2 +- components/ui/sidebar/SidebarWrap.tsx | 2 +- lib/index.ts | 1 - lib/seed.ts | 25 ----- package-lock.json | 18 +++- package.json | 2 + 17 files changed, 133 insertions(+), 135 deletions(-) delete mode 100644 actions/auth.ts create mode 100644 actions/auth/auth.ts create mode 100644 actions/auth/index.ts rename actions/{ => providers}/index.ts (54%) rename actions/{ => providers}/providers.ts (96%) delete mode 100644 lib/seed.ts diff --git a/actions/auth.ts b/actions/auth.ts deleted file mode 100644 index 2cdf70bfd2..0000000000 --- a/actions/auth.ts +++ /dev/null @@ -1,55 +0,0 @@ -"use server"; - -import { AuthError } from "next-auth"; -import { z } from "zod"; - -import { signIn, signOut } from "@/auth.config"; -import { authFormSchema } from "@/types"; - -const formSchemaSignIn = authFormSchema("sign-in"); - -const defaultValues: z.infer = { - email: "", - password: "", -}; - -export async function authenticate( - prevState: unknown, - formData: z.infer, -) { - try { - await new Promise((resolve) => setTimeout(resolve, 2000)); - await signIn("credentials", { - ...formData, - redirect: false, - }); - return { - message: "Success", - }; - } catch (error) { - if (error instanceof AuthError) { - switch (error.type) { - case "CredentialsSignin": - return { - message: "Credentials error", - errors: { - ...defaultValues, - credentials: "Incorrect email or password", - }, - }; - default: - return { - message: "Unknown error", - errors: { - ...defaultValues, - unknown: "Unknown error", - }, - }; - } - } - } -} - -export async function logOut() { - await signOut(); -} diff --git a/actions/auth/auth.ts b/actions/auth/auth.ts new file mode 100644 index 0000000000..2bd7f6e4c1 --- /dev/null +++ b/actions/auth/auth.ts @@ -0,0 +1,102 @@ +"use server"; + +import { AuthError } from "next-auth"; +import { z } from "zod"; + +import { signIn, signOut } from "@/auth.config"; +import { authFormSchema } from "@/types"; + +const formSchemaSignIn = authFormSchema("sign-in"); +// const formSchemaSignUp = authFormSchema("sign-up"); + +const defaultValues: z.infer = { + email: "", + password: "", +}; + +export async function authenticate( + prevState: unknown, + formData: z.infer, +) { + try { + await signIn("credentials", { + ...formData, + redirect: false, + }); + return { + message: "Success", + }; + } catch (error) { + if (error instanceof AuthError) { + switch (error.type) { + case "CredentialsSignin": + return { + message: "Credentials error", + errors: { + ...defaultValues, + credentials: "Incorrect email or password", + }, + }; + default: + return { + message: "Unknown error", + errors: { + ...defaultValues, + unknown: "Unknown error", + }, + }; + } + } + } +} + +export const getToken = async (formData: z.infer) => { + const keyServer = process.env.API_BASE_URL; + const url = new URL(`${keyServer}/tokens`); + + const bodyData = { + data: { + type: "Token", + attributes: { + email: formData.email, + password: formData.password, + }, + }, + }; + + try { + const response = await fetch(url.toString(), { + method: "POST", + headers: { + "Content-Type": "application/vnd.api+json", + Accept: "application/vnd.api+json", + }, + body: JSON.stringify(bodyData), + }); + + if (!response.ok) { + return null; + } + const data = await response.json(); + + // Verify if the response contains the expected data + if (data && data.data && data.data.attributes) { + // const token = data.data.attributes.access; + // const decodedToken = jwtDecode(token); + return { + // userId: decodedToken.user_id, + email: formData.email, + // token: token, + // Add here other user fields we need in the session + }; + } + } catch (error) { + // eslint-disable-next-line no-console + console.error("Error en getToken:", error); + return null; + } +}; + +export async function logOut() { + await signOut(); +} diff --git a/actions/auth/index.ts b/actions/auth/index.ts new file mode 100644 index 0000000000..97ccf76494 --- /dev/null +++ b/actions/auth/index.ts @@ -0,0 +1 @@ +export * from "./auth"; diff --git a/actions/index.ts b/actions/providers/index.ts similarity index 54% rename from actions/index.ts rename to actions/providers/index.ts index 0085735bb3..5532383f5f 100644 --- a/actions/index.ts +++ b/actions/providers/index.ts @@ -1,2 +1 @@ -export * from "./auth"; export * from "./providers"; diff --git a/actions/providers.ts b/actions/providers/providers.ts similarity index 96% rename from actions/providers.ts rename to actions/providers/providers.ts index 8b06cb9b5c..ea37ca8986 100644 --- a/actions/providers.ts +++ b/actions/providers/providers.ts @@ -11,8 +11,6 @@ export const getProviders = async ({ sort = "", filters = {}, }) => { - // const session = await auth(); - if (isNaN(Number(page)) || page < 1) redirect("/providers"); const keyServer = process.env.API_BASE_URL; @@ -46,7 +44,6 @@ export const getProviders = async ({ }; export const getProvider = async (formData: FormData) => { - // const session = await auth(); const providerId = formData.get("id"); const keyServer = process.env.API_BASE_URL; @@ -69,7 +66,6 @@ export const getProvider = async (formData: FormData) => { }; export const updateProvider = async (formData: FormData) => { - // const session = await auth(); const keyServer = process.env.API_BASE_URL; const providerId = formData.get("providerId"); @@ -106,7 +102,6 @@ export const updateProvider = async (formData: FormData) => { }; export const addProvider = async (formData: FormData) => { - // const session = await auth(); const keyServer = process.env.API_BASE_URL; const providerType = formData.get("providerType"); @@ -145,7 +140,6 @@ export const addProvider = async (formData: FormData) => { }; export const checkConnectionProvider = async (formData: FormData) => { - // const session = await auth(); const keyServer = process.env.API_BASE_URL; const providerId = formData.get("id"); @@ -170,7 +164,6 @@ export const checkConnectionProvider = async (formData: FormData) => { }; export const deleteProvider = async (formData: FormData) => { - // const session = await auth(); const keyServer = process.env.API_BASE_URL; const providerId = formData.get("id"); diff --git a/app/(prowler)/providers/page.tsx b/app/(prowler)/providers/page.tsx index a10cd9a618..34b347d8e0 100644 --- a/app/(prowler)/providers/page.tsx +++ b/app/(prowler)/providers/page.tsx @@ -1,7 +1,7 @@ import { Spacer } from "@nextui-org/react"; import { Suspense } from "react"; -import { getProviders } from "@/actions"; +import { getProviders } from "@/actions/providers"; import { FilterControls, filtersProviders } from "@/components/filters"; import { AddProvider } from "@/components/providers"; import { diff --git a/auth.config.ts b/auth.config.ts index 2756263fd1..c744a258aa 100644 --- a/auth.config.ts +++ b/auth.config.ts @@ -1,34 +1,8 @@ -import bcryptjs from "bcryptjs"; import NextAuth, { type NextAuthConfig } from "next-auth"; import Credentials from "next-auth/providers/credentials"; import { z } from "zod"; -import { userMockData } from "./lib"; - -// const key = new TextEncoder().encode(process.env.AUTH_SECRET); -// const SALT_ROUNDS = 10; - -// export async function hashPassword(password: string) { -// return hash(password, SALT_ROUNDS); -// } - -async function getUser(email: string, password: string): Promise { - // Check if the user exists in the userMockData array. - const user = userMockData.find((user) => user.email === email); - if (!user) return null; - - if (!bcryptjs.compareSync(password, user.password)) return null; - - return { - id: user.id, - tenantId: user.tenantId, - name: user.name, - companyName: user.companyName, - email: user.email, - role: user.role, - image: user.image, - }; -} +import { getToken } from "./actions/auth"; export const authConfig = { session: { @@ -65,6 +39,7 @@ export const authConfig = { session({ session, token }) { session.user = token.data as any; + console.log("session", session); return session; }, }, @@ -83,15 +58,12 @@ export const authConfig = { }) .safeParse(credentials); - if (!parsedCredentials.success) { - return null; - } - const { email, password } = parsedCredentials.data; - console.log("email", email); - console.log("password", password); + if (!parsedCredentials.success) return null; + + const user = await getToken(parsedCredentials.data); - const user = await getUser(email, password); if (!user) return null; + return user; }, }), diff --git a/components/auth/AuthForm.tsx b/components/auth/AuthForm.tsx index 2d01e39320..e14ca3ec0e 100644 --- a/components/auth/AuthForm.tsx +++ b/components/auth/AuthForm.tsx @@ -9,7 +9,7 @@ import { useFormState } from "react-dom"; import { useForm } from "react-hook-form"; import { z } from "zod"; -import { authenticate } from "@/actions"; +import { authenticate } from "@/actions/auth"; import { Form, FormControl, @@ -53,14 +53,12 @@ export const AuthForm = ({ type }: { type: string }) => { try { // Sign-up logic will be here. if (type === "sign-in") { - console.log(data); dispatch({ email: data.email.toLowerCase(), password: data.password, }); } if (type === "sign-up") { - console.log(data); // const newUser = await signUp(data); } } catch (error) { diff --git a/components/providers/CheckConnectionProvider.tsx b/components/providers/CheckConnectionProvider.tsx index 2fadafad04..d5bc9e727d 100644 --- a/components/providers/CheckConnectionProvider.tsx +++ b/components/providers/CheckConnectionProvider.tsx @@ -2,7 +2,7 @@ import { useRef } from "react"; -import { checkConnectionProvider } from "@/actions"; +import { checkConnectionProvider } from "@/actions/providers"; import { CustomButtonClientAction } from "../ui/custom"; import { useToast } from "../ui/toast"; diff --git a/components/providers/forms/add-form.tsx b/components/providers/forms/add-form.tsx index f30855d2d0..4c063c7aef 100644 --- a/components/providers/forms/add-form.tsx +++ b/components/providers/forms/add-form.tsx @@ -5,7 +5,7 @@ import { Dispatch, SetStateAction } from "react"; import { useForm } from "react-hook-form"; import * as z from "zod"; -import { addProvider } from "@/actions"; +import { addProvider } from "@/actions/providers"; import { SaveIcon } from "@/components/icons"; import { useToast } from "@/components/ui"; import { CustomButton, CustomInput } from "@/components/ui/custom"; diff --git a/components/providers/forms/delete-form.tsx b/components/providers/forms/delete-form.tsx index 434f453279..e08ac4ada5 100644 --- a/components/providers/forms/delete-form.tsx +++ b/components/providers/forms/delete-form.tsx @@ -5,7 +5,7 @@ import React, { Dispatch, SetStateAction } from "react"; import { useForm } from "react-hook-form"; import * as z from "zod"; -import { deleteProvider } from "@/actions"; +import { deleteProvider } from "@/actions/providers"; import { DeleteIcon } from "@/components/icons"; import { useToast } from "@/components/ui"; import { CustomButton } from "@/components/ui/custom"; diff --git a/components/providers/forms/edit-form.tsx b/components/providers/forms/edit-form.tsx index 669e4a9184..bae117b7da 100644 --- a/components/providers/forms/edit-form.tsx +++ b/components/providers/forms/edit-form.tsx @@ -5,7 +5,7 @@ import { Dispatch, SetStateAction } from "react"; import { useForm } from "react-hook-form"; import * as z from "zod"; -import { updateProvider } from "@/actions"; +import { updateProvider } from "@/actions/providers"; import { SaveIcon } from "@/components/icons"; import { useToast } from "@/components/ui"; import { CustomButton, CustomInput } from "@/components/ui/custom"; diff --git a/components/ui/sidebar/SidebarWrap.tsx b/components/ui/sidebar/SidebarWrap.tsx index 949cd59da0..59b29bb458 100644 --- a/components/ui/sidebar/SidebarWrap.tsx +++ b/components/ui/sidebar/SidebarWrap.tsx @@ -9,7 +9,7 @@ import { useSession } from "next-auth/react"; import React, { useCallback } from "react"; import { useMediaQuery } from "usehooks-ts"; -import { logOut } from "@/actions"; +import { logOut } from "@/actions/auth"; import { useUIStore } from "@/store"; import { diff --git a/lib/index.ts b/lib/index.ts index 8edc14574b..38d384fb6e 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,3 +1,2 @@ export * from "./custom"; -export * from "./seed"; export * from "./utils"; diff --git a/lib/seed.ts b/lib/seed.ts deleted file mode 100644 index 1ff00ee60b..0000000000 --- a/lib/seed.ts +++ /dev/null @@ -1,25 +0,0 @@ -import bcryptjs from "bcryptjs"; -import { v4 as uuidv4 } from "uuid"; - -export const userMockData = [ - { - id: uuidv4(), // Generate a unique UUID. - tenantId: "12646005-9067-4d2a-a098-8bb378604362", - email: "admin@prowler.com", - name: "Admin Prowler", - companyName: "Prowler", - password: bcryptjs.hashSync("123123", 10), - role: "admin", - image: null, - }, - { - id: uuidv4(), // Generate a unique UUID. - tenantId: "12646005-9067-4d2a-a098-8bb378604362", - email: "user@prowler.com", - name: "User Prowler", - companyName: "Prowler", - password: bcryptjs.hashSync("123123", 10), - role: "user", - image: null, - }, -]; diff --git a/package-lock.json b/package-lock.json index 9d3fc5073c..1f8510886b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,8 @@ "date-fns": "^3.6.0", "framer-motion": "~11.1.1", "intl-messageformat": "^10.5.0", + "jose": "^5.9.3", + "jwt-decode": "^4.0.0", "lucide-react": "^0.417.0", "next": "^14.2.12", "next-auth": "^5.0.0-beta.20", @@ -9423,9 +9425,10 @@ } }, "node_modules/jose": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.7.0.tgz", - "integrity": "sha512-3P9qfTYDVnNn642LCAqIKbTGb9a1TBxZ9ti5zEVEr48aDdflgRjhspWFb6WM4PzAfFbGMJYC4+803v8riCRAKw==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.3.tgz", + "integrity": "sha512-egLIoYSpcd+QUF+UHgobt5YzI2Pkw/H39ou9suW687MY6PmCwPmkNV/4TNjn1p2tX5xO3j0d0sq5hiYE24bSlg==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" } @@ -9507,6 +9510,15 @@ "node": ">=4.0" } }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", diff --git a/package.json b/package.json index 468ce86d58..67ac210f66 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "date-fns": "^3.6.0", "framer-motion": "~11.1.1", "intl-messageformat": "^10.5.0", + "jose": "^5.9.3", + "jwt-decode": "^4.0.0", "lucide-react": "^0.417.0", "next": "^14.2.12", "next-auth": "^5.0.0-beta.20", From 8e7dfcaa76222f5d0db4856de40b6bfdc88a1f07 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Wed, 2 Oct 2024 17:22:34 +0200 Subject: [PATCH 09/26] WIP --- actions/auth/auth.ts | 25 ++++++++++++++++++------- auth.config.ts | 4 ++-- components/auth/AuthForm.tsx | 4 +--- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/actions/auth/auth.ts b/actions/auth/auth.ts index 2bd7f6e4c1..19bbb5368b 100644 --- a/actions/auth/auth.ts +++ b/actions/auth/auth.ts @@ -1,11 +1,17 @@ "use server"; +import { jwtDecode, JwtPayload } from "jwt-decode"; import { AuthError } from "next-auth"; import { z } from "zod"; import { signIn, signOut } from "@/auth.config"; +import { parseStringify } from "@/lib"; import { authFormSchema } from "@/types"; +interface CustomJwtPayload extends JwtPayload { + user_id: string; +} + const formSchemaSignIn = authFormSchema("sign-in"); // const formSchemaSignUp = authFormSchema("sign-up"); @@ -75,25 +81,30 @@ export const getToken = async (formData: z.infer) => { }); if (!response.ok) { - return null; + throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); + const parsedData = parseStringify(data); + + const accessToken = parsedData.data.attributes.access; + const refreshToken = parsedData.data.attributes.refresh; + + const decodedToken = jwtDecode(accessToken); + const userId = decodedToken.user_id; // Verify if the response contains the expected data if (data && data.data && data.data.attributes) { - // const token = data.data.attributes.access; - // const decodedToken = jwtDecode(token); return { - // userId: decodedToken.user_id, email: formData.email, - // token: token, + accessToken, + refreshToken, + userId, // Add here other user fields we need in the session }; } } catch (error) { // eslint-disable-next-line no-console - console.error("Error en getToken:", error); - return null; + console.error("Error en trying to get token:", error); } }; diff --git a/auth.config.ts b/auth.config.ts index c744a258aa..ad196c0c8e 100644 --- a/auth.config.ts +++ b/auth.config.ts @@ -18,12 +18,12 @@ export const authConfig = { const isOnDashboard = nextUrl.pathname.startsWith("/"); const isSignUpPage = nextUrl.pathname === "/sign-up"; - // Permitir acceso a /sign-up incluso si no está autenticado + // Allow access to sign-up page if (isSignUpPage) return true; if (isOnDashboard) { if (isLoggedIn) return true; - return false; // Redirigir usuarios no autenticados a la página de login + return false; // Redirect users who are not logged in to the login page } else if (isLoggedIn) { return Response.redirect(new URL("/", nextUrl)); } diff --git a/components/auth/AuthForm.tsx b/components/auth/AuthForm.tsx index e14ca3ec0e..5b6b8402e7 100644 --- a/components/auth/AuthForm.tsx +++ b/components/auth/AuthForm.tsx @@ -48,8 +48,6 @@ export const AuthForm = ({ type }: { type: string }) => { }, [state]); const onSubmit = async (data: z.infer) => { - // Do something with the form values - // this will be type-safe and validated try { // Sign-up logic will be here. if (type === "sign-in") { @@ -183,7 +181,7 @@ export const AuthForm = ({ type }: { type: string }) => { {state?.message === "Credentials error" && (
-

Incorrect email or password

+

No user found

)} From d8c9720723cd0486528ba18bc0e141f82d79321e Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Thu, 3 Oct 2024 06:08:05 +0200 Subject: [PATCH 10/26] fix: order by default using sorting param --- app/(prowler)/providers/page.tsx | 2 +- components/providers/table/columns-provider.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(prowler)/providers/page.tsx b/app/(prowler)/providers/page.tsx index 34b347d8e0..a5cb092664 100644 --- a/app/(prowler)/providers/page.tsx +++ b/app/(prowler)/providers/page.tsx @@ -43,7 +43,7 @@ const SSRDataTable = async ({ searchParams: SearchParamsProps; }) => { const page = parseInt(searchParams.page?.toString() || "1", 10); - const sort = searchParams.sort?.toString() || ""; + const sort = searchParams.sort?.toString() || "-inserted_at"; // Extract all filter parameters const filters = Object.fromEntries( diff --git a/components/providers/table/columns-provider.tsx b/components/providers/table/columns-provider.tsx index fd190a811a..065b6d5863 100644 --- a/components/providers/table/columns-provider.tsx +++ b/components/providers/table/columns-provider.tsx @@ -42,7 +42,7 @@ export const ColumnsProvider: ColumnDef[] = [ { accessorKey: "uid", header: ({ column }) => ( - + ), cell: ({ row }) => { const { From 42ebf91a67b7c0f5d05a8c22dec91f76f33ae493 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Thu, 3 Oct 2024 06:22:35 +0200 Subject: [PATCH 11/26] chore: add the new colors for the dark mode --- auth.config.ts | 75 +++++++++++++++++++++++++++------------------- tailwind.config.js | 3 ++ 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/auth.config.ts b/auth.config.ts index ad196c0c8e..e2d591c080 100644 --- a/auth.config.ts +++ b/auth.config.ts @@ -12,37 +12,7 @@ export const authConfig = { signIn: "/sign-in", newUser: "/sign-up", }, - callbacks: { - authorized({ auth, request: { nextUrl } }) { - const isLoggedIn = !!auth?.user; - const isOnDashboard = nextUrl.pathname.startsWith("/"); - const isSignUpPage = nextUrl.pathname === "/sign-up"; - // Allow access to sign-up page - if (isSignUpPage) return true; - - if (isOnDashboard) { - if (isLoggedIn) return true; - return false; // Redirect users who are not logged in to the login page - } else if (isLoggedIn) { - return Response.redirect(new URL("/", nextUrl)); - } - return true; - }, - - jwt({ token, user }) { - if (user) { - token.data = user; - } - return token; - }, - - session({ session, token }) { - session.user = token.data as any; - console.log("session", session); - return session; - }, - }, providers: [ Credentials({ name: "credentials", @@ -68,6 +38,51 @@ export const authConfig = { }, }), ], + callbacks: { + authorized({ auth, request: { nextUrl } }) { + const isLoggedIn = !!auth?.user; + const isOnDashboard = nextUrl.pathname.startsWith("/"); + const isSignUpPage = nextUrl.pathname === "/sign-up"; + + // Allow access to sign-up page + if (isSignUpPage) return true; + + if (isOnDashboard) { + if (isLoggedIn) return true; + return false; // Redirect users who are not logged in to the login page + } else if (isLoggedIn) { + return Response.redirect(new URL("/", nextUrl)); + } + return true; + }, + + jwt: async ({ token, user, account }) => { + // console.log(`In jwt callback - Token is ${JSON.stringify(token)}`); + if (user && account) { + // console.log(`In jwt callback - User is ${JSON.stringify(user)}`); + // console.log(`In jwt callback - Account is ${JSON.stringify(account)}`); + // token.data = user; + return { + ...token, + accessToken: user.accessToken, + refreshToken: user.refreshToken, + user, + }; + } + return token; + }, + + session: async ({ session, token }) => { + console.log(`In session callback - Token is ${JSON.stringify(token)}`); + // session.user = token.data as any; + if (token) { + session.accessToken = token.accessToken; + session.refreshToken = token.refreshToken; + } + // console.log("session", session); + return session; + }, + }, } satisfies NextAuthConfig; export const { signIn, signOut, auth, handlers } = NextAuth(authConfig); diff --git a/tailwind.config.js b/tailwind.config.js index 6d2ff06563..f1a4b38db2 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -41,6 +41,9 @@ module.exports = { yellow: "#ffdf16", }, dark: { + DEFAULT: "#0E1117", + 700: "#151B23", + 400: "#262C36", title: "#E2E8F0", text: "#94a3b8" /* primary default for dark mode */, }, From c7d6484eb80ea3ee74a53bad587aee05221bd4e1 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Thu, 3 Oct 2024 18:20:28 +0200 Subject: [PATCH 12/26] chore: WIP --- actions/auth/auth.ts | 25 +++++++--------- auth.config.ts | 69 ++++++++++++++++++++++++++++++++++++++++---- types/components.ts | 6 ++++ 3 files changed, 80 insertions(+), 20 deletions(-) diff --git a/actions/auth/auth.ts b/actions/auth/auth.ts index 19bbb5368b..658d711402 100644 --- a/actions/auth/auth.ts +++ b/actions/auth/auth.ts @@ -1,16 +1,12 @@ "use server"; -import { jwtDecode, JwtPayload } from "jwt-decode"; +import { jwtDecode } from "jwt-decode"; import { AuthError } from "next-auth"; import { z } from "zod"; import { signIn, signOut } from "@/auth.config"; import { parseStringify } from "@/lib"; -import { authFormSchema } from "@/types"; - -interface CustomJwtPayload extends JwtPayload { - user_id: string; -} +import { authFormSchema, CustomJwtPayload } from "@/types"; const formSchemaSignIn = authFormSchema("sign-in"); // const formSchemaSignUp = authFormSchema("sign-up"); @@ -93,15 +89,14 @@ export const getToken = async (formData: z.infer) => { const userId = decodedToken.user_id; // Verify if the response contains the expected data - if (data && data.data && data.data.attributes) { - return { - email: formData.email, - accessToken, - refreshToken, - userId, - // Add here other user fields we need in the session - }; - } + + return { + email: formData.email, + accessToken, + refreshToken, + userId, + // Add here other user fields we need in the session + }; } catch (error) { // eslint-disable-next-line no-console console.error("Error en trying to get token:", error); diff --git a/auth.config.ts b/auth.config.ts index e2d591c080..b5d36fc8b3 100644 --- a/auth.config.ts +++ b/auth.config.ts @@ -1,8 +1,55 @@ +import { jwtDecode } from "jwt-decode"; import NextAuth, { type NextAuthConfig } from "next-auth"; import Credentials from "next-auth/providers/credentials"; import { z } from "zod"; import { getToken } from "./actions/auth"; +import { CustomJwtPayload } from "./types"; + +const refreshAccessToken = async (token: CustomJwtPayload) => { + const keyServer = process.env.API_BASE_URL; + const url = new URL(`${keyServer}/tokens/refresh`); + + const bodyData = { + data: { + type: "TokenRefresh", + attributes: { + refresh: token.refreshToken, + }, + }, + }; + + try { + const response = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/vnd.api+json", + Accept: "application/vnd.api+json", + }, + body: JSON.stringify(bodyData), + }); + // console.log("response", response); + const newTokens = await response.json(); + + if (!response.ok) { + // TODO: handle error + throw new Error(`HTTP error! status: ${response.status}`); + } + + return { + ...token, + accessToken: newTokens.data.attributes.access, + refreshToken: newTokens.data.attributes.refresh, + }; + } catch (error) { + // eslint-disable-next-line no-console + console.error("Error refreshing access token:", error); + return { + ...token, + error: "RefreshAccessTokenError", + }; + } +}; export const authConfig = { session: { @@ -57,10 +104,13 @@ export const authConfig = { }, jwt: async ({ token, user, account }) => { - // console.log(`In jwt callback - Token is ${JSON.stringify(token)}`); + if (token?.accessToken) { + const decodedToken = jwtDecode(token.accessToken); + console.log("decodedToken", decodedToken); + + token.accessTokenExpires = decodedToken?.exp * 1000; + } if (user && account) { - // console.log(`In jwt callback - User is ${JSON.stringify(user)}`); - // console.log(`In jwt callback - Account is ${JSON.stringify(account)}`); // token.data = user; return { ...token, @@ -69,11 +119,20 @@ export const authConfig = { user, }; } - return token; + + console.log( + "Access token expires", + token.accessTokenExpires, + new Date(Number(token.accessTokenExpires)), + ); + + if (Date.now() < token.accessTokenExpires) return token; + + // Access token is expired, we need to refresh it + return refreshAccessToken(token); }, session: async ({ session, token }) => { - console.log(`In session callback - Token is ${JSON.stringify(token)}`); // session.user = token.data as any; if (token) { session.accessToken = token.accessToken; diff --git a/types/components.ts b/types/components.ts index 28ffbe7355..9ad3507108 100644 --- a/types/components.ts +++ b/types/components.ts @@ -1,3 +1,4 @@ +import { JwtPayload } from "jwt-decode"; import { SVGProps } from "react"; export type IconSvgProps = SVGProps & { @@ -29,6 +30,11 @@ export type NextUIColors = export interface SearchParamsProps { [key: string]: string | string[] | undefined; } + +export interface CustomJwtPayload extends JwtPayload { + user_id: string; + tenant_id: string; +} export interface ProviderProps { id: string; type: "providers"; From 5c0ee0cfb38dfa0a85072d9aa2b2690d8c8a0981 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Thu, 3 Oct 2024 18:21:52 +0200 Subject: [PATCH 13/26] chore: remove dataProviders json file --- dataProviders.json | 105 --------------------------------------------- 1 file changed, 105 deletions(-) delete mode 100644 dataProviders.json diff --git a/dataProviders.json b/dataProviders.json deleted file mode 100644 index ab8a058620..0000000000 --- a/dataProviders.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "links": { - "first": "https://api.prowler.com/api/v1/providers?page%5Bnumber%5D=1", - "last": "https://api.prowler.com/api/v1/providers?page%5Bnumber%5D=1", - "next": null, - "prev": null - }, - "data": [ - { - "id": "5fd8f121-269e-4715-84cf-f92373f15dfa", - "type": "providers", - "attributes": { - "provider": "aws", - "provider_id": "1234567890", - "alias": "mock_aws_connected", - "status": "cancelled", - "resources": 101, - "connection": { - "connected": true, - "last_checked_at": "2024-07-17T09:55:14.191475Z" - }, - "scanner_args": { - "only_logs": true, - "excluded_checks": [ - "awslambda_function_no_secrets_in_code", - "cloudwatch_log_group_no_secrets_in_logs" - ], - "aws_retries_max_attempts": 5 - }, - "inserted_at": "2024-07-17T09:55:14.191475Z", - "updated_at": "2024-07-17T09:55:14.191475Z", - "created_by": { - "object": "user", - "id": "eea048ab-7cb3-47eb-9e5e-dce591ade41f" - } - } - }, - { - "id": "16aaeb4e-d3cd-4bb6-86f8-6c39cf93821e", - "type": "providers", - "attributes": { - "provider": "azure", - "provider_id": "1234567891", - "alias": "mock_aws_not_connected", - "status": "pending", - "resources": 222, - "connection": { - "connected": false, - "last_checked_at": "2024-07-17T09:55:18.987425Z" - }, - "scanner_args": { - "only_logs": true, - "excluded_checks": [ - "awslambda_function_no_secrets_in_code", - "cloudwatch_log_group_no_secrets_in_logs" - ], - "aws_retries_max_attempts": 5 - }, - "inserted_at": "2024-07-17T09:50:18.987425Z", - "updated_at": "2024-07-17T09:55:18.987425Z", - "created_by": { - "object": "user", - "id": "a8f5e964-5964-4aaf-9176-844e2c3b0716" - } - } - }, - { - "id": "63f16b03-7849-4054-b40b-300e331f46f0", - "type": "providers", - "attributes": { - "provider": "gcp", - "provider_id": "1234567895", - "alias": "mock_gcp", - "status": "completed", - "resources": 143, - "connection": { - "connected": true, - "last_checked_at": "2024-07-17T09:55:18.987425Z" - }, - "scanner_args": { - "only_logs": true, - "excluded_checks": [ - "apikeys_key_exists", - "cloudsql_instance_public_ip" - ], - "excluded_services": ["kms"] - }, - "inserted_at": "2024-07-17T09:50:18.987425Z", - "updated_at": "2024-07-17T09:55:18.987425Z", - "created_by": { - "object": "user", - "id": "eea048ab-7cb3-47eb-9e5e-dce591ade41f" - } - } - } - ], - "meta": { - "pagination": { - "page": 1, - "pages": 1, - "count": 3 - }, - "version": "v1" - } -} From 72d875aa4f1652b04543eae5f478be30e106587a Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Fri, 4 Oct 2024 16:08:57 +0200 Subject: [PATCH 14/26] chore: WIP --- components/auth/AuthButton.tsx | 21 -------------- .../auth/{AuthForm.tsx => auth-form.tsx} | 29 +++++++++++++++++-- components/auth/index.ts | 3 +- components/ui/custom/custom-button.tsx | 16 ++++++++-- tailwind.config.js | 1 + 5 files changed, 42 insertions(+), 28 deletions(-) delete mode 100644 components/auth/AuthButton.tsx rename components/auth/{AuthForm.tsx => auth-form.tsx} (89%) diff --git a/components/auth/AuthButton.tsx b/components/auth/AuthButton.tsx deleted file mode 100644 index 0eda81782a..0000000000 --- a/components/auth/AuthButton.tsx +++ /dev/null @@ -1,21 +0,0 @@ -"use client"; - -import { Button, CircularProgress } from "@nextui-org/react"; -import React from "react"; -import { useFormStatus } from "react-dom"; - -export const AuthButton = ({ type }: { type: string }) => { - const { pending } = useFormStatus(); - - return ( - - ); -}; diff --git a/components/auth/AuthForm.tsx b/components/auth/auth-form.tsx similarity index 89% rename from components/auth/AuthForm.tsx rename to components/auth/auth-form.tsx index 5b6b8402e7..18391d44d4 100644 --- a/components/auth/AuthForm.tsx +++ b/components/auth/auth-form.tsx @@ -20,8 +20,8 @@ import { authFormSchema } from "@/types"; import { NotificationIcon, ProwlerExtended } from "../icons"; import { ThemeSwitch } from "../ThemeSwitch"; -import { CustomInput } from "../ui/custom"; -import { AuthButton } from "./AuthButton"; +import { CustomButton, CustomInput } from "../ui/custom"; +// import { AuthButton } from "./AuthButton"; export const AuthForm = ({ type }: { type: string }) => { const formSchema = authFormSchema(type); @@ -39,8 +39,12 @@ export const AuthForm = ({ type }: { type: string }) => { }, }); + const isLoading = form.formState.isSubmitting; + const [state, dispatch] = useFormState(authenticate, undefined); + console.log(isLoading, state); + useEffect(() => { if (state?.message === "Success") { router.push("/"); @@ -185,7 +189,26 @@ export const AuthForm = ({ type }: { type: string }) => { )} - + {isLoading &&

Loading...

} + + + {isLoading ? ( + Loading + ) : ( + {type === "sign-in" ? "Log In" : "Sign Up"} + )} + diff --git a/components/auth/index.ts b/components/auth/index.ts index 26161c473c..82fc6feefa 100644 --- a/components/auth/index.ts +++ b/components/auth/index.ts @@ -1,2 +1 @@ -export * from "./AuthButton"; -export * from "./AuthForm"; +export * from "./auth-form"; diff --git a/components/ui/custom/custom-button.tsx b/components/ui/custom/custom-button.tsx index 32287a76c6..ca266275b2 100644 --- a/components/ui/custom/custom-button.tsx +++ b/components/ui/custom/custom-button.tsx @@ -8,7 +8,7 @@ export const buttonClasses = { base: "px-4 inline-flex items-center justify-center relative z-0 text-center whitespace-nowrap", primary: "bg-default-100 hover:bg-default-200 text-default-800", secondary: "bg-prowler-grey-light dark:bg-prowler-grey-medium text-white", - action: "text-white bg-prowler-blue-smoky dark:bg-prowler-grey-medium", + action: "text-white bg-prowler-grey-medium dark:bg-prowler-grey-medium", dashed: "border border-default border-dashed bg-transparent justify-center whitespace-nowrap font-medium shadow-sm hover:border-solid hover:bg-default-100 active:bg-default-200 active:border-solid", transparent: "border-0 border-transparent bg-transparent", @@ -19,6 +19,7 @@ export const buttonClasses = { interface ButtonProps { type?: "button" | "submit" | "reset"; ariaLabel: string; + ariaDisabled?: boolean; className?: string; variant?: | "solid" @@ -52,6 +53,7 @@ interface ButtonProps { export const CustomButton = ({ type = "button", ariaLabel, + ariaDisabled, className, variant = "solid", color = "primary", @@ -69,6 +71,7 @@ export const CustomButton = ({ + + + + )} + {type === "sign-in" ? ( +

+ Need to create an account?  + Sign Up +

+ ) : ( +

+ Already have an account?  + Log In +

+ )} - {/* Testimonial */} -
-

- - Open Cloud Security - -

-
- -
-

- {type === "sign-in" ? "Sign In" : "Sign Up"} -

- -
- - {type === "sign-up" && ( - <> - - - - )} - - - - - {type === "sign-in" && ( -
- - Remember me - - - Forgot password? - -
- )} - {type === "sign-up" && ( - <> - - ( - <> - - field.onChange(e.target.checked)} - > - I agree with the  - - Terms - -   and  - - Privacy Policy - - - - - - )} - /> - - )} - - {state?.message === "Credentials error" && ( -
- -

No user found

-
- )} - - - {isLoading ? ( - Loading - ) : ( - {type === "sign-in" ? "Log In" : "Sign Up"} - )} - - - - - {type === "sign-in" && ( - <> -
- -

OR

- -
-
- - -
- - )} - {type === "sign-in" ? ( -

- Need to create an account?  - Sign Up +

+
+

+ Open Source Security Platform

- ) : ( -

- Already have an account?  - Log In -

- )} +
); From 22bacfdcb3e419b65052901603fd9bab0b6f775f Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Sun, 6 Oct 2024 13:23:59 +0200 Subject: [PATCH 22/26] feat(sign-up/sign-in): remove unused component --- components/auth/auth-form.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/auth/auth-form.tsx b/components/auth/auth-form.tsx index 728afb930a..fe4e62f9d1 100644 --- a/components/auth/auth-form.tsx +++ b/components/auth/auth-form.tsx @@ -2,7 +2,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { Icon } from "@iconify/react"; -import { Button, Checkbox, Divider, Link, User } from "@nextui-org/react"; +import { Button, Checkbox, Divider, Link } from "@nextui-org/react"; import { useRouter } from "next/navigation"; import { useEffect } from "react"; import { useFormState } from "react-dom"; From b8b05b923fcdf589fe1ec0035cae458449719628 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Mon, 7 Oct 2024 06:41:13 +0200 Subject: [PATCH 23/26] chore: tweak styles for Prowler logo in signIn page --- components/auth/auth-form.tsx | 8 +++-- components/icons/prowler/ProwlerIcons.tsx | 41 ++++++++++++----------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/components/auth/auth-form.tsx b/components/auth/auth-form.tsx index fe4e62f9d1..038a34dd6d 100644 --- a/components/auth/auth-form.tsx +++ b/components/auth/auth-form.tsx @@ -63,6 +63,10 @@ export const AuthForm = ({ type }: { type: string }) => { if (type === "sign-up") { const newUser = await createNewUser(data); + if (!newUser.errors) { + router.push("/sign-in"); + } + if (newUser?.errors && newUser.errors.length > 0) { newUser.errors.forEach((error: ApiError) => { const errorMessage = error.detail; @@ -109,8 +113,8 @@ export const AuthForm = ({ type }: { type: string }) => { {/* Auth Form */}
{/* Prowler Logo */} -
- +
+
diff --git a/components/icons/prowler/ProwlerIcons.tsx b/components/icons/prowler/ProwlerIcons.tsx index 0229c6e0b5..425788e88b 100644 --- a/components/icons/prowler/ProwlerIcons.tsx +++ b/components/icons/prowler/ProwlerIcons.tsx @@ -7,26 +7,27 @@ export const ProwlerExtended: React.FC = ({ width = 164, height, ...props -}) => ( - - - -); +}) => { + return ( + + + + ); +}; export const ProwlerShort: React.FC = ({ size, From f5e53e814b45b090b9b3c37776162ac82194d763 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Mon, 7 Oct 2024 07:07:26 +0200 Subject: [PATCH 24/26] chore: tweak styles auth pages --- app/(auth)/sign-in/page.tsx | 2 +- app/(auth)/sign-up/page.tsx | 2 +- components/auth/{ => oss}/auth-form.tsx | 21 +++++++++++---------- components/auth/{ => oss}/index.ts | 0 4 files changed, 13 insertions(+), 12 deletions(-) rename components/auth/{ => oss}/auth-form.tsx (93%) rename components/auth/{ => oss}/index.ts (100%) diff --git a/app/(auth)/sign-in/page.tsx b/app/(auth)/sign-in/page.tsx index 0f8ebffd55..b5bd978029 100644 --- a/app/(auth)/sign-in/page.tsx +++ b/app/(auth)/sign-in/page.tsx @@ -1,4 +1,4 @@ -import { AuthForm } from "@/components/auth"; +import { AuthForm } from "@/components/auth/oss"; const SignIn = () => { return ; diff --git a/app/(auth)/sign-up/page.tsx b/app/(auth)/sign-up/page.tsx index 8f7954ba34..8e525507b8 100644 --- a/app/(auth)/sign-up/page.tsx +++ b/app/(auth)/sign-up/page.tsx @@ -1,4 +1,4 @@ -import { AuthForm } from "@/components/auth"; +import { AuthForm } from "@/components/auth/oss"; const SignUp = () => { return ; diff --git a/components/auth/auth-form.tsx b/components/auth/oss/auth-form.tsx similarity index 93% rename from components/auth/auth-form.tsx rename to components/auth/oss/auth-form.tsx index 038a34dd6d..1807fcce19 100644 --- a/components/auth/auth-form.tsx +++ b/components/auth/oss/auth-form.tsx @@ -4,12 +4,16 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { Icon } from "@iconify/react"; import { Button, Checkbox, Divider, Link } from "@nextui-org/react"; import { useRouter } from "next/navigation"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useFormState } from "react-dom"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { authenticate, createNewUser } from "@/actions/auth"; +import { NotificationIcon, ProwlerExtended } from "@/components/icons"; +import { ThemeSwitch } from "@/components/ThemeSwitch"; +import { useToast } from "@/components/ui"; +import { CustomButton, CustomInput } from "@/components/ui/custom"; import { Form, FormControl, @@ -18,11 +22,6 @@ import { } from "@/components/ui/form"; import { ApiError, authFormSchema } from "@/types"; -import { NotificationIcon, ProwlerExtended } from "../icons"; -import { ThemeSwitch } from "../ThemeSwitch"; -import { CustomButton, CustomInput } from "../ui/custom"; -import { useToast } from "../ui/toast"; - export const AuthForm = ({ type }: { type: string }) => { const formSchema = authFormSchema(type); const router = useRouter(); @@ -42,11 +41,9 @@ export const AuthForm = ({ type }: { type: string }) => { }); const [state, dispatch] = useFormState(authenticate, undefined); - + const [isLoading, setIsLoading] = useState(false); const { toast } = useToast(); - const isLoading = form.formState.isSubmitting; - useEffect(() => { if (state?.message === "Success") { router.push("/"); @@ -55,13 +52,17 @@ export const AuthForm = ({ type }: { type: string }) => { const onSubmit = async (data: z.infer) => { if (type === "sign-in") { + setIsLoading(true); dispatch({ email: data.email.toLowerCase(), password: data.password, }); + setIsLoading(false); } if (type === "sign-up") { + setIsLoading(true); const newUser = await createNewUser(data); + setIsLoading(false); if (!newUser.errors) { router.push("/sign-in"); @@ -291,7 +292,7 @@ export const AuthForm = ({ type }: { type: string }) => { className="relative hidden w-1/2 flex-col-reverse rounded-medium p-10 shadow-small lg:flex" style={{ backgroundImage: - "url(https://nextuipro.nyc3.cdn.digitaloceanspaces.com/components-images/white-building.jpg)", + "url(https://media.licdn.com/dms/image/v2/D5622AQFnGdly6BE-Qw/feedshare-shrink_1280/feedshare-shrink_1280/0/1725548764361?e=1730937600&v=beta&t=A2VLwDFbjWqOgzCtsF58GkasH-eUC9uqP9rY2UI9B9A)", backgroundSize: "cover", backgroundPosition: "center", }} diff --git a/components/auth/index.ts b/components/auth/oss/index.ts similarity index 100% rename from components/auth/index.ts rename to components/auth/oss/index.ts From 6b7fe81cf84c3e0cb561609e695c05eafe5f6873 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Mon, 7 Oct 2024 17:30:31 +0200 Subject: [PATCH 25/26] chore: tweak styles auth pages --- actions/providers/providers.ts | 11 +++++++---- components/auth/oss/auth-form.tsx | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/actions/providers/providers.ts b/actions/providers/providers.ts index 8c67196f92..200bc79bd6 100644 --- a/actions/providers/providers.ts +++ b/actions/providers/providers.ts @@ -48,7 +48,7 @@ export const getProviders = async ({ }; export const getProvider = async (formData: FormData) => { - // const session = await auth(); + const session = await auth(); const providerId = formData.get("id"); const keyServer = process.env.API_BASE_URL; @@ -58,6 +58,7 @@ export const getProvider = async (formData: FormData) => { const providers = await fetch(url.toString(), { headers: { Accept: "application/vnd.api+json", + Authorization: `Bearer ${session?.accessToken}`, }, }); const data = await providers.json(); @@ -71,7 +72,7 @@ export const getProvider = async (formData: FormData) => { }; export const updateProvider = async (formData: FormData) => { - // const session = await auth(); + const session = await auth(); const keyServer = process.env.API_BASE_URL; const providerId = formData.get("providerId"); @@ -85,6 +86,7 @@ export const updateProvider = async (formData: FormData) => { headers: { "Content-Type": "application/vnd.api+json", Accept: "application/vnd.api+json", + Authorization: `Bearer ${session?.accessToken}`, }, body: JSON.stringify({ data: { @@ -173,7 +175,7 @@ export const checkConnectionProvider = async (formData: FormData) => { }; export const deleteProvider = async (formData: FormData) => { - // const session = await auth(); + const session = await auth(); const keyServer = process.env.API_BASE_URL; const providerId = formData.get("id"); @@ -184,6 +186,7 @@ export const deleteProvider = async (formData: FormData) => { method: "DELETE", headers: { Accept: "application/vnd.api+json", + Authorization: `Bearer ${session?.accessToken}`, }, }); const data = await response.json(); @@ -196,7 +199,7 @@ export const deleteProvider = async (formData: FormData) => { } }; -export const getErrorMessage = (error: unknown): string => { +export const getErrorMessage = async (error: unknown): Promise => { let message: string; if (error instanceof Error) { diff --git a/components/auth/oss/auth-form.tsx b/components/auth/oss/auth-form.tsx index 1807fcce19..3b70a5a040 100644 --- a/components/auth/oss/auth-form.tsx +++ b/components/auth/oss/auth-form.tsx @@ -292,14 +292,14 @@ export const AuthForm = ({ type }: { type: string }) => { className="relative hidden w-1/2 flex-col-reverse rounded-medium p-10 shadow-small lg:flex" style={{ backgroundImage: - "url(https://media.licdn.com/dms/image/v2/D5622AQFnGdly6BE-Qw/feedshare-shrink_1280/feedshare-shrink_1280/0/1725548764361?e=1730937600&v=beta&t=A2VLwDFbjWqOgzCtsF58GkasH-eUC9uqP9rY2UI9B9A)", + "url(https://nextuipro.nyc3.cdn.digitaloceanspaces.com/components-images/white-building.jpg)", backgroundSize: "cover", backgroundPosition: "center", }} >

- Open Source Security Platform + Open Cloud Security Platform

From 7572136cc8a2cbdba107d8f526a4213889e85c46 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Tue, 8 Oct 2024 08:46:35 +0200 Subject: [PATCH 26/26] feat: sign-up and sign-in pages are styled and ready to be merged --- app/(prowler)/profile/page.tsx | 2 +- app/(prowler)/providers/page.tsx | 2 +- auth.config.ts | 12 ++++----- components/auth/oss/auth-form.tsx | 30 ++++++---------------- components/filters/data-filters.ts | 5 ---- tailwind.config.js | 40 ++++++++++++++---------------- 6 files changed, 35 insertions(+), 56 deletions(-) diff --git a/app/(prowler)/profile/page.tsx b/app/(prowler)/profile/page.tsx index 91fa4907f0..0cfb4f8ab2 100644 --- a/app/(prowler)/profile/page.tsx +++ b/app/(prowler)/profile/page.tsx @@ -15,7 +15,7 @@ export default async function Profile() { } // const user = await getUserByMe(); - // console.log("user", user); + return ( <>
diff --git a/app/(prowler)/providers/page.tsx b/app/(prowler)/providers/page.tsx index a5cb092664..b999671859 100644 --- a/app/(prowler)/providers/page.tsx +++ b/app/(prowler)/providers/page.tsx @@ -24,7 +24,7 @@ export default async function Providers({
- + diff --git a/auth.config.ts b/auth.config.ts index 556c7d40c0..4639f8bf37 100644 --- a/auth.config.ts +++ b/auth.config.ts @@ -124,7 +124,7 @@ export const authConfig = { token.accessToken as string, ) as CustomJwtPayload; // eslint-disable-next-line no-console - console.log("decodedToken", decodedToken); + // console.log("decodedToken", decodedToken); token.accessTokenExpires = (decodedToken.exp as number) * 1000; token.user_id = decodedToken.user_id; token.tenant_id = decodedToken.tenant_id; @@ -150,11 +150,11 @@ export const authConfig = { } // eslint-disable-next-line no-console - console.log( - "Access token expires", - token.accessTokenExpires, - new Date(Number(token.accessTokenExpires)), - ); + // console.log( + // "Access token expires", + // token.accessTokenExpires, + // new Date(Number(token.accessTokenExpires)), + // ); // If the access token is not expired, return the token if ( diff --git a/components/auth/oss/auth-form.tsx b/components/auth/oss/auth-form.tsx index 3b70a5a040..451e043c0b 100644 --- a/components/auth/oss/auth-form.tsx +++ b/components/auth/oss/auth-form.tsx @@ -112,13 +112,15 @@ export const AuthForm = ({ type }: { type: string }) => { return (
{/* Auth Form */} -
- {/* Prowler Logo */} -
- -
+
+ {/* Background Pattern */} +
-
+
+ {/* Prowler Logo */} +
+ +

{type === "sign-in" ? "Sign In" : "Sign Up"} @@ -287,22 +289,6 @@ export const AuthForm = ({ type }: { type: string }) => { )}

- -
-
-

- Open Cloud Security Platform -

-
-
); }; diff --git a/components/filters/data-filters.ts b/components/filters/data-filters.ts index 9aea8e9dd3..df1e01cea0 100644 --- a/components/filters/data-filters.ts +++ b/components/filters/data-filters.ts @@ -1,9 +1,4 @@ export const filtersProviders = [ - { - key: "provider__in", - labelCheckboxGroup: "Select a Provider", - values: ["aws", "gcp", "azure", "kubernetes"], - }, { key: "connected", labelCheckboxGroup: "Connection", diff --git a/tailwind.config.js b/tailwind.config.js index 608551bb96..31bdbfd39e 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -20,36 +20,34 @@ module.exports = { extend: { colors: { prowler: { - blue: { + theme: { midnight: "#030921", pale: "#f3fcff", - smoky: "#7b8390", - }, - grey: { - medium: "#353a4d", - light: "#868994", - }, - green: { - DEFAULT: "#9FD655", - medium: "#09BF3D", - }, - theme: { green: "#6af400", purple: "#5001d0", coral: "#ff5356", orange: "#f69000", yellow: "#ffdf16", }, - dark: { - DEFAULT: "#0E1117", - 700: "#151B23", - 400: "#262C36", - title: "#E2E8F0", - text: "#94a3b8" /* primary default for dark mode */, + blue: { + 800: "#1e293bff", }, - light: { - title: "#1e293bff", - text: "#64748b" /* primary default for light mode */, + grey: { + medium: "#353a4d", + light: "#868994", + 600: "#64748b", + }, + green: { + DEFAULT: "#9FD655", + medium: "#09BF3D", + }, + black: { + DEFAULT: "#000", + 900: "#18181A", + }, + white: { + DEFAULT: "#FFF", + 900: "#18181A", }, }, system: {