209 lines
6.3 KiB
Go
209 lines
6.3 KiB
Go
/*
|
|
* Please refer to the LICENSE file in the root directory of the project.
|
|
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
|
|
*/
|
|
|
|
package v1alpha1
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
|
|
"k8s.io/apiserver/pkg/authentication/user"
|
|
tenantv1beta1 "kubesphere.io/api/tenant/v1beta1"
|
|
|
|
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
|
|
|
|
"k8s.io/klog/v2"
|
|
|
|
"kubesphere.io/kubesphere/pkg/apiserver/request"
|
|
"kubesphere.io/kubesphere/pkg/utils/stringutils"
|
|
|
|
"github.com/emicklei/go-restful/v3"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
|
|
|
"kubesphere.io/kubesphere/pkg/api"
|
|
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
|
"kubesphere.io/kubesphere/pkg/server/errors"
|
|
k8suitl "kubesphere.io/kubesphere/pkg/utils/k8sutil"
|
|
)
|
|
|
|
func (h *templateHandler) listWorkloadTemplate(req *restful.Request, resp *restful.Response) {
|
|
secretList := corev1.SecretList{}
|
|
requirements, _ := labels.SelectorFromSet(map[string]string{SchemeGroupVersion.Group: "true"}).Requirements()
|
|
userSelector := query.ParseQueryParameter(req).Selector()
|
|
combinedSelector := labels.NewSelector()
|
|
combinedSelector = combinedSelector.Add(requirements...)
|
|
if userSelector != nil {
|
|
userRequirements, _ := userSelector.Requirements()
|
|
combinedSelector = combinedSelector.Add(userRequirements...)
|
|
}
|
|
opts := []client.ListOption{
|
|
client.MatchingLabelsSelector{Selector: combinedSelector},
|
|
}
|
|
namespace := req.PathParameter("namespace")
|
|
if namespace != "" {
|
|
opts = append(opts, client.InNamespace(namespace))
|
|
}
|
|
err := h.client.List(req.Request.Context(), &secretList, opts...)
|
|
if err != nil {
|
|
api.HandleError(resp, req, err)
|
|
return
|
|
}
|
|
workspace := req.PathParameter("workspace")
|
|
if workspace == "" {
|
|
resp.WriteEntity(k8suitl.ConvertToListResult(&secretList, req))
|
|
return
|
|
}
|
|
|
|
user, ok := request.UserFrom(req.Request.Context())
|
|
if !ok {
|
|
err := fmt.Errorf("cannot obtain user info")
|
|
klog.Errorln(err)
|
|
api.HandleForbidden(resp, nil, err)
|
|
return
|
|
}
|
|
|
|
filteredList, err := h.FilterByPermissions(workspace, user, secretList)
|
|
if err != nil {
|
|
api.HandleError(resp, req, err)
|
|
return
|
|
}
|
|
|
|
resp.WriteEntity(k8suitl.ConvertToListResult(filteredList, req))
|
|
}
|
|
|
|
func (h *templateHandler) FilterByPermissions(workspace string, user user.Info, secretList corev1.SecretList) (*corev1.SecretList, error) {
|
|
|
|
listNS := authorizer.AttributesRecord{
|
|
User: user,
|
|
Verb: "list",
|
|
Workspace: workspace,
|
|
Resource: "namespaces",
|
|
ResourceRequest: true,
|
|
ResourceScope: request.WorkspaceScope,
|
|
}
|
|
|
|
decision, _, err := h.authorizer.Authorize(listNS)
|
|
if err != nil {
|
|
klog.Error(err)
|
|
return nil, err
|
|
}
|
|
var namespaceList []string
|
|
if decision == authorizer.DecisionAllow {
|
|
queryParam := query.New()
|
|
queryParam.Filters[query.FieldLabel] = query.Value(fmt.Sprintf("%s=%s", tenantv1beta1.WorkspaceLabel, workspace))
|
|
result, err := h.resourceGetter.List("namespaces", "", queryParam)
|
|
if err != nil {
|
|
klog.Error(err)
|
|
return nil, err
|
|
}
|
|
|
|
for _, item := range result.Items {
|
|
ns := item.(*corev1.Namespace)
|
|
listWorkLoadTemplate := authorizer.AttributesRecord{
|
|
User: user,
|
|
Verb: "list",
|
|
Namespace: ns.Name,
|
|
Resource: "workloadtemplates",
|
|
ResourceRequest: true,
|
|
ResourceScope: request.NamespaceScope,
|
|
}
|
|
decision, _, err = h.authorizer.Authorize(listWorkLoadTemplate)
|
|
if err != nil {
|
|
klog.Error(err)
|
|
return nil, err
|
|
}
|
|
if decision == authorizer.DecisionAllow {
|
|
namespaceList = append(namespaceList, ns.Name)
|
|
} else {
|
|
klog.Infof("user %s has no permission to list workloadtemplate in namespace %s", user.GetName(), ns.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
filteredList := &corev1.SecretList{}
|
|
for _, item := range secretList.Items {
|
|
if !stringutils.StringIn(item.Namespace, namespaceList) {
|
|
continue
|
|
}
|
|
filteredList.Items = append(filteredList.Items, item)
|
|
}
|
|
return filteredList, nil
|
|
}
|
|
|
|
func (h *templateHandler) applyWorkloadTemplate(req *restful.Request, resp *restful.Response) {
|
|
secret := &corev1.Secret{}
|
|
err := req.ReadEntity(secret)
|
|
if err != nil {
|
|
api.HandleError(resp, req, err)
|
|
return
|
|
}
|
|
if req.PathParameter("workloadtemplate") == "" {
|
|
// create new
|
|
ns := req.PathParameter("namespace")
|
|
err = h.client.Get(req.Request.Context(), runtimeclient.ObjectKey{Name: secret.Name, Namespace: ns}, secret)
|
|
if err != nil && !apierrors.IsNotFound(err) {
|
|
api.HandleError(resp, req, err)
|
|
return
|
|
}
|
|
if err == nil {
|
|
api.HandleConflict(resp, req, fmt.Errorf("workloadtemplate %s already exists", secret.Name))
|
|
return
|
|
}
|
|
}
|
|
|
|
newSecret := &corev1.Secret{}
|
|
newSecret.Name = secret.Name
|
|
newSecret.Namespace = req.PathParameter("namespace")
|
|
mutateFn := func() error {
|
|
newSecret.Annotations = secret.Annotations
|
|
if secret.Labels == nil {
|
|
secret.Labels = make(map[string]string)
|
|
}
|
|
newSecret.Labels = secret.Labels
|
|
newSecret.Labels[SchemeGroupVersion.Group] = "true"
|
|
newSecret.StringData = secret.StringData
|
|
newSecret.Type = corev1.SecretType(fmt.Sprintf("%s/%s", SchemeGroupVersion.Group, "workloadtemplate"))
|
|
return nil
|
|
}
|
|
_, err = controllerutil.CreateOrUpdate(req.Request.Context(), h.client, newSecret, mutateFn)
|
|
if err != nil {
|
|
api.HandleError(resp, req, err)
|
|
return
|
|
}
|
|
newSecret.SetManagedFields(nil)
|
|
resp.WriteAsJson(newSecret)
|
|
}
|
|
|
|
func (h *templateHandler) deleteWorkloadTemplate(req *restful.Request, resp *restful.Response) {
|
|
name := req.PathParameter("workloadtemplate")
|
|
secret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: name}}
|
|
secret.Namespace = req.PathParameter("namespace")
|
|
err := h.client.Delete(req.Request.Context(), secret)
|
|
if err != nil {
|
|
api.HandleError(resp, req, err)
|
|
return
|
|
}
|
|
resp.WriteEntity(errors.None)
|
|
}
|
|
|
|
func (h *templateHandler) getWorkloadTemplate(req *restful.Request, resp *restful.Response) {
|
|
name := req.PathParameter("workloadtemplate")
|
|
secret := &corev1.Secret{}
|
|
ns := req.PathParameter("namespace")
|
|
err := h.client.Get(req.Request.Context(), runtimeclient.ObjectKey{Name: name, Namespace: ns}, secret)
|
|
if err != nil {
|
|
api.HandleError(resp, req, err)
|
|
return
|
|
}
|
|
secret.SetManagedFields(nil)
|
|
resp.WriteAsJson(secret)
|
|
}
|