@@ -14,6 +14,8 @@ import (
1414 "github.com/rancher/shepherd/extensions/clusters"
1515 "github.com/rancher/shepherd/extensions/users"
1616 namegen "github.com/rancher/shepherd/pkg/namegenerator"
17+ "github.com/rancher/shepherd/pkg/wrangler"
18+ clusterapi "github.com/rancher/tests/actions/kubeapi/clusters"
1719 namespacesapi "github.com/rancher/tests/actions/kubeapi/namespaces"
1820 projectsapi "github.com/rancher/tests/actions/kubeapi/projects"
1921 rbacapi "github.com/rancher/tests/actions/kubeapi/rbac"
@@ -24,6 +26,7 @@ import (
2426 coreV1 "k8s.io/api/core/v1"
2527 apierrors "k8s.io/apimachinery/pkg/api/errors"
2628 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+ "k8s.io/apimachinery/pkg/types"
2730)
2831
2932// VerifyGlobalRoleBindingsForUser validates that a global role bindings is created for a user when the user is created
@@ -286,3 +289,220 @@ func VerifyProjectRoleTemplateBindingForUser(client *rancher.Client, username st
286289
287290 return userPrtbs , nil
288291}
292+
293+ // VerifyUserPermission validates that a user has the expected permissions for a given resource
294+ func VerifyUserPermission (client * rancher.Client , clusterID string , user * management.User , verb , resourceType , namespaceName , resourceName string , expected , isCRDInLocalCluster bool ) error {
295+ allowed , err := CheckUserAccess (client , clusterID , user , verb , resourceType , namespaceName , resourceName , isCRDInLocalCluster )
296+
297+ if expected {
298+ if err != nil {
299+ if apierrors .IsForbidden (err ) {
300+ return fmt .Errorf ("user should have '%s' access to %s/%s/%s, but got forbidden error: %v" , verb , resourceType , namespaceName , resourceName , err )
301+ }
302+ return fmt .Errorf ("error verifying user access to %s/%s/%s: %v" , resourceType , namespaceName , resourceName , err )
303+ }
304+ if ! allowed {
305+ return fmt .Errorf ("user should have '%s' access to %s/%s/%s, but access was denied" , verb , resourceType , namespaceName , resourceName )
306+ }
307+ } else {
308+ if err == nil && allowed {
309+ return fmt .Errorf ("expected '%s' access to %s/%s/%s to be denied, but access was granted" , verb , resourceType , namespaceName , resourceName )
310+ }
311+ if err != nil && ! apierrors .IsForbidden (err ) {
312+ return fmt .Errorf ("expected forbidden error for %s/%s/%s, but got: %v" , resourceType , namespaceName , resourceName , err )
313+ }
314+ }
315+
316+ return nil
317+ }
318+
319+ // CheckUserAccess checks if a user has the specified access to a resource in a cluster. It returns true if the user has access, false otherwise.
320+ func CheckUserAccess (client * rancher.Client , clusterID string , user * management.User , verb , resourceType , namespaceName , resourceName string , isCRDInLocalCluster bool ) (bool , error ) {
321+ userClient , err := client .AsUser (user )
322+ if err != nil {
323+ return false , fmt .Errorf ("failed to create user client: %w" , err )
324+ }
325+
326+ var userContext * wrangler.Context
327+ if isCRDInLocalCluster {
328+ userContext , err = clusterapi .GetClusterWranglerContext (userClient , rbacapi .LocalCluster )
329+ } else {
330+ userContext , err = clusterapi .GetClusterWranglerContext (userClient , clusterID )
331+ }
332+
333+ if err != nil {
334+ return false , fmt .Errorf ("failed to get user context: %w" , err )
335+ }
336+
337+ switch resourceType {
338+ case "projects" :
339+ return CheckProjectAccess (userContext , verb , clusterID , resourceName )
340+ case "namespaces" :
341+ return CheckNamespaceAccess (userContext , verb , resourceName )
342+ case "deployments" :
343+ return CheckDeploymentAccess (userContext , verb , namespaceName , resourceName )
344+ case "pods" :
345+ return CheckPodAccess (userContext , verb , namespaceName , resourceName )
346+ case "secrets" :
347+ return CheckSecretAccess (userContext , verb , namespaceName , resourceName )
348+ case "projectroletemplatebindings" :
349+ return CheckPrtbAccess (userContext , verb , namespaceName , resourceName )
350+ case "configmaps" :
351+ return CheckConfigMapAccess (userContext , verb , namespaceName , resourceName )
352+ default :
353+ return false , fmt .Errorf ("checks for resource type '%s' not added" , resourceType )
354+ }
355+ }
356+
357+ // CheckProjectAccess checks if a user has the specified access to a project in a cluster. It returns true if the user has access, false otherwise.
358+ func CheckProjectAccess (userContext * wrangler.Context , verb , clusterID , projectName string ) (bool , error ) {
359+ switch verb {
360+ case "get" :
361+ _ , err := userContext .Mgmt .Project ().Get (clusterID , projectName , metav1.GetOptions {})
362+ return err == nil , err
363+ case "list" :
364+ _ , err := userContext .Mgmt .Project ().List (clusterID , metav1.ListOptions {})
365+ return err == nil , err
366+ case "create" :
367+ projectTemplate := projectsapi .NewProjectTemplate (clusterID )
368+ _ , err := userContext .Mgmt .Project ().Create (projectTemplate )
369+ return err == nil , err
370+ case "delete" :
371+ err := userContext .Mgmt .Project ().Delete (clusterID , projectName , & metav1.DeleteOptions {})
372+ return err == nil , err
373+ case "update" :
374+ project , err := userContext .Mgmt .Project ().Get (clusterID , projectName , metav1.GetOptions {})
375+ if err != nil {
376+ return false , err
377+ }
378+ if project .Labels == nil {
379+ project .Labels = make (map [string ]string )
380+ }
381+ project .Labels ["hello" ] = "world"
382+ _ , err = userContext .Mgmt .Project ().Update (project )
383+ return err == nil , err
384+ case "patch" :
385+ patchData := []byte (`{"metadata":{"annotations":{"patched":"true"}}}` )
386+ _ , err := userContext .Mgmt .Project ().Patch (clusterID , projectName , types .MergePatchType , patchData )
387+ return err == nil , err
388+ default :
389+ return false , fmt .Errorf ("verb '%s' not available in checks for projects" , verb )
390+ }
391+ }
392+
393+ // CheckNamespaceAccess checks if a user has the specified access to a namespace in a cluster. It returns true if the user has access, false otherwise.
394+ func CheckNamespaceAccess (userContext * wrangler.Context , verb , namespaceName string ) (bool , error ) {
395+ switch verb {
396+ case "get" :
397+ _ , err := userContext .Core .Namespace ().Get (namespaceName , metav1.GetOptions {})
398+ return err == nil , err
399+ case "list" :
400+ _ , err := userContext .Core .Namespace ().List (metav1.ListOptions {})
401+ return err == nil , err
402+ case "delete" :
403+ err := userContext .Core .Namespace ().Delete (namespaceName , & metav1.DeleteOptions {})
404+ return err == nil , err
405+ default :
406+ return false , fmt .Errorf ("verb '%s' not available in checks for resource 'namespaces'" , verb )
407+ }
408+ }
409+
410+ // CheckPodAccess checks if a user has the specified access to a pod in a namespace. It returns true if the user has access, false otherwise.
411+ func CheckPodAccess (userContext * wrangler.Context , verb , namespaceName , podName string ) (bool , error ) {
412+ switch verb {
413+ case "get" :
414+ _ , err := userContext .Core .Pod ().Get (namespaceName , podName , metav1.GetOptions {})
415+ return err == nil , err
416+ case "list" :
417+ _ , err := userContext .Core .Pod ().List (namespaceName , metav1.ListOptions {})
418+ return err == nil , err
419+ case "delete" :
420+ err := userContext .Core .Pod ().Delete (namespaceName , podName , & metav1.DeleteOptions {})
421+ return err == nil , err
422+ default :
423+ return false , fmt .Errorf ("verb '%s' not available in checks for resource 'pods'" , verb )
424+ }
425+ }
426+
427+ // CheckDeploymentAccess checks if a user has the specified access to a deployment in a namespace. It returns true if the user has access, false otherwise.
428+ func CheckDeploymentAccess (userContext * wrangler.Context , verb , namespaceName , deploymentName string ) (bool , error ) {
429+ switch verb {
430+ case "get" :
431+ _ , err := userContext .Apps .Deployment ().Get (namespaceName , deploymentName , metav1.GetOptions {})
432+ return err == nil , err
433+ case "list" :
434+ _ , err := userContext .Apps .Deployment ().List (namespaceName , metav1.ListOptions {})
435+ return err == nil , err
436+ case "delete" :
437+ err := userContext .Apps .Deployment ().Delete (namespaceName , deploymentName , & metav1.DeleteOptions {})
438+ return err == nil , err
439+ default :
440+ return false , fmt .Errorf ("verb '%s' not available in checks for resource 'deployments'" , verb )
441+ }
442+ }
443+
444+ // CheckSecretAccess checks if a user has the specified access to a secret in a namespace. It returns true if the user has access, false otherwise.
445+ func CheckSecretAccess (userContext * wrangler.Context , verb , namespaceName , secretName string ) (bool , error ) {
446+ switch verb {
447+ case "get" :
448+ _ , err := userContext .Core .Secret ().Get (namespaceName , secretName , metav1.GetOptions {})
449+ return err == nil , err
450+ case "list" :
451+ _ , err := userContext .Core .Secret ().List (namespaceName , metav1.ListOptions {})
452+ return err == nil , err
453+ case "delete" :
454+ err := userContext .Core .Secret ().Delete (namespaceName , secretName , & metav1.DeleteOptions {})
455+ return err == nil , err
456+ default :
457+ return false , fmt .Errorf ("verb '%s' not available in checks for resource 'namespaces'" , verb )
458+ }
459+ }
460+
461+ // CheckPrtbAccess checks if a user has the specified access to a project role template binding in a namespace. It returns true if the user has access, false otherwise.
462+ func CheckPrtbAccess (userContext * wrangler.Context , verb , prtbNamespace , prtbName string ) (bool , error ) {
463+ switch verb {
464+ case "get" :
465+ _ , err := userContext .Mgmt .ProjectRoleTemplateBinding ().Get (prtbNamespace , prtbName , metav1.GetOptions {})
466+ return err == nil , err
467+ case "list" :
468+ _ , err := userContext .Mgmt .ProjectRoleTemplateBinding ().List (prtbNamespace , metav1.ListOptions {})
469+ return err == nil , err
470+ case "delete" :
471+ err := userContext .Mgmt .ProjectRoleTemplateBinding ().Delete (prtbNamespace , prtbName , & metav1.DeleteOptions {})
472+ return err == nil , err
473+ case "update" :
474+ prtb , err := userContext .Mgmt .ProjectRoleTemplateBinding ().Get (prtbNamespace , prtbName , metav1.GetOptions {})
475+ if err != nil {
476+ return false , err
477+ }
478+ if prtb .Labels == nil {
479+ prtb .Labels = make (map [string ]string )
480+ }
481+ prtb .Labels ["hello" ] = "world"
482+ _ , err = userContext .Mgmt .ProjectRoleTemplateBinding ().Update (prtb )
483+ return err == nil , err
484+ case "patch" :
485+ patchData := []byte (`{"metadata":{"annotations":{"patched":"true"}}}` )
486+ _ , err := userContext .Mgmt .ProjectRoleTemplateBinding ().Patch (prtbNamespace , prtbName , types .MergePatchType , patchData )
487+ return err == nil , err
488+ default :
489+ return false , fmt .Errorf ("verb '%s' not available in checks for prtbs" , verb )
490+ }
491+ }
492+
493+ // CheckConfigMapAccess checks if a user has the specified access to a ConfigMap in a namespace. It returns true if the user has access, false otherwise.
494+ func CheckConfigMapAccess (userContext * wrangler.Context , verb , namespaceName , configMapName string ) (bool , error ) {
495+ switch verb {
496+ case "get" :
497+ _ , err := userContext .Core .ConfigMap ().Get (namespaceName , configMapName , metav1.GetOptions {})
498+ return err == nil , err
499+ case "list" :
500+ _ , err := userContext .Core .ConfigMap ().List (namespaceName , metav1.ListOptions {})
501+ return err == nil , err
502+ case "delete" :
503+ err := userContext .Core .ConfigMap ().Delete (namespaceName , configMapName , & metav1.DeleteOptions {})
504+ return err == nil , err
505+ default :
506+ return false , fmt .Errorf ("verb '%s' not available in checks for resource 'configmaps'" , verb )
507+ }
508+ }
0 commit comments