1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-02 20:35:25 +02:00

refactor(namespace): migrate namespace access view to react [r8s-141] (#87)

This commit is contained in:
Ali 2024-11-11 08:17:20 +13:00 committed by GitHub
parent 8ed7cd80cb
commit e9fc6d5598
62 changed files with 1018 additions and 610 deletions

View file

@ -1,116 +0,0 @@
<page-header
ng-if="ctrl.state.viewReady"
title="'Namespace access management'"
breadcrumbs="[
{ label:'Namespaces', link:'kubernetes.resourcePools' },
{
label:ctrl.pool.Namespace.Name,
link: 'kubernetes.resourcePools.resourcePool',
linkParams:{id: ctrl.pool.Namespace.Name}
},
'Access management'
]"
reload="true"
>
</page-header>
<kubernetes-view-loading view-ready="ctrl.state.viewReady"></kubernetes-view-loading>
<div ng-if="ctrl.state.viewReady">
<div class="row" ng-if="ctrl.pool">
<div class="col-sm-12">
<rd-widget>
<rd-widget-header icon="layers" title-text="Namespace"></rd-widget-header>
<rd-widget-body>
<table class="table">
<tbody>
<tr>
<td>Name</td>
<td>
{{ ctrl.pool.Namespace.Name }}
</td>
</tr>
</tbody>
</table>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<rd-widget ng-if="ctrl.availableUsersAndTeams">
<rd-widget-header icon="user-x" title-text="Create access"></rd-widget-header>
<rd-widget-body>
<form class="form-horizontal">
<div class="form-group">
<div
ng-if="!ctrl.isRBACEnabled"
class="small mx-[15px] mb-6 flex gap-1 rounded-lg border border-solid border-warning-5 bg-warning-2 p-4 text-warning-8 th-highcontrast:bg-yellow-11 th-highcontrast:text-white th-dark:bg-yellow-11 th-dark:text-white"
>
<div class="mt-0.5">
<pr-icon icon="'alert-triangle'" feather="true" class-name="'text-warning-7 th-dark:text-white th-highcontrast:text-white'"></pr-icon>
</div>
<div>
<p> Your cluster does not have Kubernetes role-based access control (RBAC) enabled. </p>
<p> This means you can't use Portainer RBAC functionality to regulate access to environment resources based on user roles. </p>
<p class="mb-0">
To enable RBAC, start the&nbsp;<a
class="th-highcontrast:text-blue-4 th-dark:text-blue-7"
href="https://kubernetes.io/docs/concepts/overview/components/#kube-apiserver"
target="_blank"
>API server</a
>&nbsp;with the&nbsp;<code class="bg-gray-4 box-decoration-clone th-highcontrast:bg-black th-dark:bg-black">--authorization-mode</code>&nbsp;flag set to a
comma-separated list that includes&nbsp;<code class="bg-gray-4 th-highcontrast:bg-black th-dark:bg-black">RBAC</code>, for example:&nbsp;
<code class="bg-gray-4 box-decoration-clone th-highcontrast:bg-black th-dark:bg-black">kube-apiserver --authorization-mode=Example1,RBAC,Example2</code>.
</p>
</div>
</div>
<span class="col-sm-12 small text-warning">
<p class="vertical-center">
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon>
Adding user access will require the affected user(s) to logout and login for the changes to be taken into account.
</p>
</span>
</div>
<div class="form-group">
<label class="col-sm-3 col-lg-2 control-label text-left" for="users-selector"> Select user(s) and/or team(s) </label>
<div class="col-sm-9 col-lg-4">
<span class="small text-muted" ng-if="ctrl.availableUsersAndTeams.length === 0">
No user nor team access has been set on the environment. Head over to the
<a ui-sref="portainer.endpoints">Environments view</a> to manage them.
</span>
<namespace-access-users-selector
ng-if="ctrl.availableUsersAndTeams.length > 0"
input-id="users-selector"
value="ctrl.formValues.multiselectOutput"
options="ctrl.availableUsersAndTeams"
on-change="(ctrl.onUsersAndTeamsChange)"
></namespace-access-users-selector>
</div>
</div>
<!-- actions -->
<div class="form-group">
<div class="col-sm-12">
<button
type="submit"
class="btn btn-primary btn-sm vertical-center !ml-0"
ng-disabled="ctrl.formValues.multiselectOutput.length === 0 || ctrl.actionInProgress"
ng-click="ctrl.authorizeAccess()"
button-spinner="ctrl.actionInProgress"
>
<span class="vertical-center" ng-hide="ctrl.state.actionInProgress"><pr-icon icon="'plus'" class="vertical-center"></pr-icon> Create access</span>
<span ng-show="ctrl.state.actionInProgress">Creating access...</span>
</button>
</div>
</div>
<!-- !actions -->
</form>
</rd-widget-body>
</rd-widget>
</div>
</div>
<namespace-access-datatable ng-if="ctrl.authorizedUsersAndTeams" dataset="ctrl.authorizedUsersAndTeams" on-remove="(ctrl.unauthorizeAccess)"> </namespace-access-datatable>
</div>

View file

@ -1,9 +0,0 @@
angular.module('portainer.kubernetes').component('kubernetesResourcePoolAccessView', {
templateUrl: './resourcePoolAccess.html',
controller: 'KubernetesResourcePoolAccessController',
controllerAs: 'ctrl',
bindings: {
$transition$: '<',
endpoint: '<',
},
});

View file

