mirror of https://github.com/portainer/portainer
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
parent
98972dec0d
commit
2a1b8efaed
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue