@@ -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,201 @@ 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+ default :
351+ return false , fmt .Errorf ("checks for resource type '%s' not added" , resourceType )
352+ }
353+ }
354+
355+ // 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.
356+ func CheckProjectAccess (userContext * wrangler.Context , verb , clusterID , projectName string ) (bool , error ) {
357+ switch verb {
358+ case "get" :
359+ _ , err := userContext .Mgmt .Project ().Get (clusterID , projectName , metav1.GetOptions {})
360+ return err == nil , err
361+ case "list" :
362+ _ , err := userContext .Mgmt .Project ().List (clusterID , metav1.ListOptions {})
363+ return err == nil , err
364+ case "create" :
365+ projectTemplate := projectsapi .NewProjectTemplate (clusterID )
366+ _ , err := userContext .Mgmt .Project ().Create (projectTemplate )
367+ return err == nil , err
368+ case "delete" :
369+ err := userContext .Mgmt .Project ().Delete (clusterID , projectName , & metav1.DeleteOptions {})
370+ return err == nil , err
371+ case "update" :
372+ project , err := userContext .Mgmt .Project ().Get (clusterID , projectName , metav1.GetOptions {})
373+ if err != nil {
374+ return false , err
375+ }
376+ if project .Labels == nil {
377+ project .Labels = make (map [string ]string )
378+ }
379+ project .Labels ["hello" ] = "world"
380+ _ , err = userContext .Mgmt .Project ().Update (project )
381+ return err == nil , err
382+ case "patch" :
383+ patchData := []byte (`{"metadata":{"annotations":{"patched":"true"}}}` )
384+ _ , err := userContext .Mgmt .Project ().Patch (clusterID , projectName , types .MergePatchType , patchData )
385+ return err == nil , err
386+ default :
387+ return false , fmt .Errorf ("verb '%s' not available in checks for projects" , verb )
388+ }
389+ }
390+
391+ // 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.
392+ func CheckNamespaceAccess (userContext * wrangler.Context , verb , namespaceName string ) (bool , error ) {
393+ switch verb {
394+ case "get" :
395+ _ , err := userContext .Core .Namespace ().Get (namespaceName , metav1.GetOptions {})
396+ return err == nil , err
397+ case "list" :
398+ _ , err := userContext .Core .Namespace ().List (metav1.ListOptions {})
399+ return err == nil , err
400+ case "delete" :
401+ err := userContext .Core .Namespace ().Delete (namespaceName , & metav1.DeleteOptions {})
402+ return err == nil , err
403+ default :
404+ return false , fmt .Errorf ("verb '%s' not available in checks for resource 'namespaces'" , verb )
405+ }
406+ }
407+
408+ // 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.
409+ func CheckPodAccess (userContext * wrangler.Context , verb , namespaceName , podName string ) (bool , error ) {
410+ switch verb {
411+ case "get" :
412+ _ , err := userContext .Core .Pod ().Get (namespaceName , podName , metav1.GetOptions {})
413+ return err == nil , err
414+ case "list" :
415+ _ , err := userContext .Core .Pod ().List (namespaceName , metav1.ListOptions {})
416+ return err == nil , err
417+ case "delete" :
418+ err := userContext .Core .Pod ().Delete (namespaceName , podName , & metav1.DeleteOptions {})
419+ return err == nil , err
420+ default :
421+ return false , fmt .Errorf ("verb '%s' not available in checks for resource 'pods'" , verb )
422+ }
423+ }
424+
425+ // 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.
426+ func CheckDeploymentAccess (userContext * wrangler.Context , verb , namespaceName , deploymentName string ) (bool , error ) {
427+ switch verb {
428+ case "get" :
429+ _ , err := userContext .Apps .Deployment ().Get (namespaceName , deploymentName , metav1.GetOptions {})
430+ return err == nil , err
431+ case "list" :
432+ _ , err := userContext .Apps .Deployment ().List (namespaceName , metav1.ListOptions {})
433+ return err == nil , err
434+ case "delete" :
435+ err := userContext .Apps .Deployment ().Delete (namespaceName , deploymentName , & metav1.DeleteOptions {})
436+ return err == nil , err
437+ default :
438+ return false , fmt .Errorf ("verb '%s' not available in checks for resource 'deployments'" , verb )
439+ }
440+ }
441+
442+ // 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.
443+ func CheckSecretAccess (userContext * wrangler.Context , verb , namespaceName , secretName string ) (bool , error ) {
444+ switch verb {
445+ case "get" :
446+ _ , err := userContext .Core .Secret ().Get (namespaceName , secretName , metav1.GetOptions {})
447+ return err == nil , err
448+ case "list" :
449+ _ , err := userContext .Core .Secret ().List (namespaceName , metav1.ListOptions {})
450+ return err == nil , err
451+ case "delete" :
452+ err := userContext .Core .Secret ().Delete (namespaceName , secretName , & metav1.DeleteOptions {})
453+ return err == nil , err
454+ default :
455+ return false , fmt .Errorf ("verb '%s' not available in checks for resource 'namespaces'" , verb )
456+ }
457+ }
458+
459+ // 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.
460+ func CheckPrtbAccess (userContext * wrangler.Context , verb , prtbNamespace , prtbName string ) (bool , error ) {
461+ switch verb {
462+ case "get" :
463+ _ , err := userContext .Mgmt .ProjectRoleTemplateBinding ().Get (prtbNamespace , prtbName , metav1.GetOptions {})
464+ return err == nil , err
465+ case "list" :
466+ _ , err := userContext .Mgmt .ProjectRoleTemplateBinding ().List (prtbNamespace , metav1.ListOptions {})
467+ return err == nil , err
468+ case "delete" :
469+ err := userContext .Mgmt .ProjectRoleTemplateBinding ().Delete (prtbNamespace , prtbName , & metav1.DeleteOptions {})
470+ return err == nil , err
471+ case "update" :
472+ prtb , err := userContext .Mgmt .ProjectRoleTemplateBinding ().Get (prtbNamespace , prtbName , metav1.GetOptions {})
473+ if err != nil {
474+ return false , err
475+ }
476+ if prtb .Labels == nil {
477+ prtb .Labels = make (map [string ]string )
478+ }
479+ prtb .Labels ["hello" ] = "world"
480+ _ , err = userContext .Mgmt .ProjectRoleTemplateBinding ().Update (prtb )
481+ return err == nil , err
482+ case "patch" :
483+ patchData := []byte (`{"metadata":{"annotations":{"patched":"true"}}}` )
484+ _ , err := userContext .Mgmt .ProjectRoleTemplateBinding ().Patch (prtbNamespace , prtbName , types .MergePatchType , patchData )
485+ return err == nil , err
486+ default :
487+ return false , fmt .Errorf ("verb '%s' not available in checks for prtbs" , verb )
488+ }
489+ }
0 commit comments