Skip to content

Commit

Permalink
feat: logging in to GoCert (#44)
Browse files Browse the repository at this point in the history
Co-authored-by: Guillaume Belanger <[email protected]>
  • Loading branch information
kayra1 and gruyaume authored Jul 24, 2024
1 parent 38959c3 commit c755064
Show file tree
Hide file tree
Showing 17 changed files with 466 additions and 146 deletions.
82 changes: 77 additions & 5 deletions ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
"test-live": "vitest"
},
"dependencies": {
"jwt-decode": "^4.0.0",
"next": "14.2.3",
"pkijs": "^3.1.0",
"react": "^18",
"react-cookie": "^7.1.4",
"react-dom": "^18",
"react-query": "^3.39.3",
"sass": "^1.77.4",
Expand Down
85 changes: 85 additions & 0 deletions ui/src/app/aside.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { SetStateAction, Dispatch, useState, createContext, ChangeEvent } from "react"
import { useMutation, useQueryClient } from "react-query";
import { postCSR } from "./queries";
import { extractCSR } from "./utils";
import { useCookies } from "react-cookie";

type AsideContextType = {
isOpen: boolean,
setIsOpen: Dispatch<SetStateAction<boolean>>
}
export const AsideContext = createContext<AsideContextType>({ isOpen: false, setIsOpen: () => { } });

export function Aside({ isOpen, setIsOpen }: { isOpen: boolean, setIsOpen: Dispatch<SetStateAction<boolean>> }) {
const [cookies, setCookie, removeCookie] = useCookies(['user_token']);
const queryClient = useQueryClient()
const mutation = useMutation(postCSR, {
onSuccess: () => {
queryClient.invalidateQueries('csrs')
},
})
const [CSRPEMString, setCSRPEMString] = useState<string>("")
const handleTextChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
setCSRPEMString(event.target.value);
}
const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0]
if (file) {
const reader = new FileReader();
reader.onload = (e: ProgressEvent<FileReader>) => {
if (e.target) {
if (e.target.result) {
setCSRPEMString(e.target.result.toString());
}
}
};
reader.readAsText(file);
}
};
return (
<aside className={"l-aside" + (isOpen ? "" : " is-collapsed")} id="aside-panel" aria-label="aside-panel" >
<div className="p-panel">
<div className="p-panel__header">
<h4 className="p-panel__title">Add a New Certificate Request</h4>
<div className="p-panel__controls">
<button onClick={() => setIsOpen(false)} className="p-button--base u-no-margin--bottom has-icon"><i className="p-icon--close"></i></button>
</div>
</div>
<div className="p-panel__content">
<form className="p-form p-form--stacked">
<div className="p-form__group row">
<label htmlFor="textarea">
Enter or upload the CSR in PEM format below
</label>
<textarea id="csr-textarea" name="textarea" rows={10} placeholder="-----BEGIN CERTIFICATE REQUEST-----" onChange={handleTextChange} value={CSRPEMString} />
</div>
<div className="p-form__group row">
<input type="file" name="upload" accept=".pem,.csr" onChange={handleFileChange}></input>
</div>
<div className="p-form__group row">
<SubmitCSR csrText={CSRPEMString} onClickFunc={() => mutation.mutate({ authToken: cookies.user_token, csr: CSRPEMString })} />
</div>
</form>
</div>
</div >
</aside >
)
}

function SubmitCSR({ csrText, onClickFunc }: { csrText: string, onClickFunc: any }) {
let csrIsValid = false
try {
extractCSR(csrText.trim())
csrIsValid = true
}
catch { }

const validationComponent = csrText == "" ? <></> : csrIsValid ? <div><i className="p-icon--success"></i>Valid CSR</div> : <div><i className="p-icon--error"></i>Invalid CSR</div>
const buttonComponent = csrIsValid ? <button className="p-button--positive u-float-right" name="submit" onClick={onClickFunc} >Submit</button> : <button className="p-button--positive u-float-right" name="submit" disabled={true} onClick={onClickFunc} >Submit</button>
return (
<>
{validationComponent}
{buttonComponent}
</>
)
}
38 changes: 38 additions & 0 deletions ui/src/app/auth/authContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"use client"

