frontend/src/components/RegistryWizard/steps/RegistrySupplierAuth.vue (235 lines of code) (raw):
<script lang="ts" setup>
import Typography from '@/components/common/Typography.vue';
import TextField from '@/components/common/TextField.vue';
import ToggleSwitch from '@/components/common/ToggleSwitch.vue';
import { ref } from 'vue';
import { useForm, useField } from 'vee-validate';
import * as Yup from 'yup';
import { OfficerAuthType, type CitizenAuthFlow, PORTALS } from '@/types/registry';
interface FormValues {
authType: OfficerAuthType,
url: string,
widgetHeight: number,
clientId: string,
secret: string,
individualAccessEnabled: boolean,
}
interface RegistryRecipientAuthProps {
keycloakSettings: {
authFlows: {
officerAuthFlow: {
widgetHeight: number
}
}
citizenAuthFlow: CitizenAuthFlow
customHost: string
identityProviders: {
idGovUa: {
clientId: string
url: string
secretKey: string
}
}
realms: {
officerPortal: {
browserFlow: string
selfRegistration: boolean
}
}
},
signWidgetSettings: {
url: string
},
officerPortalSettings: {
individualAccessEnabled: boolean,
},
isEnabledPortal: boolean;
}
const props = defineProps<RegistryRecipientAuthProps>();
const isSecretExists = props.keycloakSettings?.identityProviders?.idGovUa?.secretKey.length > 0;
const selfRegistrationEnabled = ref(props.keycloakSettings?.realms?.officerPortal?.selfRegistration || false);
const isEnabledPortal = ref(props.isEnabledPortal);
const portal = ref(props.isEnabledPortal ? '' : PORTALS.officer);
const defaultValues = {
authType: OfficerAuthType.widget,
url: "https://eu.iit.com.ua/sign-widget/v20200922/",
widgetHeight: 720,
clientId: "",
secret: "",
individualAccessEnabled: false,
};
const validationSchema = Yup.object<FormValues>({
authType: Yup.string()
.required()
.oneOf([OfficerAuthType.registryIdGovUa, OfficerAuthType.widget]),
url: Yup.string().required().url(),
widgetHeight: Yup.number()
.when('authType', {
is: (value: OfficerAuthType) => value === OfficerAuthType.widget,
then: (schema) => schema.required().min(1, 'required').integer().typeError('wrongFormat'),
}),
clientId: Yup.string()
.when('authType', {
is: (value: OfficerAuthType) => value === OfficerAuthType.registryIdGovUa,
then: (schema) => schema.required(),
}),
secret: Yup.string()
.when('authType', {
is: (value: OfficerAuthType) => value === OfficerAuthType.registryIdGovUa && !isSecretExists,
then: (schema) => schema.required(),
}),
});
const { errors, validate, setFieldValue } = useForm<FormValues>({
validationSchema,
initialValues: {
authType: props.keycloakSettings?.realms?.officerPortal?.browserFlow as OfficerAuthType.widget || defaultValues.authType,
url: (
props.keycloakSettings?.realms?.officerPortal?.browserFlow === OfficerAuthType.widget
? props.signWidgetSettings?.url
: props.keycloakSettings?.identityProviders.idGovUa.url
) || defaultValues.url,
widgetHeight: props.keycloakSettings?.authFlows?.officerAuthFlow?.widgetHeight ?? defaultValues.widgetHeight,
clientId: props.keycloakSettings?.identityProviders.idGovUa.clientId || defaultValues.clientId,
secret: defaultValues.secret,
individualAccessEnabled: props.officerPortalSettings?.individualAccessEnabled || defaultValues.individualAccessEnabled
}
});
const { value: authType } = useField('authType');
const { value: url } = useField('url');
const { value: widgetHeight } = useField('widgetHeight');
const { value: clientId } = useField('clientId');
const { value: secret } = useField('secret');
const { value: individualAccessEnabled } = useField('individualAccessEnabled');
function validator() {
return new Promise((resolve) => {
validate().then((res) => {
if (res.valid) {
resolve(true);
}
});
});
}
function handleEnabledPortalChange(enabled: boolean) {
portal.value = enabled ? '' : PORTALS.officer;
}
defineExpose({
validator,
});
function handleChangeAuthType() {
setFieldValue('secret', "");
if (authType.value === OfficerAuthType.widget) {
if (props.signWidgetSettings?.url !== "") {
setFieldValue('url', props.signWidgetSettings?.url);
}
else {
setFieldValue('url', defaultValues.url);
}
if (props.keycloakSettings.authFlows.officerAuthFlow.widgetHeight !== 0) {
setFieldValue('widgetHeight', props.keycloakSettings.authFlows.officerAuthFlow.widgetHeight);
}
}
else {
setFieldValue('url', props.keycloakSettings.identityProviders.idGovUa.url);
if (props.keycloakSettings.identityProviders.idGovUa.clientId !== "") {
setFieldValue('clientId', props.keycloakSettings.identityProviders.idGovUa.clientId);
}
}
}
</script>
<template>
<Typography variant="h3" class="heading">Кабінет надавача послуг</Typography>
<input type="hidden" name="excludePortals[]" :value="portal"/>
<ToggleSwitch
name="enabledOfficerPortal"
label="Розгорнути Кабінет надавача послуг"
v-model="isEnabledPortal"
@change="handleEnabledPortalChange"
/>
<template v-if="isEnabledPortal">
<div>
<Typography variant="h5" upper-case class="subheading">Управління доступом</Typography>
<Typography variant="bodyText" class="mb16">Налаштування доступу користувачам до Кабінету користувача з використанням КЕП фізичної особи.</Typography>
<div class="toggle-switch backup-switch">
<input v-model="individualAccessEnabled" class="switch-input"
type="checkbox" id="rec-individual-access-enabled" name="rec-individual-access-enabled" />
<label for="rec-individual-access-enabled">Toggle</label>
<span>Дозволити доступ з КЕП фізичної особи</span>
</div>
</div>
<Typography variant="bodyText" class="mb16">
Є можливість використовувати власний віджет автентифікації або налаштувати інтеграцію з id.gov.ua.
</Typography>
<div class="rc-form-group">
<label for="sup-auth-type">Вкажіть тип автентифікації</label>
<select name="sup-auth-browser-flow" id="sup-auth-type"
v-model="authType" @change="handleChangeAuthType">
<option value="dso-officer-auth-flow">Віджет</option>
<option value="id-gov-ua-officer-redirector">id.gov.ua</option>
</select>
</div>
<TextField
required
label="Посилання"
name="sup-auth-url"
:error="errors.url"
v-model="url"
description="URL, повинен починатись з http:// або https://"
/>
<div v-if="authType === OfficerAuthType.widget">
<TextField
required
type="number"
label="Висота віджета, px"
name="sup-auth-widget-height"
:error="errors.widgetHeight"
v-model="widgetHeight"
/>
</div>
<div v-if="authType === OfficerAuthType.registryIdGovUa">
<TextField
required
label="Ідентифікатор клієнта (client_id)"
name="sup-auth-client-id"
:error="errors.clientId"
v-model="clientId"
/>
<TextField
required
label="Клієнтський секрет (secret)"
name="sup-auth-client-secret"
v-model="secret"
:error="errors.secret"
type="password"
placeholder="******"
/>
</div>
<div class="rc-self-registration">
<Typography variant="h5" upper-case class="subheading">Самостійна реєстрація користувачів</Typography>
<Typography variant="bodyText" class="mb16">Передбачає наявність у реєстрі попередньо змодельованого бізнес-процесу самореєстрації.</Typography>
<div class="toggle-switch backup-switch">
<input v-model="selfRegistrationEnabled" class="switch-input"
type="checkbox" id="self-registration-switch-input" name="self-registration-enabled" />
<label for="self-registration-switch-input">Toggle</label>
<span>Дозволити самостійну реєстрацію</span>
</div>
<div class="wizard-warning" v-if="selfRegistrationEnabled">
При вимкненні можливості, користувачі, які почали процес самореєстрації, не зможуть виконати свої задачі, якщо вони змодельовані.
</div>
</div>
</template>
</template>
<style scoped>
.rc-self-registration {
margin-top: 32px;
}
.mb16 {
margin-bottom: 16px;
}
.heading {
margin-bottom: 24px;
}
.subheading {
margin-top: 32px;
margin-bottom: 24px;
}
</style>