feat: add additional SAML information
This commit is contained in:
@@ -390,16 +390,23 @@
|
||||
"SAML": {
|
||||
"TITLE": "SAML SSO",
|
||||
"NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
|
||||
"ACS_URL": {
|
||||
"LABEL": "ACS URL"
|
||||
},
|
||||
"SSO_URL": {
|
||||
"LABEL": "SSO URL",
|
||||
"HELP": "The URL where SAML authentication requests will be sent",
|
||||
"PLACEHOLDER": "https://your-idp.com/saml/sso"
|
||||
},
|
||||
"CERTIFICATE": {
|
||||
"LABEL": "X.509 Certificate",
|
||||
"LABEL": "Signing certificate in PEM format",
|
||||
"HELP": "The public certificate from your identity provider used to verify SAML responses",
|
||||
"PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
|
||||
},
|
||||
"FINGERPRINT": {
|
||||
"LABEL": "Fingerprint"
|
||||
},
|
||||
"COPY_SUCCESS": "Copied to clipboard",
|
||||
"SP_ENTITY_ID": {
|
||||
"LABEL": "Service Provider Entity ID",
|
||||
"HELP": "Unique identifier for this application as a service provider. Leave blank to use default.",
|
||||
@@ -426,6 +433,10 @@
|
||||
"UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
|
||||
"UPGRADE_NOW": "Upgrade now",
|
||||
"CANCEL_ANYTIME": "You can change or cancel your plan anytime"
|
||||
},
|
||||
"ATTRIBUTE_MAPPING": {
|
||||
"TITLE": "SAML Attribute Setup",
|
||||
"DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
const isExpanded = ref(false);
|
||||
|
||||
const toggleExpanded = () => {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section
|
||||
class="rounded-xl border border-n-weak bg-n-solid-1 w-full text-sm text-n-slate-12 mb-5 overflow-hidden"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="w-full px-4 py-3 flex items-center justify-between text-left hover:bg-n-solid-2 transition-colors"
|
||||
@click="toggleExpanded"
|
||||
>
|
||||
<h4 class="font-medium text-n-slate-12">
|
||||
{{ t('SECURITY_SETTINGS.SAML.ATTRIBUTE_MAPPING.TITLE') }}
|
||||
</h4>
|
||||
<i
|
||||
class="i-lucide-chevron-down transition-transform duration-200"
|
||||
:class="{ 'rotate-180': isExpanded }"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="transition-[height] duration-200 ease-in-out overflow-hidden"
|
||||
:class="isExpanded ? 'h-auto' : 'h-0'"
|
||||
>
|
||||
<div class="px-4 pb-3">
|
||||
<p class="text-n-slate-11 mb-2">
|
||||
{{ t('SECURITY_SETTINGS.SAML.ATTRIBUTE_MAPPING.DESCRIPTION') }}
|
||||
</p>
|
||||
<!-- eslint-disable vue/no-bare-strings-in-template -->
|
||||
<ul class="list-none text-n-slate-12 space-y-1">
|
||||
<li><code class="px-1 rounded bg-n-slate-3">email</code></li>
|
||||
<li><code class="px-1 rounded bg-n-slate-3">first_name</code></li>
|
||||
<li><code class="px-1 rounded bg-n-slate-3">last_name</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { copyTextToClipboard } from 'shared/helpers/clipboard';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useAccount } from 'dashboard/composables/useAccount';
|
||||
import NextButton from 'next/button/Button.vue';
|
||||
|
||||
defineProps({
|
||||
fingerprint: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const { accountId } = useAccount();
|
||||
|
||||
const acsUrl = computed(() => {
|
||||
const currentHost = window.location.origin;
|
||||
return `${currentHost}/omniauth/saml/callback?account_id=${accountId.value}`;
|
||||
});
|
||||
|
||||
const handleCopy = async text => {
|
||||
await copyTextToClipboard(text);
|
||||
useAlert(t('SECURITY_SETTINGS.SAML.COPY_SUCCESS'));
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section
|
||||
class="rounded-xl border border-n-weak bg-n-solid-1 w-full text-sm text-n-slate-12 divide-y divide-n-weak"
|
||||
>
|
||||
<div
|
||||
v-if="fingerprint"
|
||||
class="pl-4 pr-1 py-1 flex justify-between items-center"
|
||||
>
|
||||
<div class="flex">
|
||||
<span class="text-n-slate-11 w-24">
|
||||
{{ t('SECURITY_SETTINGS.SAML.FINGERPRINT.LABEL') }}
|
||||
</span>
|
||||
{{ fingerprint }}
|
||||
</div>
|
||||
<NextButton
|
||||
type="button"
|
||||
ghost
|
||||
sm
|
||||
slate
|
||||
icon="i-lucide-copy"
|
||||
@click="handleCopy(fingerprint)"
|
||||
/>
|
||||
</div>
|
||||
<div class="pl-4 pr-1 py-1 flex justify-between items-center">
|
||||
<div class="flex">
|
||||
<span class="text-n-slate-11 w-24">
|
||||
{{ t('SECURITY_SETTINGS.SAML.ACS_URL.LABEL') }}
|
||||
</span>
|
||||
{{ acsUrl }}
|
||||
</div>
|
||||
<NextButton
|
||||
type="button"
|
||||
ghost
|
||||
sm
|
||||
slate
|
||||
icon="i-lucide-copy"
|
||||
@click="handleCopy(acsUrl)"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
+20
-12
@@ -3,19 +3,23 @@ import { ref, computed, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useAccount } from 'dashboard/composables/useAccount';
|
||||
import samlSettingsAPI from 'dashboard/api/samlSettings';
|
||||
|
||||
import SectionLayout from '../../account/components/SectionLayout.vue';
|
||||
import WithLabel from 'v3/components/Form/WithLabel.vue';
|
||||
import TextInput from 'next/input/Input.vue';
|
||||
import TextArea from 'next/textarea/TextArea.vue';
|
||||
import Switch from 'next/switch/Switch.vue';
|
||||
import NextButton from 'next/button/Button.vue';
|
||||
import samlSettingsAPI from 'dashboard/api/samlSettings';
|
||||
import SamlInfoSection from './SamlInfoSection.vue';
|
||||
import SamlAttributeMap from './SamlAttributeMap.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { isCloudFeatureEnabled } = useAccount();
|
||||
|
||||
const ssoUrl = ref('');
|
||||
const certificate = ref('');
|
||||
const fingerprint = ref('');
|
||||
const spEntityId = ref('');
|
||||
const roleMappings = ref({});
|
||||
const isEnabled = ref(false);
|
||||
@@ -37,6 +41,7 @@ const loadSamlSettings = async () => {
|
||||
certificate.value = settings.certificate || '';
|
||||
spEntityId.value = settings.sp_entity_id || '';
|
||||
roleMappings.value = settings.role_mappings || {};
|
||||
fingerprint.value = settings.fingerprint || '';
|
||||
isEnabled.value = ssoUrl.value !== '';
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -129,6 +134,9 @@ onMounted(() => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<SamlInfoSection class="mb-5" :fingerprint="fingerprint" />
|
||||
<SamlAttributeMap class="mb-5" />
|
||||
|
||||
<form class="grid gap-5" @submit.prevent="handleSubmit">
|
||||
<WithLabel
|
||||
:label="t('SECURITY_SETTINGS.SAML.SSO_URL.LABEL')"
|
||||
@@ -144,6 +152,17 @@ onMounted(() => {
|
||||
/>
|
||||
</WithLabel>
|
||||
|
||||
<WithLabel
|
||||
:label="t('SECURITY_SETTINGS.SAML.SP_ENTITY_ID.LABEL')"
|
||||
:help-message="t('SECURITY_SETTINGS.SAML.SP_ENTITY_ID.HELP')"
|
||||
>
|
||||
<TextInput
|
||||
v-model="spEntityId"
|
||||
class="w-full"
|
||||
:placeholder="t('SECURITY_SETTINGS.SAML.SP_ENTITY_ID.PLACEHOLDER')"
|
||||
/>
|
||||
</WithLabel>
|
||||
|
||||
<WithLabel
|
||||
:label="t('SECURITY_SETTINGS.SAML.CERTIFICATE.LABEL')"
|
||||
:help-message="t('SECURITY_SETTINGS.SAML.CERTIFICATE.HELP')"
|
||||
@@ -158,17 +177,6 @@ onMounted(() => {
|
||||
/>
|
||||
</WithLabel>
|
||||
|
||||
<WithLabel
|
||||
:label="t('SECURITY_SETTINGS.SAML.SP_ENTITY_ID.LABEL')"
|
||||
:help-message="t('SECURITY_SETTINGS.SAML.SP_ENTITY_ID.HELP')"
|
||||
>
|
||||
<TextInput
|
||||
v-model="spEntityId"
|
||||
class="w-full"
|
||||
:placeholder="t('SECURITY_SETTINGS.SAML.SP_ENTITY_ID.PLACEHOLDER')"
|
||||
/>
|
||||
</WithLabel>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<NextButton
|
||||
blue
|
||||
|
||||
Reference in New Issue
Block a user