feat: Certificate request form #7
7 changed files with 196 additions and 0 deletions
|
@ -1,5 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<v-app>
|
<v-app>
|
||||||
|
<v-app-bar>
|
||||||
|
<v-app-bar-nav-icon icon="mdi-home" to="/"/>
|
||||||
|
</v-app-bar>
|
||||||
<v-main>
|
<v-main>
|
||||||
<router-view />
|
<router-view />
|
||||||
</v-main>
|
</v-main>
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
rounded="lg"
|
rounded="lg"
|
||||||
title="Manage your certificates"
|
title="Manage your certificates"
|
||||||
variant="text"
|
variant="text"
|
||||||
|
to="/cert-request"
|
||||||
|
|||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
|
|
162
frontend/src/pages/cert-request.vue
Normal file
162
frontend/src/pages/cert-request.vue
Normal 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
25
frontend/src/types/certificate.d.ts
vendored
Normal 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
3
frontend/src/types/util.d.ts
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export type Validated<T> = {
|
||||||
|
[K in keyof T as `${K}Rules`]: ((val: string) => true | string)[]
|
||||||
|
} & T;
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"files": [],
|
"files": [],
|
||||||
|
"include": ["./typed-router.d.ts"],
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
"path": "./tsconfig.node.json"
|
"path": "./tsconfig.node.json"
|
||||||
|
|
1
frontend/typed-router.d.ts
vendored
1
frontend/typed-router.d.ts
vendored
|
@ -19,5 +19,6 @@ declare module 'vue-router/auto-routes' {
|
||||||
*/
|
*/
|
||||||
export interface RouteNamedMap {
|
export interface RouteNamedMap {
|
||||||
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
|
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
|
||||||
|
'/cert-request': RouteRecordInfo<'/cert-request', '/cert-request', Record<never, never>, Record<never, never>>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue
????