@ -1,145 +0,0 @@
import angular from 'angular';
import _ from 'lodash-es';
import { KubernetesPortainerConfigMapConfigName, KubernetesPortainerConfigMapNamespace, KubernetesPortainerConfigMapAccessKey } from 'Kubernetes/models/config-map/models';
import { UserAccessViewModel, TeamAccessViewModel } from 'Portainer/models/access';
import KubernetesConfigMapHelper from 'Kubernetes/helpers/configMapHelper';
import { getIsRBACEnabled } from '@/react/kubernetes/cluster/getIsRBACEnabled';
class KubernetesResourcePoolAccessController {
/* @ngInject */
constructor($async, $state, $scope, Notifications, KubernetesResourcePoolService, KubernetesConfigMapService, GroupService, AccessService, EndpointProvider) {
this.$async = $async;
this.$state = $state;
this.$scope = $scope;
this.EndpointProvider = EndpointProvider;
this.Notifications = Notifications;
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.KubernetesConfigMapService = KubernetesConfigMapService;
this.GroupService = GroupService;
this.AccessService = AccessService;
this.onInit = this.onInit.bind(this);
this.authorizeAccessAsync = this.authorizeAccessAsync.bind(this);
this.unauthorizeAccessAsync = this.unauthorizeAccessAsync.bind(this);
this.onUsersAndTeamsChange = this.onUsersAndTeamsChange.bind(this);
this.unauthorizeAccess = this.unauthorizeAccess.bind(this);
}
initAccessConfigMap(configMap) {
configMap.Name = KubernetesPortainerConfigMapConfigName;
configMap.Namespace = KubernetesPortainerConfigMapNamespace;
configMap.Data[KubernetesPortainerConfigMapAccessKey] = {};
return configMap;
}
/**
* Init
*/
async onInit() {
const endpoint = this.endpoint;
this.state = {
actionInProgress: false,
viewReady: false,
};
this.formValues = {
multiselectOutput: [],
};
// default to true if error is thrown
this.isRBACEnabled = true;
try {
const name = this.$transition$.params().id;
let [pool, configMap] = await Promise.all([
this.KubernetesResourcePoolService.get(name),
this.KubernetesConfigMapService.getAccess(KubernetesPortainerConfigMapNamespace, KubernetesPortainerConfigMapConfigName),
]);
this.isRBACEnabled = await getIsRBACEnabled(this.EndpointProvider.endpointID());
const group = await this.GroupService.group(endpoint.GroupId);
const roles = [];
const endpointAccesses = await this.AccessService.accesses(endpoint, group, roles);
this.pool = pool;
if (configMap.Id === 0) {
configMap = this.initAccessConfigMap(configMap);
}
configMap = KubernetesConfigMapHelper.parseJSONData(configMap);
this.authorizedUsersAndTeams = [];
this.accessConfigMap = configMap;
const poolAccesses = configMap.Data[KubernetesPortainerConfigMapAccessKey][name];
if (poolAccesses) {
this.authorizedUsersAndTeams = _.filter(endpointAccesses.authorizedUsersAndTeams, (item) => {
if (item instanceof UserAccessViewModel && poolAccesses.UserAccessPolicies) {
return poolAccesses.UserAccessPolicies[item.Id] !== undefined;
} else if (item instanceof TeamAccessViewModel && poolAccesses.TeamAccessPolicies) {
return poolAccesses.TeamAccessPolicies[item.Id] !== undefined;
}
return false;
});
}
this.availableUsersAndTeams = _.without(endpointAccesses.authorizedUsersAndTeams, ...this.authorizedUsersAndTeams);
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve namespace information');
} finally {
this.state.viewReady = true;
}
}
$onInit() {
return this.$async(this.onInit);
}
/**
* Authorize access
*/
async authorizeAccessAsync() {
try {
this.state.actionInProgress = true;
const newAccesses = _.concat(this.authorizedUsersAndTeams, this.formValues.multiselectOutput);
const accessConfigMap = KubernetesConfigMapHelper.modifiyNamespaceAccesses(angular.copy(this.accessConfigMap), this.pool.Namespace.Name, newAccesses);
await this.KubernetesConfigMapService.updateAccess(accessConfigMap);
this.Notifications.success('Success', 'Access successfully created');
this.$state.reload(this.$state.current);
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to create accesses');
}
}
onUsersAndTeamsChange(value) {
this.$scope.$evalAsync(() => {
this.formValues.multiselectOutput = value;
});
}
authorizeAccess() {
return this.$async(this.authorizeAccessAsync);
}
/**
*
*/
async unauthorizeAccessAsync(selectedItems) {
try {
this.state.actionInProgress = true;
const newAccesses = _.without(this.authorizedUsersAndTeams, ...selectedItems);
const accessConfigMap = KubernetesConfigMapHelper.modifiyNamespaceAccesses(angular.copy(this.accessConfigMap), this.pool.Namespace.Name, newAccesses);
await this.KubernetesConfigMapService.updateAccess(accessConfigMap);
this.Notifications.success('Success', 'Access successfully removed');
this.$state.reload(this.$state.current);
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to remove accesses');
} finally {
this.state.actionInProgress = false;
}
}
unauthorizeAccess(selectedItems) {
return this.$async(this.unauthorizeAccessAsync, selectedItems);
}
}
export default KubernetesResourcePoolAccessController;
angular.module('portainer.kubernetes').controller('KubernetesResourcePoolAccessController', KubernetesResourcePoolAccessController);