fix(kubeconfig): show kubeconfig download button for non admin users [EE-2123] (#6204)

Co-authored-by: Simon Meng <simon.meng@portainer.io>
pull/6233/head
Marcelo Rydel 2021-12-06 18:40:59 -03:00 committed by GitHub
parent 98972dec0d
commit 2a1b8efaed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 21 additions and 25 deletions

View File

@ -6,8 +6,6 @@ import (
"net/http"
"strings"
clientV1 "k8s.io/client-go/tools/clientcmd/api/v1"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
@ -15,6 +13,7 @@ import (
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/internal/endpointutils"
kcli "github.com/portainer/portainer/api/kubernetes/cli"
clientV1 "k8s.io/client-go/tools/clientcmd/api/v1"
)
// @id GetKubernetesConfig
@ -124,21 +123,14 @@ func (handler *Handler) buildConfig(r *http.Request, tokenData *portainer.TokenD
authInfosSet := make(map[string]bool)
for idx, endpoint := range endpoints {
cli, err := handler.kubernetesClientFactory.GetKubeClient(&endpoint)
if err != nil {
return nil, &httperror.HandlerError{http.StatusInternalServerError, "Unable to create Kubernetes client", err}
}
serviceAccount, err := cli.GetServiceAccount(tokenData)
if err != nil {
return nil, &httperror.HandlerError{http.StatusInternalServerError, fmt.Sprintf("unable to find serviceaccount associated with user; username=%s", tokenData.Username), err}
}
instanceID := handler.kubernetesClientFactory.GetInstanceID()
serviceAccountName := kcli.UserServiceAccountName(int(tokenData.ID), instanceID)
configClusters[idx] = buildCluster(r, handler.BaseURL, endpoint)
configContexts[idx] = buildContext(serviceAccount.Name, endpoint)
if !authInfosSet[serviceAccount.Name] {
configAuthInfos = append(configAuthInfos, buildAuthInfo(serviceAccount.Name, bearerToken))
authInfosSet[serviceAccount.Name] = true
configContexts[idx] = buildContext(serviceAccountName, endpoint)
if !authInfosSet[serviceAccountName] {
configAuthInfos = append(configAuthInfos, buildAuthInfo(serviceAccountName, bearerToken))
authInfosSet[serviceAccountName] = true
}
}

View File

@ -44,6 +44,10 @@ func NewClientFactory(signatureService portainer.DigitalSignatureService, revers
}
}
func (factory *ClientFactory) GetInstanceID() (instanceID string) {
return factory.instanceID
}
// Remove the cached kube client so a new one can be created
func (factory *ClientFactory) RemoveKubeClient(endpointID portainer.EndpointID) {
factory.endpointClients.Remove(strconv.Itoa(int(endpointID)))

View File

@ -17,7 +17,7 @@ const (
portainerShellPodPrefix = "portainer-pod-kubectl-shell"
)
func userServiceAccountName(userID int, instanceID string) string {
func UserServiceAccountName(userID int, instanceID string) string {
return fmt.Sprintf("%s-%s-%d", portainerUserServiceAccountPrefix, instanceID, userID)
}

View File

@ -16,7 +16,7 @@ func (kcl *KubeClient) GetServiceAccount(tokenData *portainer.TokenData) (*v1.Se
if tokenData.Role == portainer.AdministratorRole {
portainerServiceAccountName = portainerClusterAdminServiceAccountName
} else {
portainerServiceAccountName = userServiceAccountName(int(tokenData.ID), kcl.instanceID)
portainerServiceAccountName = UserServiceAccountName(int(tokenData.ID), kcl.instanceID)
}
// verify name exists as service account resource within portainer namespace
@ -30,7 +30,7 @@ func (kcl *KubeClient) GetServiceAccount(tokenData *portainer.TokenData) (*v1.Se
// GetServiceAccountBearerToken returns the ServiceAccountToken associated to the specified user.
func (kcl *KubeClient) GetServiceAccountBearerToken(userID int) (string, error) {
serviceAccountName := userServiceAccountName(userID, kcl.instanceID)
serviceAccountName := UserServiceAccountName(userID, kcl.instanceID)
return kcl.getServiceAccountToken(serviceAccountName)
}
@ -39,7 +39,7 @@ func (kcl *KubeClient) GetServiceAccountBearerToken(userID int) (string, error)
// cluster before creating a ServiceAccount and a ServiceAccountToken for the specified Portainer user.
//It will also create required default RoleBinding and ClusterRoleBinding rules.
func (kcl *KubeClient) SetupUserServiceAccount(userID int, teamIDs []int, restrictDefaultNamespace bool) error {
serviceAccountName := userServiceAccountName(userID, kcl.instanceID)
serviceAccountName := UserServiceAccountName(userID, kcl.instanceID)
err := kcl.ensureRequiredResourcesExist()
if err != nil {

View File

@ -67,7 +67,7 @@ func Test_GetServiceAccount(t *testing.T) {
ID: 1,
Role: portainer.StandardUserRole,
}
serviceAccountName := userServiceAccountName(int(tokenData.ID), k.instanceID)
serviceAccountName := UserServiceAccountName(int(tokenData.ID), k.instanceID)
serviceAccount := &v1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: serviceAccountName,

View File

@ -5,11 +5,11 @@
<div class="toolBarTitle"> <i class="fa" ng-class="$ctrl.titleIcon" aria-hidden="true" style="margin-right: 2px;"></i> {{ $ctrl.titleText }} </div>
</div>
<div class="actionBar" ng-if="$ctrl.showSnapshotAction">
<div style="margin-bottom: 10px;" ng-if="$ctrl.endpoints.length"
><i class="fa fa-exclamation-circle blue-icon" style="margin-right: 5px;"></i>Click on an environment to manage</div
>
<button type="button" class="btn btn-sm btn-primary" ng-click="$ctrl.snapshotAction()" data-cy="home-refreshEndpointsButton">
<div class="actionBar" ng-if="$ctrl.showSnapshotAction || $ctrl.showKubeconfigButton()">
<div style="margin-bottom: 10px;" ng-if="$ctrl.endpoints.length">
<i class="fa fa-exclamation-circle blue-icon" style="margin-right: 5px;"></i>Click on an environment to manage
</div>
<button type="button" ng-if="$ctrl.showSnapshotAction" class="btn btn-sm btn-primary" ng-click="$ctrl.snapshotAction()" data-cy="home-refreshEndpointsButton">
<i class="fa fa-sync space-right" aria-hidden="true"></i>Refresh
</button>
<button