feat: Certificate request form (#7)

* wip: first draft cert request

* feat: form for cert request

* fix: add missing fields

* add app bar

* Give alt names list more space

* add missing rules
This commit is contained in:
CybAtax 2024-11-17 22:48:38 +01:00 committed by GitHub
parent d98f60ab54
commit f870fc1ac6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 196 additions and 0 deletions

View file

@ -1,5 +1,8 @@
<template>
<v-app>
<v-app-bar>
<v-app-bar-nav-icon icon="mdi-home" to="/"/>
</v-app-bar>
<v-main>
<router-view />
</v-main>

View file

@ -39,6 +39,7 @@
rounded="lg"
title="Manage your certificates"
variant="text"
to="/cert-request"
/>
</v-col>

View file

@ -0,0 +1,162 @@
<template>
<v-card class="ma-5">
<v-card-title>Request a certificate</v-card-title>
<v-card-item>
<v-form v-model="valid">
<v-container>
<v-row>
<v-col cols="12" md="4">
<v-text-field
v-model="subject.country"
:rules="subject.countryRules"
label="Country"
required
/>
</v-col>
<v-col cols="12" md="4">
<v-text-field
v-model="subject.state"
:counter="10"
:rules="subject.stateRules"
label="State"
required
/>
</v-col>
<v-col cols="12" md="4">
<v-text-field
v-model="subject.city"
:rules="subject.cityRules"
label="City"
required
/>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="4">
<v-text-field
v-model="subject.organization"
:rules="subject.organizationRules"
label="Organization"
required
/>
</v-col>
<v-col cols="12" md="4">
<v-text-field
v-model="subject.orgUnit"
:rules="subject.orgUnitRules"
label="Organization Unit"
required
/>
</v-col>
<v-col cols="12" md="4">
<v-text-field
v-model="subject.commonName"
:counter="10"
:rules="subject.commonNameRules"
label="Common name"
required
/>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="4">
<v-text-field type="number" v-model="validity.duration" :rules="validity.durationRules" label="Duration for validity in Days"/>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="4">
<v-text-field label="Alternative Names" v-model="ui.altName">
<template v-slot:append>
<v-btn icon="mdi-plus" color="green" :disabled="!ui.altName" @click="addAltName"/>
</template>
</v-text-field>
</v-col>
<v-col cols="12" md="8">
<v-combobox
v-model="subject.altNames"
:items="subject.altNames"
label="Alternative Names"
multiple
>
<template v-slot:selection="data">
<v-chip
:key="JSON.stringify(data.item)"
v-bind="data"
size="small"
>
{{ data.item.title }}
</v-chip>
</template>
</v-combobox>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="12">
<v-textarea v-model="trustingAuthority" label="Trusting Authority"/>
</v-col>
</v-row>
</v-container>
</v-form>
</v-card-item>
<v-card-actions>
<v-col class="text-right">
<v-btn :disabled="!valid" @click="requestCertificate" text="Request certificate" prepend-icon="mdi-certificate"/>
</v-col>
</v-card-actions>
</v-card>
</template>
<script lang="ts">
import type { Validated } from "@/types/util";
import type { Certificate, Subject, Validity } from "@/types/certificate";
const requiredValidation = (fieldName: string) => {
return (val: string) => {
if (val) return true;
return fieldName + " is required"
}
}
type SubjectInput = Partial<Validated<Subject>> & Required<Subject>;
type ValidityInput = Validated<{duration: number}>;//Partial<Valid<Validity>> & Required<Validity>;
type UiInput = {
altName: string;
}
export default {
data: (): {valid: boolean, subject: SubjectInput; validity: ValidityInput; trustingAuthority: string; ui: UiInput} => ({
valid: false,
subject: {
country: '',
countryRules: [requiredValidation("Country")],
state: '',
city: '',
organization: '',
orgUnit: '',
commonName: '',
commonNameRules: [requiredValidation("Common Name")],
altNames: [],
},
validity: {
duration: undefined as unknown as number,
durationRules: [
requiredValidation("Validity duration"),
]
},
trustingAuthority: '',
ui: {
altName: '',
},
}),
methods: {
addAltName() {
this.subject.altNames.push(this.ui.altName)
this.ui.altName = ''
},
requestCertificate(): Certificate {
throw new Error("Not supported yet");
},
}
}
</script>

25
frontend/src/types/certificate.d.ts vendored Normal file
View file

@ -0,0 +1,25 @@
export interface Subject {
commonName: string;
organization: string;
orgUnit: string;
country: string;
state: string;
city: string
altNames: string[];
}
export interface Validity {
from: Date;
to: Date;
}
export interface Extensions {
}
export interface Certificate {
issuer: Subject;
validity: Validity;
subject: Subject;
extensions: Extensions;
}

3
frontend/src/types/util.d.ts vendored Normal file
View file

@ -0,0 +1,3 @@
export type Validated<T> = {
[K in keyof T as `${K}Rules`]: ((val: string) => true | string)[]
} & T;

View file

@ -1,5 +1,6 @@
{
"files": [],
"include": ["./typed-router.d.ts"],
"references": [
{
"path": "./tsconfig.node.json"

View file

@ -19,5 +19,6 @@ declare module 'vue-router/auto-routes' {
*/
export interface RouteNamedMap {
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
'/cert-request': RouteRecordInfo<'/cert-request', '/cert-request', Record<never, never>, Record<never, never>>,
}
}