提交 bda48c24 编写于 作者: H hongming

fix: devop IAM bugs

Signed-off-by: Nhongming <talonwan@yunify.com>
上级 afcd0efe
......@@ -122,7 +122,7 @@ func addControllers(
client.KubeSphere(), devopsClient,
informerFactory.KubernetesSharedInformerFactory().Core().V1().Namespaces(),
informerFactory.KubeSphereSharedInformerFactory().Devops().V1alpha3().DevOpsProjects(),
)
informerFactory.KubeSphereSharedInformerFactory().Tenant().V1alpha1().Workspaces())
devopsPipelineController = pipeline.NewController(client.Kubernetes(),
client.KubeSphere(),
......
......@@ -51,6 +51,9 @@ type Attributes interface {
// The namespace of the object, if a request is for a REST object.
GetNamespace() string
// The devops project of the object, if a request is for a REST object.
GetDevOps() string
// The kind of object, if a request is for a REST object.
GetResource() string
......@@ -109,6 +112,7 @@ type AttributesRecord struct {
Cluster string
Workspace string
Namespace string
DevOps string
APIGroup string
APIVersion string
Resource string
......@@ -144,6 +148,10 @@ func (a AttributesRecord) GetNamespace() string {
return a.Namespace
}
func (a AttributesRecord) GetDevOps() string {
return a.DevOps
}
func (a AttributesRecord) GetResource() string {
return a.Resource
}
......
......@@ -234,11 +234,21 @@ func (r *RBACAuthorizer) visitRulesFor(requestAttributes authorizer.Attributes,
}
}
if requestAttributes.GetResourceScope() == request.WorkspaceScope || requestAttributes.GetResourceScope() == request.NamespaceScope {
if requestAttributes.GetResourceScope() == request.WorkspaceScope ||
requestAttributes.GetResourceScope() == request.NamespaceScope ||
requestAttributes.GetResourceScope() == request.DevOpsScope {
var workspace string
var err error
// all of resource under namespace and devops belong to workspace
if requestAttributes.GetResourceScope() == request.NamespaceScope {
if workspace, err = r.am.GetControlledWorkspace(requestAttributes.GetNamespace()); err != nil {
if workspace, err = r.am.GetNamespaceControlledWorkspace(requestAttributes.GetNamespace()); err != nil {
if !visitor(nil, "", nil, err) {
return
}
}
} else if requestAttributes.GetResourceScope() == request.DevOpsScope {
if workspace, err = r.am.GetDevOpsControlledWorkspace(requestAttributes.GetDevOps()); err != nil {
if !visitor(nil, "", nil, err) {
return
}
......@@ -279,19 +289,33 @@ func (r *RBACAuthorizer) visitRulesFor(requestAttributes authorizer.Attributes,
}
}
if requestAttributes.GetResourceScope() == request.NamespaceScope {
if roleBindings, err := r.am.ListRoleBindings("", requestAttributes.GetNamespace()); err != nil {
if requestAttributes.GetResourceScope() == request.NamespaceScope ||
requestAttributes.GetResourceScope() == request.DevOpsScope {
namespace := requestAttributes.GetNamespace()
// list devops role binding
if requestAttributes.GetResourceScope() == request.DevOpsScope {
if relatedNamespace, err := r.am.GetDevOpsRelatedNamespace(requestAttributes.GetDevOps()); err != nil {
if !visitor(nil, "", nil, err) {
return
}
} else {
namespace = relatedNamespace
}
}
if roleBindings, err := r.am.ListRoleBindings("", namespace); err != nil {
if !visitor(nil, "", nil, err) {
return
}
} else {
sourceDescriber := &roleBindingDescriber{}
for _, roleBinding := range roleBindings {
subjectIndex, applies := appliesTo(requestAttributes.GetUser(), roleBinding.Subjects, requestAttributes.GetNamespace())
subjectIndex, applies := appliesTo(requestAttributes.GetUser(), roleBinding.Subjects, namespace)
if !applies {
continue
}
regoPolicy, rules, err := r.am.GetRoleReferenceRules(roleBinding.RoleRef, requestAttributes.GetNamespace())
regoPolicy, rules, err := r.am.GetRoleReferenceRules(roleBinding.RoleRef, namespace)
if err != nil {
visitor(nil, "", nil, err)
continue
......
......@@ -82,12 +82,12 @@ func getAuthorizerAttributes(ctx context.Context) (authorizer.Attributes, error)
attribs.Cluster = requestInfo.Cluster
attribs.Workspace = requestInfo.Workspace
attribs.KubernetesRequest = requestInfo.IsKubernetesRequest
attribs.APIGroup = requestInfo.APIGroup
attribs.APIVersion = requestInfo.APIVersion
attribs.Resource = requestInfo.Resource
attribs.Subresource = requestInfo.Subresource
attribs.Namespace = requestInfo.Namespace
attribs.DevOps = requestInfo.DevOps
attribs.Name = requestInfo.Name
return &attribs, nil
......
......@@ -69,6 +69,9 @@ type RequestInfo struct {
// Cluster of requested resource, this is empty in single-cluster environment
Cluster string
// DevOps project of requested resource
DevOps string
// Scope of requested resource.
ResourceScope string
}
......@@ -195,15 +198,6 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
}
}
selector := req.URL.Query().Get("labelSelector")
// URL forms: /api/v1/watch/namespaces?labelSelector=kubesphere.io/workspace=system-workspace
if strings.HasPrefix(selector, workspaceSelectorPrefix) {
workspace := strings.TrimPrefix(selector, workspaceSelectorPrefix)
// URL forms: /api/v1/watch/namespaces?labelSelector=kubesphere.io/workspace==system-workspace
workspace = strings.TrimPrefix(workspace, "=")
requestInfo.Workspace = workspace
}
// URL forms: /namespaces/{namespace}/{kind}/*, where parts are adjusted to be relative to kind
if currentParts[0] == "namespaces" {
if len(currentParts) > 1 {
......@@ -215,8 +209,19 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
currentParts = currentParts[2:]
}
}
} else if currentParts[0] == "devops" {
if len(currentParts) > 1 {
requestInfo.DevOps = currentParts[1]
// if there is another step after the devops name
// move currentParts to include it as a resource in its own right
if len(currentParts) > 2 {
currentParts = currentParts[2:]
}
}
} else {
requestInfo.Namespace = metav1.NamespaceNone
requestInfo.DevOps = metav1.NamespaceNone
}
// parsing successful, so we now know the proper value for .Parts
......@@ -260,6 +265,15 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
requestInfo.Verb = "list"
}
// URL forms: /api/v1/watch/namespaces?labelSelector=kubesphere.io/workspace=system-workspace
if requestInfo.Verb == "watch" {
selector := req.URL.Query().Get("labelSelector")
if strings.HasPrefix(selector, workspaceSelectorPrefix) {
workspace := strings.TrimPrefix(selector, workspaceSelectorPrefix)
requestInfo.Workspace = workspace
}
}
if opts.FieldSelector != nil {
if name, ok := opts.FieldSelector.RequiresExactMatch("metadata.name"); ok {
if len(path.IsValidPathSegmentName(name)) == 0 {
......@@ -306,6 +320,7 @@ const (
ClusterScope = "Cluster"
WorkspaceScope = "Workspace"
NamespaceScope = "Namespace"
DevOpsScope = "DevOps"
workspaceSelectorPrefix = constants.WorkspaceLabelKey + "="
)
......@@ -318,6 +333,10 @@ func (r *RequestInfoFactory) resolveResourceScope(request RequestInfo) string {
return NamespaceScope
}
if request.DevOps != "" {
return DevOpsScope
}
if request.Workspace != "" {
return WorkspaceScope
}
......
......@@ -18,6 +18,8 @@ import (
"k8s.io/klog"
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
tenantv1alpha1informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha1"
tenantv1alpha1listers "kubesphere.io/kubesphere/pkg/client/listers/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/constants"
devopsClient "kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
......@@ -48,6 +50,9 @@ type Controller struct {
namespaceLister corev1lister.NamespaceLister
namespaceSynced cache.InformerSynced
workspaceLister tenantv1alpha1listers.WorkspaceLister
workspaceSynced cache.InformerSynced
workqueue workqueue.RateLimitingInterface
workerLoopPeriod time.Duration
......@@ -59,7 +64,8 @@ func NewController(client clientset.Interface,
kubesphereClient kubesphereclient.Interface,
devopsClinet devopsClient.Interface,
namespaceInformer corev1informer.NamespaceInformer,
devopsInformer devopsinformers.DevOpsProjectInformer) *Controller {
devopsInformer devopsinformers.DevOpsProjectInformer,
workspaceInformer tenantv1alpha1informers.WorkspaceInformer) *Controller {
broadcaster := record.NewBroadcaster()
broadcaster.StartLogging(func(format string, args ...interface{}) {
......@@ -77,6 +83,8 @@ func NewController(client clientset.Interface,
devOpsProjectSynced: devopsInformer.Informer().HasSynced,
namespaceLister: namespaceInformer.Lister(),
namespaceSynced: namespaceInformer.Informer().HasSynced,
workspaceLister: workspaceInformer.Lister(),
workspaceSynced: workspaceInformer.Informer().HasSynced,
workerLoopPeriod: time.Second,
}
......@@ -163,7 +171,7 @@ func (c *Controller) Run(workers int, stopCh <-chan struct{}) error {
klog.Info("starting devops project controller")
defer klog.Info("shutting down devops project controller")
if !cache.WaitForCacheSync(stopCh, c.devOpsProjectSynced) {
if !cache.WaitForCacheSync(stopCh, c.devOpsProjectSynced, c.devOpsProjectSynced, c.workspaceSynced) {
return fmt.Errorf("failed to wait for caches to sync")
}
......@@ -273,12 +281,18 @@ func (c *Controller) syncHandler(key string) error {
}
if !reflect.DeepEqual(copyProject, project) {
_, err := c.kubesphereClient.DevopsV1alpha3().DevOpsProjects().Update(copyProject)
copyProject, err = c.kubesphereClient.DevopsV1alpha3().DevOpsProjects().Update(copyProject)
if err != nil {
klog.V(8).Info(err, fmt.Sprintf("failed to update ns %s ", key))
return err
}
}
if copyProject, err = c.bindWorkspace(copyProject); err != nil {
klog.Error(err)
return err
}
// Check project exists, otherwise we will create it.
_, err := c.devopsClient.GetDevOpsProject(copyProject.Status.AdminNamespace)
if err != nil {
......@@ -312,6 +326,38 @@ func (c *Controller) syncHandler(key string) error {
return nil
}
func (c *Controller) bindWorkspace(project *devopsv1alpha3.DevOpsProject) (*devopsv1alpha3.DevOpsProject, error) {
workspaceName := project.Labels[constants.WorkspaceLabelKey]
if workspaceName == "" {
return project, nil
}
workspace, err := c.workspaceLister.Get(workspaceName)
if err != nil {
// skip if workspace not found
if errors.IsNotFound(err) {
return project, nil
}
klog.Error(err)
return nil, err
}
if !metav1.IsControlledBy(project, workspace) {
project.OwnerReferences = nil
if err := controllerutil.SetControllerReference(workspace, project, scheme.Scheme); err != nil {
klog.Error(err)
return nil, err
}
return c.kubesphereClient.DevopsV1alpha3().DevOpsProjects().Update(project)
}
return project, nil
}
func (c *Controller) deleteDevOpsProjectInDevOps(project *devopsv1alpha3.DevOpsProject) error {
err := c.devopsClient.DeleteDevOpsProject(project.Status.AdminNamespace)
......@@ -330,9 +376,16 @@ func (c *Controller) generateNewNamespace(project *devopsv1alpha3.DevOpsProject)
},
ObjectMeta: metav1.ObjectMeta{
GenerateName: project.Name,
Labels: map[string]string{constants.DevOpsProjectLabelKey: project.Name},
Labels: map[string]string{
constants.DevOpsProjectLabelKey: project.Name,
},
},
}
if creator := project.Annotations[constants.CreatorAnnotationKey]; creator != "" {
ns.Annotations = map[string]string{constants.CreatorAnnotationKey: creator}
}
controllerutil.SetControllerReference(project, ns, scheme.Scheme)
return ns
}
......@@ -120,8 +120,10 @@ func (f *fixture) newController() (*Controller, informers.SharedInformerFactory,
k8sI := kubeinformers.NewSharedInformerFactory(f.kubeclient, noResyncPeriodFunc())
dI := fakeDevOps.New(f.initDevOpsProject...)
c := NewController(f.kubeclient, f.client, dI, k8sI.Core().V1().Namespaces(),
i.Devops().V1alpha3().DevOpsProjects())
c := NewController(f.kubeclient, f.client, dI,
k8sI.Core().V1().Namespaces(),
i.Devops().V1alpha3().DevOpsProjects(),
i.Tenant().V1alpha1().Workspaces())
c.devOpsProjectSynced = alwaysReady
c.eventRecorder = &record.FakeRecorder{}
......@@ -251,7 +253,9 @@ func filterInformerActions(actions []core.Action) []core.Action {
(action.Matches("list", devopsprojects.ResourcePluralDevOpsProject) ||
action.Matches("watch", devopsprojects.ResourcePluralDevOpsProject) ||
action.Matches("list", "namespaces") ||
action.Matches("watch", "namespaces")) {
action.Matches("watch", "namespaces") ||
action.Matches("watch", "workspaces") ||
action.Matches("list", "workspaces")) {
continue
}
ret = append(ret, action)
......
......@@ -229,17 +229,16 @@ func (c *Controller) reconcile(key string) error {
}
if globalRoleBinding.RoleRef.Name == iamv1alpha2.PlatformAdmin {
if err := c.relateToClusterAdmin(globalRoleBinding); err != nil {
if err := c.assignClusterAdminRole(globalRoleBinding); err != nil {
klog.Error(err)
return err
}
if c.devopsClient != nil {
username := findExpectUsername(globalRoleBinding)
err = c.devopsClient.AssignGlobalRole(modeldevops.JenkinsAdminRoleName, username)
if err != nil {
klog.Errorf("%+v", err)
return err
}
}
if c.devopsClient != nil {
if err := c.assignDevOpsAdminRole(globalRoleBinding); err != nil {
klog.Error(err)
return err
}
}
......@@ -299,11 +298,9 @@ func (c *Controller) multiClusterSync(globalRoleBinding *iamv1alpha2.GlobalRoleB
return nil
}
func (c *Controller) relateToClusterAdmin(globalRoleBinding *iamv1alpha2.GlobalRoleBinding) error {
func (c *Controller) assignClusterAdminRole(globalRoleBinding *iamv1alpha2.GlobalRoleBinding) error {
username := findExpectUsername(globalRoleBinding)
// unexpected
if username == "" {
return nil
}
......@@ -436,6 +433,16 @@ func (c *Controller) ensureNotControlledByKubefed(globalRoleBinding *iamv1alpha2
return nil
}
func (c *Controller) assignDevOpsAdminRole(globalRoleBinding *iamv1alpha2.GlobalRoleBinding) error {
if username := findExpectUsername(globalRoleBinding); username != "" {
if err := c.devopsClient.AssignGlobalRole(modeldevops.JenkinsAdminRoleName, username); err != nil {
klog.Errorf("%+v", err)
return err
}
}
return nil
}
func ensureSubjectAPIVersionIsValid(subjects []rbacv1.Subject) []rbacv1.Subject {
validSubjects := make([]rbacv1.Subject, 0)
for _, subject := range subjects {
......
......@@ -5,8 +5,6 @@ import (
"github.com/emicklei/go-restful"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
......@@ -1079,7 +1077,7 @@ func (h *iamHandler) resolveNamespace(namespace string, devops string) (string,
if devops == "" {
return namespace, nil
}
return h.am.GetControlledNamespace(devops)
return h.am.GetDevOpsRelatedNamespace(devops)
}
func (h *iamHandler) PatchWorkspaceRole(request *restful.Request, response *restful.Response) {
......@@ -1213,18 +1211,7 @@ func (h *iamHandler) updateGlobalRoleBinding(operator user.Info, user *iamv1alph
func (h *iamHandler) ListUserLoginRecords(request *restful.Request, response *restful.Response) {
username := request.PathParameter("user")
queryParam := query.ParseQueryParameter(request)
selector, _ := labels.Parse(queryParam.LabelSelector)
if selector == nil {
selector = labels.NewSelector()
}
requirement, err := labels.NewRequirement(iamv1alpha2.UserReferenceLabel, selection.Equals, []string{username})
if err != nil {
klog.Error(err)
handleError(request, response, err)
return
}
selector.Add(*requirement)
queryParam.LabelSelector = selector.String()
queryParam.Filters[query.FieldLabel] = query.Value(fmt.Sprintf("%s=%s", iamv1alpha2.UserReferenceLabel, username))
result, err := h.im.ListLoginRecords(queryParam)
if err != nil {
klog.Error(err)
......
......@@ -290,6 +290,7 @@ func (h *handler) passwordGrant(username string, password string, req *restful.R
return
default:
response.WriteError(http.StatusInternalServerError, apierrors.NewInternalError(err))
return
}
}
......
......@@ -101,6 +101,29 @@ func (h *tenantHandler) ListNamespaces(req *restful.Request, resp *restful.Respo
resp.WriteEntity(result)
}
func (h *tenantHandler) ListDevOpsProjects(req *restful.Request, resp *restful.Response) {
user, ok := request.UserFrom(req.Request.Context())
queryParam := query.ParseQueryParameter(req)
if !ok {
err := fmt.Errorf("cannot obtain user info")
klog.Errorln(err)
api.HandleForbidden(resp, nil, err)
return
}
workspace := req.PathParameter("workspace")
result, err := h.tenant.ListDevOpsProjects(user, workspace, queryParam)
if err != nil {
api.HandleInternalError(resp, nil, err)
return
}
resp.WriteEntity(result)
}
func (h *tenantHandler) CreateNamespace(request *restful.Request, response *restful.Response) {
workspace := request.PathParameter("workspace")
var namespace corev1.Namespace
......
......@@ -27,7 +27,6 @@ import (
eventsv1alpha1 "kubesphere.io/kubesphere/pkg/api/events/v1alpha1"
loggingv1alpha2 "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
typesv1beta1 "kubesphere.io/kubesphere/pkg/apis/types/v1beta1"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/constants"
......@@ -105,31 +104,37 @@ func AddToContainer(c *restful.Container, factory informers.InformerFactory, k8s
To(handler.ListNamespaces).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the namespaces for the current user").
Returns(http.StatusOK, api.StatusOK, []corev1.Namespace{}).
Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/federatednamespaces").
To(handler.ListFederatedNamespaces).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the federated namespaces for the current user").
Returns(http.StatusOK, api.StatusOK, []typesv1beta1.FederatedNamespace{}).
Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/federatednamespaces").
To(handler.ListFederatedNamespaces).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the federated namespaces of the specified workspace for the current user").
Returns(http.StatusOK, api.StatusOK, []typesv1beta1.FederatedNamespace{}).
Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/namespaces").
To(handler.ListNamespaces).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the namespaces of the specified workspace for the current user").
Returns(http.StatusOK, api.StatusOK, []corev1.Namespace{}).
Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/devops").
To(handler.ListDevOpsProjects).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the devops projects of the specified workspace for the current user").
Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/namespaces/{namespace}").
To(handler.DescribeNamespace).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("Retrieve namespace details.").
Returns(http.StatusOK, api.StatusOK, []corev1.Namespace{}).
Returns(http.StatusOK, api.StatusOK, corev1.Namespace{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.DELETE("/workspaces/{workspace}/namespaces/{namespace}").
To(handler.DeleteNamespace).
......@@ -142,20 +147,20 @@ func AddToContainer(c *restful.Container, factory informers.InformerFactory, k8s
Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the namespaces of the specified workspace for the current user").
Reads(corev1.Namespace{}).
Returns(http.StatusOK, api.StatusOK, []corev1.Namespace{}).
Returns(http.StatusOK, api.StatusOK, corev1.Namespace{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.PUT("/workspaces/{workspace}/namespaces/{namespace}").
To(handler.UpdateNamespace).
Param(ws.PathParameter("workspace", "workspace name")).
Reads(corev1.Namespace{}).
Returns(http.StatusOK, api.StatusOK, []corev1.Namespace{}).
Returns(http.StatusOK, api.StatusOK, corev1.Namespace{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.PATCH("/workspaces/{workspace}/namespaces/{namespace}").
To(handler.PatchNamespace).
Consumes(mimePatch...).
Param(ws.PathParameter("workspace", "workspace name")).
Reads(corev1.Namespace{}).
Returns(http.StatusOK, api.StatusOK, []corev1.Namespace{}).
Returns(http.StatusOK, api.StatusOK, corev1.Namespace{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/events").
......
......@@ -70,8 +70,9 @@ type AccessManagementInterface interface {
RemoveUserFromNamespace(username string, namespace string) error
CreateClusterRoleBinding(username string, role string) error
RemoveUserFromCluster(username string) error
GetControlledNamespace(devops string) (string, error)
GetControlledWorkspace(namespace string) (string, error)
GetDevOpsRelatedNamespace(devops string) (string, error)
GetNamespaceControlledWorkspace(namespace string) (string, error)
GetDevOpsControlledWorkspace(devops string) (string, error)
PatchNamespaceRole(namespace string, role *rbacv1.Role) (*rbacv1.Role, error)
PatchClusterRole(clusterRole *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error)
}
......@@ -279,23 +280,19 @@ func (am *amOperator) ListGlobalRoleBindings(username string) ([]*iamv1alpha2.Gl
}
func (am *amOperator) ListRoleBindings(username, namespace string) ([]*rbacv1.RoleBinding, error) {
roleBindings, err := am.resourceGetter.List(iamv1alpha2.ResourcesPluralRoleBinding, namespace, query.New())
if err != nil {
klog.Error(err)
return nil, err
}
result := make([]*rbacv1.RoleBinding, 0)
for _, obj := range roleBindings.Items {
roleBinding := obj.(*rbacv1.RoleBinding)
if contains(roleBinding.Subjects, username) {
result = append(result, roleBinding)
}
}
return result, nil
}
......@@ -964,7 +961,7 @@ func (am *amOperator) GetClusterRole(name string) (*rbacv1.ClusterRole, error) {
}
return obj.(*rbacv1.ClusterRole), nil
}
func (am *amOperator) GetControlledNamespace(devops string) (string, error) {
func (am *amOperator) GetDevOpsRelatedNamespace(devops string) (string, error) {
obj, err := am.resourceGetter.Get(devopsv1alpha3.ResourcePluralDevOpsProject, "", devops)
if err != nil {
klog.Error(err)
......@@ -975,7 +972,17 @@ func (am *amOperator) GetControlledNamespace(devops string) (string, error) {
return devopsProject.Status.AdminNamespace, nil
}
func (am *amOperator) GetControlledWorkspace(namespace string) (string, error) {
func (am *amOperator) GetDevOpsControlledWorkspace(devops string) (string, error) {
obj, err := am.resourceGetter.Get(devopsv1alpha3.ResourcePluralDevOpsProject, "", devops)
if err != nil {
klog.Error(err)
return "", err
}
devopsProject := obj.(*devopsv1alpha3.DevOpsProject)
return devopsProject.Labels[tenantv1alpha1.WorkspaceLabel], nil
}
func (am *amOperator) GetNamespaceControlledWorkspace(namespace string) (string, error) {
obj, err := am.resourceGetter.Get("namespaces", "", namespace)
if err != nil {
if errors.IsNotFound(err) {
......
......@@ -17,26 +17,109 @@ limitations under the License.
package tenant
import (
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/server/params"
dsClient "kubesphere.io/kubesphere/pkg/simple/client/devops"
"fmt"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/apiserver/request"
"kubesphere.io/kubesphere/pkg/constants"
resources "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
)
type DevOpsProjectLister interface {
ListDevOpsProjects(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error)
}
func (t *tenantOperator) ListDevOpsProjects(user user.Info, workspace string, queryParam *query.Query) (*api.ListResult, error) {
scope := request.ClusterScope
if workspace != "" {
scope = request.WorkspaceScope
}
type devopsProjectLister struct {
dsProject dsClient.ProjectOperator
}
listDevOps := authorizer.AttributesRecord{
User: user,
Verb: "list",
Workspace: workspace,
Resource: "devops",
ResourceRequest: true,
ResourceScope: scope,
}
func newProjectLister(client dsClient.ProjectOperator) DevOpsProjectLister {
return &devopsProjectLister{
dsProject: client,
decision, _, err := t.authorizer.Authorize(listDevOps)
if err != nil {
klog.Error(err)
return nil, err
}
}
func (o *devopsProjectLister) ListDevOpsProjects(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) {
//TODO: @runzexia use informer to impl it
return nil, nil
// allowed list devops in the specified scope
if decision == authorizer.DecisionAllow {
// filter by workspace
if workspace != "" {
queryParam.Filters[query.FieldLabel] = query.Value(fmt.Sprintf("%s=%s", tenantv1alpha1.WorkspaceLabel, workspace))
}
result, err := t.resourceGetter.List(devopsv1alpha3.ResourcePluralDevOpsProject, "", queryParam)
if err != nil {
klog.Error(err)
return nil, err
}
return result, nil
}
roleBindings, err := t.am.ListRoleBindings(user.GetName(), "")
if err != nil {
klog.Error(err)
return nil, err
}
devopsProjects := make([]runtime.Object, 0)
for _, roleBinding := range roleBindings {
obj, err := t.resourceGetter.Get("namespaces", "", roleBinding.Namespace)
if err != nil {
klog.Error(err)
return nil, err
}
namespace := obj.(*corev1.Namespace)
controlledDevOpsProject := namespace.Labels[constants.DevOpsProjectLabelKey]
// skip if not controlled by devops project
if controlledDevOpsProject == "" {
continue
}
devopsProject, err := t.resourceGetter.Get(devopsv1alpha3.ResourcePluralDevOpsProject, "", controlledDevOpsProject)
if err != nil {
if errors.IsNotFound(err) {
klog.Warning("orphan devops project found ", devopsProject)
continue
}
klog.Error(err)
return nil, err
}
// skip if not controlled by the specified workspace
if workspace != "" &&
devopsProject.(*devopsv1alpha3.DevOpsProject).Labels[tenantv1alpha1.WorkspaceLabel] != workspace {
continue
}
if !contains(devopsProjects, devopsProject) {
devopsProjects = append(devopsProjects, namespace)
}
}
result := resources.DefaultList(devopsProjects, queryParam, func(left runtime.Object, right runtime.Object, field query.Field) bool {
return resources.DefaultObjectMetaCompare(left.(*devopsv1alpha3.DevOpsProject).ObjectMeta, right.(*devopsv1alpha3.DevOpsProject).ObjectMeta, field)
}, func(object runtime.Object, filter query.Filter) bool {
devopsProject := object.(*devopsv1alpha3.DevOpsProject).ObjectMeta
if workspace != "" {
if workspaceLabel, ok := devopsProject.Labels[tenantv1alpha1.WorkspaceLabel]; !ok || workspaceLabel != workspace {
return false
}
}
return resources.DefaultObjectMetaFilter(devopsProject, filter)
})
return result, nil
}
......@@ -60,6 +60,7 @@ import (
type Interface interface {
ListWorkspaces(user user.Info, query *query.Query) (*api.ListResult, error)
ListNamespaces(user user.Info, workspace string, query *query.Query) (*api.ListResult, error)
ListDevOpsProjects(user user.Info, workspace string, query *query.Query) (*api.ListResult, error)
ListFederatedNamespaces(info user.Info, workspace string, param *query.Query) (*api.ListResult, error)
CreateNamespace(workspace string, namespace *corev1.Namespace) (*corev1.Namespace, error)
CreateWorkspace(workspace *tenantv1alpha2.WorkspaceTemplate) (*tenantv1alpha2.WorkspaceTemplate, error)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册