Skip to content

Commit

Permalink
login functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
kayra1 committed Jul 16, 2024
1 parent b3ddceb commit 3f529b6
Show file tree
Hide file tree
Showing 9 changed files with 277 additions and 89 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
83 changes: 83 additions & 0 deletions ui/src/app/aside.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { SetStateAction, Dispatch, useState, createContext, ChangeEvent } from "react"
import { useMutation, useQueryClient } from "react-query";
import { postCSR } from "./queries";
import { extractCSR } from "./utils";

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 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(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}
</>
)
}
2 changes: 1 addition & 1 deletion ui/src/app/certificate_requests/table.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useContext, useState, Dispatch, SetStateAction } from "react"
import { AsideContext } from "../nav"
import { AsideContext } from "../aside"
import Row from "./row"
import { CSREntry } from "../types"

Expand Down
6 changes: 6 additions & 0 deletions ui/src/app/globals.scss
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,10 @@

.certificate-info p {
margin: 4px 0;
}

.sidenav-bottom-ul {
bottom: $spv--x-large;
position: absolute;
width: 15rem;
}
30 changes: 30 additions & 0 deletions ui/src/app/login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { jwtDecode } from "jwt-decode";
import { useState } from "react";
import { useCookies } from "react-cookie"

type UserObject = {
exp: number
id: number
permissions: number
username: string
}

export function Login() {
const [cookies, setCookie, removeCookie] = useCookies(['user_token']);
const [user, setUser] = useState<string>("")
const getUserObject = (token: string) => {
if (token) {
const tokenObject: UserObject = jwtDecode(cookies.user_token)
return tokenObject
}
return null
}
const userObj = getUserObject(cookies.user_token)

return (
<>
{
cookies.user_token ? <p>{userObj?.username}</p> : <a className="p-button u-float-right" style={{ marginRight: "5px" }} href="login">Login</a>
}
</>)
}
55 changes: 55 additions & 0 deletions ui/src/app/login/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"use client"

import { useMutation } from "react-query"
import { login } from "../queries"
import { useState, ChangeEvent } from "react"
import { useCookies } from "react-cookie"

export default function LoginPage() {
const [cookies, setCookie, removeCookie] = useCookies(['user_token']);
const mutation = useMutation(login, {
onSuccess: (e) => {
setErrorText("")
setCookie('user_token', e, {
sameSite: true,
secure: true,
expires: new Date(new Date().getTime() + 60 * 60 * 1000),
})
},
onError: (e: Error) => {
setErrorText(e.message)
}
})

const [username, setUsername] = useState<string>("")
const [password, setPassword] = useState<string>("")
const [errorText, setErrorText] = useState<string>("")
const handleUsernameChange = (event: ChangeEvent<HTMLInputElement>) => { setUsername(event.target.value) }
const handlePasswordChange = (event: ChangeEvent<HTMLInputElement>) => { setPassword(event.target.value) }
return (
<div className="p-panel">
<div className="p-panel__header is-sticky">
<h4 className="p-panel__title">Login</h4>
</div>
<div className="p-panel__content">
<div className="u-fixed-width">
<form>
<label htmlFor="InputUsername">User Name</label>
<input type="text" id="InputUsername" name="InputUsername" onChange={handleUsernameChange} />
<label htmlFor="InputPassword">Password</label>
<input type="password" id="InputPassword" name="InputPassword" placeholder="******" autoComplete="current-password" onChange={handlePasswordChange} />
<button type="submit" name="submit" onClick={(event) => { event.preventDefault(); mutation.mutate({ username: username, password: password }) }}>Submit</button>
{errorText &&
<div className="p-notification--negative">
<div className="p-notification__content">
<h5 className="p-notification__title">Error Logging In</h5>
<p className="p-notification__message">{errorText.split("error: ")}</p>
</div>
</div>
}
</form>
</div>
</div>
</div >
)
}
Loading

0 comments on commit 3f529b6

Please sign in to comment.