import { createContext, useContext, useState, useEffect } from 'react';
import { User } from '../types';
import { useCookies } from 'react-cookie';
import { jwtDecode } from 'jwt-decode';
import { useRouter } from 'next/navigation';

type AuthContextType = {
user: User | null
}

const AuthContext = createContext<AuthContextType>({ user: null });

export const AuthProvider = ({ children }: Readonly<{ children: React.ReactNode }>) => {
const [cookies, setCookie, removeCookie] = useCookies(['user_token']);
const [user, setUser] = useState<User | null>(null);
const router = useRouter();

useEffect(() => {
const token = cookies.user_token;
if (token) {
let userObject = jwtDecode(cookies.user_token) as User
setUser(userObject);
} else {
setUser(null)
router.push('/login');
}
}, [cookies.user_token, router]);

return (
<AuthContext.Provider value={{ user }}>
{children}
</AuthContext.Provider>
);
};

export const useAuth = () => useContext(AuthContext);
8 changes: 5 additions & 3 deletions ui/src/app/certificate_requests/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useMutation, useQueryClient } from "react-query"
import { ConfirmationModalData } from "./row"
import { extractCert, csrMatchesCertificate } from "../utils"
import { postCertToID } from "../queries"
import { useCookies } from "react-cookie"

interface ConfirmationModalProps {
modalData: ConfirmationModalData
Expand All @@ -12,7 +13,7 @@ interface ConfirmationModalProps {

export function ConfirmationModal({ modalData, setModalData }: ConfirmationModalProps) {
const confirmQuery = () => {
modalData?.func()
modalData?.onMouseDownFunc()
setModalData(null)
}
return (
Expand Down Expand Up @@ -68,8 +69,9 @@ interface SubmitCertificateModalProps {
setFormOpen: Dispatch<SetStateAction<boolean>>
}
export function SubmitCertificateModal({ id, csr, cert, setFormOpen }: SubmitCertificateModalProps) {
const [cookies, setCookie, removeCookie] = useCookies(['user_token']);
const queryClient = useQueryClient()
const mutation = useMutation(postCertToID(id), {
const mutation = useMutation(postCertToID, {
onSuccess: () => {
queryClient.invalidateQueries('csrs')
},
Expand All @@ -93,7 +95,7 @@ export function SubmitCertificateModal({ id, csr, cert, setFormOpen }: SubmitCer
}
};
const handleSubmit = () => {
mutation.mutate(certificatePEMString)
mutation.mutate({ id: id, authToken: cookies.user_token, cert: certificatePEMString })
setFormOpen(false)
}
return (
Expand Down
25 changes: 23 additions & 2 deletions ui/src/app/certificate_requests/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { useQuery } from "react-query"
import { CertificateRequestsTable } from "./table"
import { getCertificateRequests } from "../queries"
import { CSREntry } from "../types"
import { useCookies } from "react-cookie"
import { useRouter } from "next/navigation"

function Error({ msg }: { msg: string }) {
return (
Expand Down Expand Up @@ -35,9 +37,28 @@ function Loading() {
}

export default function CertificateRequests() {
const query = useQuery<CSREntry[], Error>('csrs', getCertificateRequests)
const router = useRouter()
const [cookies, setCookie, removeCookie] = useCookies(['user_token']);
if (!cookies.user_token) {
router.push("/login")
}
const query = useQuery<CSREntry[], Error>({
queryKey: ['csrs', cookies.user_token],
queryFn: () => getCertificateRequests({ authToken: cookies.user_token }),
retry: (failureCount, error): boolean => {
if (error.message.includes("401")) {
return false
}
return true
},
})
if (query.status == "loading") { return <Loading /> }
if (query.status == "error") { return <Error msg={query.error.message} /> }
if (query.status == "error") {
if (query.error.message.includes("401")) {
removeCookie("user_token")
}
return <Error msg={query.error.message} />
}
const csrs = Array.from(query.data ? query.data : [])
return (
<CertificateRequestsTable csrs={csrs} />
Expand Down
Loading

0 comments on commit c755064

Please sign in to comment.