mirror of https://github.com/rancher/dashboard.git
Migrating cluster membership from ember to vue (clusterRoleTemplateBindings)
rancher/dashboard#2501
This commit is contained in:
parent
1b6da12755
commit
bbfc4070aa
|
|
@ -1686,6 +1686,33 @@ login:
|
||||||
loginWithLocal: Log in with Local User
|
loginWithLocal: Log in with Local User
|
||||||
useProvider: Use a {provider} user
|
useProvider: Use a {provider} user
|
||||||
|
|
||||||
|
members:
|
||||||
|
clusterMembers: Cluster Members
|
||||||
|
createActionLabel: Add
|
||||||
|
clusterPermissions:
|
||||||
|
noDescription: User created - no description
|
||||||
|
label: Cluster Permissions
|
||||||
|
description: Controls what access users have to the Cluster
|
||||||
|
createProjects: Create Projects
|
||||||
|
manageClusterBackups: Manage Cluster Backups
|
||||||
|
manageClusterCatalogs: Manage Cluster Catalogs
|
||||||
|
manageClusterMembers: Manage Cluster Members
|
||||||
|
manageNodes: Manage Nodes
|
||||||
|
manageStorage: Manage Storage
|
||||||
|
viewAllProjects: View All Projects
|
||||||
|
viewClusterCatalogs: View Cluster Catalogs
|
||||||
|
viewClusterMembers: View Cluster Members
|
||||||
|
viewNodes: View Nodes
|
||||||
|
owner:
|
||||||
|
label: Owner
|
||||||
|
description: Owners have full control over the Cluster and all resources inside it.
|
||||||
|
member:
|
||||||
|
label: Member
|
||||||
|
description: Members can manage the resources inside the Cluster but not change the Cluster itself.
|
||||||
|
custom:
|
||||||
|
label: Custom
|
||||||
|
description: Choose individual roles for this user.
|
||||||
|
|
||||||
monitoring:
|
monitoring:
|
||||||
accessModes:
|
accessModes:
|
||||||
many: ReadWriteMany
|
many: ReadWriteMany
|
||||||
|
|
@ -2500,6 +2527,8 @@ rbac:
|
||||||
fleetworkspace-admin: Admin
|
fleetworkspace-admin: Admin
|
||||||
fleetworkspace-member: Member
|
fleetworkspace-member: Member
|
||||||
fleetworkspace-readonly: Read-Only
|
fleetworkspace-readonly: Read-Only
|
||||||
|
members:
|
||||||
|
label: Members
|
||||||
roletemplate:
|
roletemplate:
|
||||||
label: Roles
|
label: Roles
|
||||||
newUserDefault:
|
newUserDefault:
|
||||||
|
|
@ -4129,6 +4158,11 @@ typeLabel:
|
||||||
one { Cluster Group }
|
one { Cluster Group }
|
||||||
other {Cluster Groups }
|
other {Cluster Groups }
|
||||||
}
|
}
|
||||||
|
management.cattle.io.clusterroletemplatebinding: |-
|
||||||
|
{count, plural,
|
||||||
|
one { Cluster Member }
|
||||||
|
other { Cluster Members }
|
||||||
|
}
|
||||||
fleet.cattle.io.gitrepo: |-
|
fleet.cattle.io.gitrepo: |-
|
||||||
{count, plural,
|
{count, plural,
|
||||||
one { Git Repo }
|
one { Git Repo }
|
||||||
|
|
@ -4215,6 +4249,11 @@ typeLabel:
|
||||||
one { User }
|
one { User }
|
||||||
other { Users }
|
other { Users }
|
||||||
}
|
}
|
||||||
|
namespace: |-
|
||||||
|
{count, plural,
|
||||||
|
one { Namespace }
|
||||||
|
other { Namespaces }
|
||||||
|
}
|
||||||
group.principal: |-
|
group.principal: |-
|
||||||
{count, plural,
|
{count, plural,
|
||||||
one { Group }
|
one { Group }
|
||||||
|
|
|
||||||
|
|
@ -317,7 +317,8 @@ export default {
|
||||||
{{ parent.displayName }}:
|
{{ parent.displayName }}:
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
<span v-else>{{ parent.displayName }}:</span>
|
<span v-else>{{ parent.displayName }}:</span>
|
||||||
<t :k="'resourceDetail.header.' + realMode" :subtype="resourceSubtype" :name="value.nameDisplay" />
|
<span v-if="value.detailPageHeaderActionOverride && value.detailPageHeaderActionOverride(realMode)">{{ value.detailPageHeaderActionOverride(realMode) }}</span>
|
||||||
|
<t v-else :k="'resourceDetail.header.' + realMode" :subtype="resourceSubtype" :name="value.nameDisplay" />
|
||||||
<BadgeState v-if="!isCreate && parent.showState" class="masthead-state" :value="value" />
|
<BadgeState v-if="!isCreate && parent.showState" class="masthead-state" :value="value" />
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,17 @@ export default {
|
||||||
mode: {
|
mode: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'edit',
|
default: 'edit',
|
||||||
}
|
},
|
||||||
|
|
||||||
|
descriptionKey: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -102,17 +112,27 @@ export default {
|
||||||
:aria-checked="isChecked"
|
:aria-checked="isChecked"
|
||||||
role="radio"
|
role="radio"
|
||||||
/>
|
/>
|
||||||
<label
|
<div class="labeling">
|
||||||
v-if="label"
|
<label
|
||||||
:class="[ muteLabel ? 'text-muted' : '', 'radio-label']"
|
v-if="label"
|
||||||
v-html="label"
|
:class="[ muteLabel ? 'text-muted' : '', 'radio-label', 'm-0']"
|
||||||
>
|
v-html="label"
|
||||||
<slot name="label">{{ label }}</slot>
|
>
|
||||||
</label>
|
<slot name="label">{{ label }}</slot>
|
||||||
|
</label>
|
||||||
|
<div v-if="descriptionKey || description" class="radio-button-outer-container-description">
|
||||||
|
<t v-if="descriptionKey" :k="descriptionKey" />
|
||||||
|
<template v-else-if="description">
|
||||||
|
{{ description }}
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang='scss'>
|
<style lang='scss'>
|
||||||
|
$fontColor: var(--input-label);
|
||||||
|
|
||||||
.radio-view {
|
.radio-view {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -131,7 +151,7 @@ export default {
|
||||||
.radio-container {
|
.radio-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
@ -142,10 +162,6 @@ export default {
|
||||||
cursor: not-allowed
|
cursor: not-allowed
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio-label {
|
|
||||||
margin: 3px 10px 0px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio-custom {
|
.radio-custom {
|
||||||
height: 14px;
|
height: 14px;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
|
|
@ -155,6 +171,7 @@ export default {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
transition: all 0.3s ease-out;
|
transition: all 0.3s ease-out;
|
||||||
border: 1.5px solid var(--border);
|
border: 1.5px solid var(--border);
|
||||||
|
margin-top: 5px;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|
@ -186,6 +203,19 @@ export default {
|
||||||
background-color: var(--disabled-bg);
|
background-color: var(--disabled-bg);
|
||||||
opacity: .25;
|
opacity: .25;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.radio-button-outer-container-description {
|
||||||
|
color: $fontColor;
|
||||||
|
font-size: 11px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.labeling {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
margin: 3px 10px 0px 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,7 @@ export default {
|
||||||
:name="name"
|
:name="name"
|
||||||
:value="value"
|
:value="value"
|
||||||
:label="option.label"
|
:label="option.label"
|
||||||
|
:description="option.description"
|
||||||
:val="option.value"
|
:val="option.value"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import {
|
||||||
WORKLOAD, WORKLOAD_TYPES, SERVICE, HPA, NETWORK_POLICY, PV, PVC, STORAGE_CLASS, POD,
|
WORKLOAD, WORKLOAD_TYPES, SERVICE, HPA, NETWORK_POLICY, PV, PVC, STORAGE_CLASS, POD,
|
||||||
RBAC,
|
RBAC,
|
||||||
MANAGEMENT,
|
MANAGEMENT,
|
||||||
|
NAMESPACE,
|
||||||
NORMAN,
|
NORMAN,
|
||||||
} from '@/config/types';
|
} from '@/config/types';
|
||||||
|
|
||||||
|
|
@ -27,6 +28,7 @@ export function init(store) {
|
||||||
basicType,
|
basicType,
|
||||||
ignoreType,
|
ignoreType,
|
||||||
mapGroup,
|
mapGroup,
|
||||||
|
mapType,
|
||||||
weightGroup,
|
weightGroup,
|
||||||
weightType,
|
weightType,
|
||||||
headers,
|
headers,
|
||||||
|
|
@ -40,7 +42,7 @@ export function init(store) {
|
||||||
weight: 3,
|
weight: 3,
|
||||||
showNamespaceFilter: true,
|
showNamespaceFilter: true,
|
||||||
icon: 'compass',
|
icon: 'compass',
|
||||||
typeStoreMap: { [MANAGEMENT.PROJECT]: 'management' }
|
typeStoreMap: { [MANAGEMENT.PROJECT]: 'management', [MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING]: 'management' }
|
||||||
});
|
});
|
||||||
|
|
||||||
basicType(['cluster-dashboard', 'cluster-tools']);
|
basicType(['cluster-dashboard', 'cluster-tools']);
|
||||||
|
|
@ -77,6 +79,7 @@ export function init(store) {
|
||||||
RBAC.CLUSTER_ROLE,
|
RBAC.CLUSTER_ROLE,
|
||||||
RBAC.ROLE_BINDING,
|
RBAC.ROLE_BINDING,
|
||||||
RBAC.CLUSTER_ROLE_BINDING,
|
RBAC.CLUSTER_ROLE_BINDING,
|
||||||
|
'cluster-members'
|
||||||
], 'rbac');
|
], 'rbac');
|
||||||
|
|
||||||
weightGroup('cluster', 99, true);
|
weightGroup('cluster', 99, true);
|
||||||
|
|
@ -124,9 +127,12 @@ export function init(store) {
|
||||||
mapGroup(/^(.*\.)?cluster\.x-k8s\.io$/, 'Cluster Provisioning');
|
mapGroup(/^(.*\.)?cluster\.x-k8s\.io$/, 'Cluster Provisioning');
|
||||||
mapGroup(/^(aks|eks|gke|rke|rke-machine-config|provisioning)\.cattle\.io$/, 'Cluster Provisioning');
|
mapGroup(/^(aks|eks|gke|rke|rke-machine-config|provisioning)\.cattle\.io$/, 'Cluster Provisioning');
|
||||||
|
|
||||||
|
mapType(MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING, store.getters['i18n/t'](`typeLabel.${ MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING }`, { count: 2 }));
|
||||||
|
|
||||||
configureType(NODE, { isCreatable: false, isEditable: false });
|
configureType(NODE, { isCreatable: false, isEditable: false });
|
||||||
configureType(WORKLOAD_TYPES.JOB, { isEditable: false, match: WORKLOAD_TYPES.JOB });
|
configureType(WORKLOAD_TYPES.JOB, { isEditable: false, match: WORKLOAD_TYPES.JOB });
|
||||||
configureType(PVC, { isEditable: false });
|
configureType(PVC, { isEditable: false });
|
||||||
|
configureType(MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING, { isEditable: false });
|
||||||
|
|
||||||
configureType('workload', {
|
configureType('workload', {
|
||||||
displayName: 'Workload',
|
displayName: 'Workload',
|
||||||
|
|
@ -200,7 +206,7 @@ export function init(store) {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
virtualType({
|
virtualType({
|
||||||
label: 'Cluster Dashboard',
|
label: store.getters['i18n/t']('clusterIndexPage.header'),
|
||||||
group: 'Root',
|
group: 'Root',
|
||||||
namespaced: false,
|
namespaced: false,
|
||||||
name: 'cluster-dashboard',
|
name: 'cluster-dashboard',
|
||||||
|
|
@ -211,7 +217,18 @@ export function init(store) {
|
||||||
});
|
});
|
||||||
|
|
||||||
virtualType({
|
virtualType({
|
||||||
label: 'Overview',
|
label: store.getters['i18n/t']('members.clusterMembers'),
|
||||||
|
group: 'rbac',
|
||||||
|
namespaced: false,
|
||||||
|
name: 'cluster-members',
|
||||||
|
icon: 'globe',
|
||||||
|
weight: 100,
|
||||||
|
route: { name: 'c-cluster-explorer-members' },
|
||||||
|
exact: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
virtualType({
|
||||||
|
label: store.getters['i18n/t']('generic.overview'),
|
||||||
group: 'Workload',
|
group: 'Workload',
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
name: 'workload',
|
name: 'workload',
|
||||||
|
|
@ -226,7 +243,7 @@ export function init(store) {
|
||||||
});
|
});
|
||||||
|
|
||||||
virtualType({
|
virtualType({
|
||||||
label: 'Projects/Namespaces',
|
label: store.getters['i18n/t']('projectNamespaces.label'),
|
||||||
group: 'cluster',
|
group: 'cluster',
|
||||||
icon: 'globe',
|
icon: 'globe',
|
||||||
namespaced: false,
|
namespaced: false,
|
||||||
|
|
@ -238,7 +255,7 @@ export function init(store) {
|
||||||
});
|
});
|
||||||
|
|
||||||
virtualType({
|
virtualType({
|
||||||
label: 'Namespaces',
|
label: store.getters['i18n/t'](`typeLabel.${ NAMESPACE }`, { count: 2 }),
|
||||||
group: 'cluster',
|
group: 'cluster',
|
||||||
icon: 'globe',
|
icon: 'globe',
|
||||||
namespaced: false,
|
namespaced: false,
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,14 @@ export const RAM = {
|
||||||
width: 120,
|
width: 120,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const PRINCIPAL = {
|
||||||
|
name: 'principal',
|
||||||
|
labelKey: 'tableHeaders.name',
|
||||||
|
sort: 'principal.loginName',
|
||||||
|
value: 'userPrincipalName',
|
||||||
|
formatter: 'Principal',
|
||||||
|
};
|
||||||
|
|
||||||
export const PODS = {
|
export const PODS = {
|
||||||
name: 'pods',
|
name: 'pods',
|
||||||
labelKey: 'tableHeaders.pods',
|
labelKey: 'tableHeaders.pods',
|
||||||
|
|
@ -789,6 +797,12 @@ export const RESTART = {
|
||||||
width: 75,
|
width: 75,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ROLE = {
|
||||||
|
name: 'role',
|
||||||
|
value: 'roleDisplay',
|
||||||
|
labelKey: 'tableHeaders.role',
|
||||||
|
};
|
||||||
|
|
||||||
export const FEATURE_DESCRIPTION = {
|
export const FEATURE_DESCRIPTION = {
|
||||||
name: 'description',
|
name: 'description',
|
||||||
labelKey: 'tableHeaders.description',
|
labelKey: 'tableHeaders.description',
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,15 @@ export const STEVE = {
|
||||||
// Auth (via Norman)
|
// Auth (via Norman)
|
||||||
// Base: /v3
|
// Base: /v3
|
||||||
export const NORMAN = {
|
export const NORMAN = {
|
||||||
AUTH_CONFIG: 'authconfig',
|
AUTH_CONFIG: 'authconfig',
|
||||||
ETCD_BACKUP: 'etcdbackup',
|
ETCD_BACKUP: 'etcdbackup',
|
||||||
CLUSTER_TOKEN: 'clusterregistrationtoken',
|
CLUSTER_TOKEN: 'clusterregistrationtoken',
|
||||||
GROUP: 'group',
|
CLUSTER_ROLE_TEMPLATE_BINDING: 'clusterRoleTemplateBinding',
|
||||||
PRINCIPAL: 'principal',
|
GROUP: 'group',
|
||||||
SPOOFED: { GROUP_PRINCIPAL: 'group.principal' },
|
PRINCIPAL: 'principal',
|
||||||
TOKEN: 'token',
|
SPOOFED: { GROUP_PRINCIPAL: 'group.principal' },
|
||||||
USER: 'user',
|
TOKEN: 'token',
|
||||||
|
USER: 'user',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Public (via Norman)
|
// Public (via Norman)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,222 @@
|
||||||
|
<script>
|
||||||
|
import CreateEditView from '@/mixins/create-edit-view';
|
||||||
|
import CruResource from '@/components/CruResource';
|
||||||
|
import SelectPrincipal from '@/components/auth/SelectPrincipal';
|
||||||
|
import { MANAGEMENT, NORMAN } from '@/config/types';
|
||||||
|
import RadioGroup from '@/components/form/RadioGroup';
|
||||||
|
import Card from '@/components/Card';
|
||||||
|
import Loading from '@/components/Loading';
|
||||||
|
import Checkbox from '@/components/form/Checkbox';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Card,
|
||||||
|
Checkbox,
|
||||||
|
CruResource,
|
||||||
|
Loading,
|
||||||
|
RadioGroup,
|
||||||
|
SelectPrincipal
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [CreateEditView],
|
||||||
|
async fetch() {
|
||||||
|
await this.$store.dispatch('management/findAll', { type: MANAGEMENT.USER });
|
||||||
|
this.roleTemplates = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.ROLE_TEMPLATE });
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
customPermissions: [
|
||||||
|
{
|
||||||
|
label: this.t('members.clusterPermissions.createProjects'),
|
||||||
|
key: 'projects-create',
|
||||||
|
value: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.t('members.clusterPermissions.manageClusterBackups'),
|
||||||
|
key: 'backups-manage',
|
||||||
|
value: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.t('members.clusterPermissions.manageClusterCatalogs'),
|
||||||
|
key: 'clustercatalogs-manage',
|
||||||
|
value: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.t('members.clusterPermissions.manageClusterMembers'),
|
||||||
|
key: 'clusterroletemplatebindings-manage',
|
||||||
|
value: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.t('members.clusterPermissions.manageNodes'),
|
||||||
|
key: 'nodes-manage',
|
||||||
|
value: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.t('members.clusterPermissions.manageStorage'),
|
||||||
|
key: 'storage-manage',
|
||||||
|
value: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.t('members.clusterPermissions.viewAllProjects'),
|
||||||
|
key: 'projects-view',
|
||||||
|
value: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.t('members.clusterPermissions.viewClusterCatalogs'),
|
||||||
|
key: 'clustercatalogs-view',
|
||||||
|
value: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.t('members.clusterPermissions.viewClusterMembers'),
|
||||||
|
key: 'clusterroletemplatebindings-view',
|
||||||
|
value: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.t('members.clusterPermissions.viewNodes'),
|
||||||
|
key: 'nodes-view',
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
permissionGroup: 'member',
|
||||||
|
custom: {},
|
||||||
|
roleTemplates: [],
|
||||||
|
userPrincipalId: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
customRoles() {
|
||||||
|
return this.roleTemplates
|
||||||
|
.filter((role) => {
|
||||||
|
return !role.builtin && !role.external && !role.hidden && role.context === 'cluster';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
doneLocationOverride() {
|
||||||
|
return this.value.listLocation;
|
||||||
|
},
|
||||||
|
roleTemplateIds() {
|
||||||
|
if (this.permissionGroup === 'owner') {
|
||||||
|
return ['cluster-owner'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.permissionGroup === 'member') {
|
||||||
|
return ['cluster-member'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.permissionGroup === 'custom') {
|
||||||
|
return this.customPermissions
|
||||||
|
.filter(permission => permission.value)
|
||||||
|
.map(permission => permission.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [this.permissionGroup];
|
||||||
|
},
|
||||||
|
options() {
|
||||||
|
const customRoles = this.customRoles.map(role => ({
|
||||||
|
label: role.nameDisplay,
|
||||||
|
description: role.description || this.t('members.clusterPermissions.noDescription'),
|
||||||
|
value: role.id
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: this.t('members.clusterPermissions.owner.label'),
|
||||||
|
description: this.t('members.clusterPermissions.owner.description'),
|
||||||
|
value: 'owner'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.t('members.clusterPermissions.member.label'),
|
||||||
|
description: this.t('members.clusterPermissions.member.description'),
|
||||||
|
value: 'member'
|
||||||
|
},
|
||||||
|
...customRoles,
|
||||||
|
{
|
||||||
|
label: this.t('members.clusterPermissions.custom.label'),
|
||||||
|
description: this.t('members.clusterPermissions.custom.description'),
|
||||||
|
value: 'custom'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onAdd(userId) {
|
||||||
|
this.$set(this, 'userPrincipalId', userId);
|
||||||
|
},
|
||||||
|
async saveOverride() {
|
||||||
|
const asyncBindings = this.roleTemplateIds.map(roleTemplateId => this.$store.dispatch(`rancher/create`, {
|
||||||
|
type: NORMAN.CLUSTER_ROLE_TEMPLATE_BINDING,
|
||||||
|
clusterId: this.$store.getters['currentCluster'].id,
|
||||||
|
roleTemplateId,
|
||||||
|
userPrincipalId: this.userPrincipalId
|
||||||
|
}));
|
||||||
|
|
||||||
|
const bindings = await Promise.all(asyncBindings);
|
||||||
|
|
||||||
|
await Promise.all(bindings.map(binding => binding.save()));
|
||||||
|
await this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING, opt: { force: true } });
|
||||||
|
|
||||||
|
this.$router.replace(this.value.listLocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Loading v-if="$fetchState.pending" />
|
||||||
|
<CruResource
|
||||||
|
v-else
|
||||||
|
class="cluster-role-template-binding"
|
||||||
|
:errors="errors"
|
||||||
|
:mode="mode"
|
||||||
|
:resource="value"
|
||||||
|
:subtypes="[]"
|
||||||
|
:can-yaml="false"
|
||||||
|
:validation-passed="!!userPrincipalId"
|
||||||
|
@error="e=>errors = e"
|
||||||
|
@finish="saveOverride"
|
||||||
|
@cancel="done"
|
||||||
|
>
|
||||||
|
<div class="row m-10">
|
||||||
|
<div class="col span-12">
|
||||||
|
<SelectPrincipal class="mb-20" :mode="mode" :retain-selection="true" @add="onAdd" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Card :show-highlight-border="false" :show-actions="false">
|
||||||
|
<template v-slot:title>
|
||||||
|
<div class="type-title">
|
||||||
|
<h3>{{ t('members.clusterPermissions.label') }}</h3>
|
||||||
|
<div class="type-description">
|
||||||
|
{{ t('members.clusterPermissions.description') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-slot:body>
|
||||||
|
<RadioGroup
|
||||||
|
v-model="permissionGroup"
|
||||||
|
:options="options"
|
||||||
|
name="permission-group"
|
||||||
|
/>
|
||||||
|
<div v-if="permissionGroup === 'custom'" class="custom-permissions ml-20 mt-10">
|
||||||
|
<Checkbox v-for="permission in customPermissions" :key="permission.key" v-model="permission.value" class="mb-5" :label="permission.label" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Card>
|
||||||
|
</CruResource>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$detailSize: 11px;
|
||||||
|
|
||||||
|
::v-deep .type-description {
|
||||||
|
font-size: $detailSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
label.radio {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-permissions {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,6 +1,41 @@
|
||||||
|
import { _CREATE } from '@/config/query-params';
|
||||||
import { MANAGEMENT } from '@/config/types';
|
import { MANAGEMENT } from '@/config/types';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
detailPageHeaderActionOverride() {
|
||||||
|
return (realMode) => {
|
||||||
|
if (realMode === _CREATE) {
|
||||||
|
return this.t('members.createActionLabel');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
canCustomEdit() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
canYaml() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
canClone() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
user() {
|
||||||
|
return this.$rootGetters['management/byId'](MANAGEMENT.USER, this.userName);
|
||||||
|
},
|
||||||
|
|
||||||
|
nameDisplay() {
|
||||||
|
return this.user?.nameDisplay;
|
||||||
|
},
|
||||||
|
|
||||||
|
roleDisplay() {
|
||||||
|
return this.roleTemplate.nameDisplay;
|
||||||
|
},
|
||||||
|
|
||||||
|
roleDescription() {
|
||||||
|
return this.roleTemplate.description;
|
||||||
|
},
|
||||||
|
|
||||||
roleTemplate() {
|
roleTemplate() {
|
||||||
return this.$rootGetters['management/byId'](MANAGEMENT.ROLE_TEMPLATE, this.roleTemplateName);
|
return this.$rootGetters['management/byId'](MANAGEMENT.ROLE_TEMPLATE, this.roleTemplateName);
|
||||||
|
|
@ -29,4 +64,20 @@ export default {
|
||||||
|
|
||||||
return { name, params };
|
return { name, params };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
listLocation() {
|
||||||
|
return { name: 'c-cluster-explorer-members' };
|
||||||
|
},
|
||||||
|
|
||||||
|
doneOverride() {
|
||||||
|
return this.listLocation;
|
||||||
|
},
|
||||||
|
|
||||||
|
parentLocationOverride() {
|
||||||
|
return this.listLocation;
|
||||||
|
},
|
||||||
|
|
||||||
|
subSearch() {
|
||||||
|
return [{ nameDisplay: this.nameDisplay }];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,12 @@ export default {
|
||||||
return !!(this.principalIds || []).find(p => p === currentPrincipal);
|
return !!(this.principalIds || []).find(p => p === currentPrincipal);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
principals() {
|
||||||
|
return this.principalIds
|
||||||
|
.map(id => this.$rootGetters['rancher/byId'](NORMAN.PRINCIPAL, id))
|
||||||
|
.filter(p => p);
|
||||||
|
},
|
||||||
|
|
||||||
nameDisplay() {
|
nameDisplay() {
|
||||||
return this.displayName || this.username || this.id;
|
return this.displayName || this.username || this.id;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
projectNameSort() {
|
projectNameSort() {
|
||||||
return this.project?.nameSort || '}';
|
return this.project?.nameSort || '';
|
||||||
},
|
},
|
||||||
|
|
||||||
istioInstalled() {
|
istioInstalled() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
<script>
|
||||||
|
import { MANAGEMENT } from '@/config/types';
|
||||||
|
import ResourceTable from '@/components/ResourceTable';
|
||||||
|
import Loading from '@/components/Loading';
|
||||||
|
import { NAME } from '@/config/product/explorer';
|
||||||
|
import Masthead from '@/components/ResourceList/Masthead';
|
||||||
|
import { AGE, ROLE, STATE, PRINCIPAL } from '@/config/table-headers';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Loading, Masthead, ResourceTable
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetch() {
|
||||||
|
const clusterRoleTemplateBindingSchema = this.$store.getters[`management/schemaFor`](MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING);
|
||||||
|
|
||||||
|
const hydration = [
|
||||||
|
this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.USER }),
|
||||||
|
this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.ROLE_TEMPLATE })
|
||||||
|
];
|
||||||
|
const clusterRoleTemplateBindings = clusterRoleTemplateBindingSchema ? await this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING }) : [];
|
||||||
|
|
||||||
|
await Promise.all(hydration);
|
||||||
|
this.$set(this, 'clusterRoleTemplateBindings', clusterRoleTemplateBindings);
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
schema: this.$store.getters[`management/schemaFor`](MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING),
|
||||||
|
headers: [
|
||||||
|
STATE,
|
||||||
|
PRINCIPAL,
|
||||||
|
ROLE,
|
||||||
|
AGE
|
||||||
|
],
|
||||||
|
createLocation: {
|
||||||
|
name: 'c-cluster-product-resource-create',
|
||||||
|
params: {
|
||||||
|
product: NAME,
|
||||||
|
resource: MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resource: MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING,
|
||||||
|
clusterRoleTemplateBindings: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
filteredClusterRoleTemplateBindings() {
|
||||||
|
return this.clusterRoleTemplateBindings
|
||||||
|
.filter(b => !b.user?.isSystem && b.clusterName === this.$store.getters['currentCluster'].id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Loading v-if="$fetchState.pending" />
|
||||||
|
<div v-else>
|
||||||
|
<Masthead
|
||||||
|
:schema="schema"
|
||||||
|
:resource="resource"
|
||||||
|
:create-location="createLocation"
|
||||||
|
:create-button-label="t('members.createActionLabel')"
|
||||||
|
/>
|
||||||
|
<ResourceTable
|
||||||
|
:schema="schema"
|
||||||
|
:headers="headers"
|
||||||
|
:rows="filteredClusterRoleTemplateBindings"
|
||||||
|
:groupable="false"
|
||||||
|
sub-search="subSearch"
|
||||||
|
:sub-fields="['nameDisplay']"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -684,7 +684,7 @@ export default {
|
||||||
action: (this.canCustomEdit ? 'goToClone' : 'cloneYaml'),
|
action: (this.canCustomEdit ? 'goToClone' : 'cloneYaml'),
|
||||||
label: this.t('action.clone'),
|
label: this.t('action.clone'),
|
||||||
icon: 'icon icon-copy',
|
icon: 'icon icon-copy',
|
||||||
enabled: this.canCreate && (this.canCustomEdit || this.canYaml),
|
enabled: this.canClone && this.canCreate && (this.canCustomEdit || this.canYaml),
|
||||||
},
|
},
|
||||||
{ divider: true },
|
{ divider: true },
|
||||||
{
|
{
|
||||||
|
|
@ -717,6 +717,10 @@ export default {
|
||||||
return this.hasLink('remove') && this.$rootGetters['type-map/optionsFor'](this.type).isRemovable;
|
return this.hasLink('remove') && this.$rootGetters['type-map/optionsFor'](this.type).isRemovable;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
canClone() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
canUpdate() {
|
canUpdate() {
|
||||||
return this.hasLink('update') && this.$rootGetters['type-map/optionsFor'](this.type).isEditable;
|
return this.hasLink('update') && this.$rootGetters['type-map/optionsFor'](this.type).isEditable;
|
||||||
},
|
},
|
||||||
|
|
@ -839,7 +843,7 @@ export default {
|
||||||
const schema = this.$getters['schemaFor'](this.type);
|
const schema = this.$getters['schemaFor'](this.type);
|
||||||
let url = schema.linkFor('collection');
|
let url = schema.linkFor('collection');
|
||||||
|
|
||||||
if ( schema.attributes && schema.attributes.namespaced ) {
|
if ( schema.attributes && schema.attributes.namespaced && this.metadata && this.metadata.namespace ) {
|
||||||
url += `/${ this.metadata.namespace }`;
|
url += `/${ this.metadata.namespace }`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue