mirror of https://github.com/rancher/dashboard.git
399 lines
11 KiB
Vue
399 lines
11 KiB
Vue
<script>
|
|
import Loading from '@shell/components/Loading';
|
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
import AuthConfig from '@shell/mixins/auth-config';
|
|
import CruResource from '@shell/components/CruResource';
|
|
import AllowedPrincipals from '@shell/components/auth/AllowedPrincipals';
|
|
import FileSelector from '@shell/components/form/FileSelector';
|
|
import AuthBanner from '@shell/components/auth/AuthBanner';
|
|
import AuthProviderWarningBanners from '@shell/edit/auth/AuthProviderWarningBanners';
|
|
import AdvancedSection from '@shell/components/AdvancedSection.vue';
|
|
import ArrayList from '@shell/components/form/ArrayList';
|
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
|
import { RadioGroup } from '@components/Form/Radio';
|
|
|
|
export default {
|
|
components: {
|
|
Loading,
|
|
CruResource,
|
|
AllowedPrincipals,
|
|
FileSelector,
|
|
AuthBanner,
|
|
AuthProviderWarningBanners,
|
|
AdvancedSection,
|
|
ArrayList,
|
|
LabeledInput,
|
|
RadioGroup
|
|
},
|
|
|
|
mixins: [CreateEditView, AuthConfig],
|
|
|
|
data() {
|
|
return {
|
|
customEndpoint: {
|
|
value: false,
|
|
labels: [
|
|
this.t('authConfig.oidc.customEndpoint.standard'),
|
|
this.t('authConfig.oidc.customEndpoint.custom'),
|
|
],
|
|
options: [
|
|
false,
|
|
true
|
|
]
|
|
},
|
|
oidcUrls: {
|
|
url: null,
|
|
realm: null,
|
|
jwksUrl: null,
|
|
tokenEndpoint: null,
|
|
userInfoEndpoint: null,
|
|
},
|
|
oidcScope: []
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
tArgs() {
|
|
return {
|
|
baseUrl: this.serverSetting,
|
|
provider: this.displayName,
|
|
username: this.principal.loginName || this.principal.name,
|
|
};
|
|
},
|
|
|
|
toSave() {
|
|
return {
|
|
enabled: true,
|
|
oidcConfig: this.model
|
|
};
|
|
},
|
|
|
|
validationPassed() {
|
|
if ( this.model.enabled && !this.editConfig ) {
|
|
return true;
|
|
}
|
|
|
|
const { clientId, clientSecret } = this.model;
|
|
const isValidScope = this.model.id === 'keycloakoidc' || this.oidcScope?.includes('openid');
|
|
|
|
if ( !isValidScope ) {
|
|
return false;
|
|
}
|
|
|
|
if ( !this.customEndpoint.value ) {
|
|
const { url, realm } = this.oidcUrls;
|
|
|
|
return !!(clientId && clientSecret && url && realm);
|
|
} else {
|
|
const { rancherUrl, issuer } = this.model;
|
|
|
|
return !!(clientId && clientSecret && rancherUrl && issuer);
|
|
}
|
|
}
|
|
},
|
|
|
|
watch: {
|
|
'oidcUrls.url'() {
|
|
this.updateEndpoints();
|
|
},
|
|
|
|
'oidcUrls.realm'() {
|
|
this.updateEndpoints();
|
|
},
|
|
|
|
'model.enabled'(neu) {
|
|
// Cover case where oidc gets disabled and we return to the edit screen with a reset model
|
|
if (!neu) {
|
|
this.oidcUrls = {
|
|
url: null,
|
|
realm: null,
|
|
jwksUrl: null,
|
|
tokenEndpoint: null,
|
|
userInfoEndpoint: null,
|
|
};
|
|
this.customEndpoint.value = false;
|
|
this.oidcScope = this.model?.scope?.split(' ');
|
|
} else {
|
|
this.oidcScope = this.model?.scope?.split(' ');
|
|
}
|
|
},
|
|
|
|
editConfig(neu, old) {
|
|
// Cover use case where user edits existing oidc (oidcUrls aren't persisted, so if we have issuer set custom endpoints to true)
|
|
if (!old && neu) {
|
|
this.customEndpoint.value = !this.oidcUrls.url && !!this.model.issuer;
|
|
}
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
updateEndpoints() {
|
|
if (!this.oidcUrls.url) {
|
|
return;
|
|
}
|
|
const isKeycloak = this.model.id === 'keycloakoidc';
|
|
|
|
const url = this.oidcUrls.url.replaceAll(' ', '');
|
|
const realmsPath = isKeycloak ? 'auth/realms' : 'realms';
|
|
|
|
this.model.issuer = `${ url }/${ realmsPath }/${ this.oidcUrls.realm || '' }`;
|
|
|
|
if ( isKeycloak ) {
|
|
this.model.authEndpoint = `${ this.model.issuer || '' }/protocol/openid-connect/auth`;
|
|
}
|
|
},
|
|
|
|
updateScope() {
|
|
this.model.scope = this.oidcScope.join(' ');
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<Loading v-if="$fetchState.pending" />
|
|
<div v-else>
|
|
<CruResource
|
|
:cancel-event="true"
|
|
:done-route="doneRoute"
|
|
:mode="mode"
|
|
:resource="model"
|
|
:subtypes="[]"
|
|
:validation-passed="validationPassed"
|
|
:finish-button-mode="model.enabled ? 'edit' : 'enable'"
|
|
:can-yaml="false"
|
|
:errors="errors"
|
|
:show-cancel="showCancel"
|
|
@error="e=>errors = e"
|
|
@finish="save"
|
|
@cancel="cancel"
|
|
>
|
|
<template v-if="model.enabled && !isEnabling && !editConfig">
|
|
<AuthBanner
|
|
:t-args="tArgs"
|
|
:disable="disable"
|
|
:edit="goToEdit"
|
|
>
|
|
<template #rows>
|
|
<tr><td>{{ t(`authConfig.oidc.rancherUrl`) }}: </td><td>{{ model.rancherUrl }}</td></tr>
|
|
<tr><td>{{ t(`authConfig.oidc.clientId`) }}: </td><td>{{ model.clientId }}</td></tr>
|
|
<tr><td>{{ t(`authConfig.oidc.issuer`) }}: </td><td>{{ model.issuer }}</td></tr>
|
|
<tr><td>{{ t(`authConfig.oidc.authEndpoint`) }}: </td><td>{{ model.authEndpoint }}</td></tr>
|
|
</template>
|
|
</AuthBanner>
|
|
|
|
<hr>
|
|
|
|
<AllowedPrincipals
|
|
:provider="NAME"
|
|
:auth-config="model"
|
|
:mode="mode"
|
|
/>
|
|
</template>
|
|
|
|
<template v-else>
|
|
<AuthProviderWarningBanners
|
|
v-if="!model.enabled"
|
|
:t-args="tArgs"
|
|
/>
|
|
|
|
<h3>{{ t(`authConfig.oidc.${NAME}`) }}</h3>
|
|
|
|
<div class="row mb-20">
|
|
<div class="col span-6">
|
|
<LabeledInput
|
|
v-model:value="model.clientId"
|
|
:label="t(`authConfig.oidc.clientId`)"
|
|
:mode="mode"
|
|
required
|
|
data-testid="oidc-client-id"
|
|
/>
|
|
</div>
|
|
<div class="col span-6">
|
|
<LabeledInput
|
|
v-model:value="model.clientSecret"
|
|
:label="t(`authConfig.oidc.clientSecret`)"
|
|
:mode="mode"
|
|
required
|
|
data-testid="oidc-client-secret"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-20">
|
|
<div class="col span-6">
|
|
<LabeledInput
|
|
v-model:value="model.privateKey"
|
|
:label="t(`authConfig.oidc.key.label`)"
|
|
:placeholder="t(`authConfig.oidc.key.placeholder`)"
|
|
:mode="mode"
|
|
type="multiline"
|
|
/>
|
|
<FileSelector
|
|
class="role-tertiary add mt-5"
|
|
:label="t('generic.readFromFile')"
|
|
:mode="mode"
|
|
@selected="model.privateKey = $event"
|
|
/>
|
|
</div>
|
|
<div class="col span-6">
|
|
<LabeledInput
|
|
v-model:value="model.certificate"
|
|
:label="t(`authConfig.oidc.cert.label`)"
|
|
:placeholder="t(`authConfig.oidc.cert.placeholder`)"
|
|
:mode="mode"
|
|
type="multiline"
|
|
/>
|
|
<FileSelector
|
|
class="role-tertiary add mt-5"
|
|
:label="t('generic.readFromFile')"
|
|
:mode="mode"
|
|
@selected="model.certificate = $event"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-20">
|
|
<div class="col span-6">
|
|
<ArrayList
|
|
v-model:value="oidcScope"
|
|
:mode="mode"
|
|
:title="t('authConfig.oidc.scope.label')"
|
|
:value-placeholder="t('authConfig.oidc.scope.placeholder')"
|
|
:protip="t('authConfig.oidc.scope.protip', {}, true)"
|
|
@update:value="updateScope"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-20">
|
|
<div class="col span-6">
|
|
<RadioGroup
|
|
v-model:value="customEndpoint.value"
|
|
name="customEndpoint"
|
|
label-key="authConfig.oidc.customEndpoint.label"
|
|
:labels="customEndpoint.labels"
|
|
:options="customEndpoint.options"
|
|
data-testid="oidc-custom-endpoint"
|
|
>
|
|
<template #label>
|
|
<h4>{{ t('authConfig.oidc.customEndpoint.label') }}</h4>
|
|
</template>
|
|
</RadioGroup>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-20">
|
|
<div class="col span-6">
|
|
<LabeledInput
|
|
v-model:value="oidcUrls.url"
|
|
:label="t(`authConfig.oidc.url`)"
|
|
:mode="mode"
|
|
:required="!customEndpoint.value"
|
|
:disabled="customEndpoint.value"
|
|
data-testid="oidc-url"
|
|
/>
|
|
</div>
|
|
<div class="col span-6">
|
|
<LabeledInput
|
|
v-model:value="oidcUrls.realm"
|
|
:label="t(`authConfig.oidc.realm`)"
|
|
:mode="mode"
|
|
:required="!customEndpoint.value"
|
|
:disabled="customEndpoint.value"
|
|
data-testid="oidc-realm"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-20">
|
|
<div class="col span-6">
|
|
<LabeledInput
|
|
v-model:value="model.rancherUrl"
|
|
:label="t(`authConfig.oidc.rancherUrl`)"
|
|
:mode="mode"
|
|
required
|
|
:disabled="!customEndpoint.value"
|
|
data-testid="oidc-rancher-url"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-20">
|
|
<div class="col span-6">
|
|
<LabeledInput
|
|
v-model:value="model.issuer"
|
|
:label="t(`authConfig.oidc.issuer`)"
|
|
:mode="mode"
|
|
required
|
|
:disabled="!customEndpoint.value"
|
|
data-testid="oidc-issuer"
|
|
/>
|
|
</div>
|
|
<div class="col span-6">
|
|
<LabeledInput
|
|
v-model:value="model.authEndpoint"
|
|
:label="t(`authConfig.oidc.authEndpoint`)"
|
|
:mode="mode"
|
|
:disabled="!customEndpoint.value"
|
|
:required="model.id === 'keycloakoidc'"
|
|
data-testid="oidc-auth-endpoint"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<AdvancedSection :mode="mode">
|
|
<div class="row mb-20">
|
|
<div class="col span-6">
|
|
<LabeledInput
|
|
v-model:value="model.jwksUrl"
|
|
:label="t(`authConfig.oidc.jwksUrl`)"
|
|
:mode="mode"
|
|
:disabled="!customEndpoint.value"
|
|
/>
|
|
</div>
|
|
<div class="col span-6">
|
|
<LabeledInput
|
|
v-model:value="model.tokenEndpoint"
|
|
:label="t(`authConfig.oidc.tokenEndpoint`)"
|
|
:mode="mode"
|
|
:disabled="!customEndpoint.value"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-20">
|
|
<div class="col span-6">
|
|
<LabeledInput
|
|
v-model:value="model.userInfoEndpoint"
|
|
:label="t(`authConfig.oidc.userInfoEndpoint`)"
|
|
:mode="mode"
|
|
:disabled="!customEndpoint.value"
|
|
/>
|
|
</div>
|
|
<div class="col span-6">
|
|
<LabeledInput
|
|
v-model:value="model.acrValue"
|
|
:label="t(`authConfig.oidc.acrValue`)"
|
|
:mode="mode"
|
|
:disabled="!customEndpoint.value"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</AdvancedSection>
|
|
</template>
|
|
</CruResource>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.banner {
|
|
display: block;
|
|
|
|
&:deep() code {
|
|
padding: 0 3px;
|
|
margin: 0 3px;
|
|
}
|
|
}
|
|
</style>
|