github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/pkg/auth/rbac_test.go (about)

     1  /*This file is part of kuberpult.
     2  
     3  Kuberpult is free software: you can redistribute it and/or modify
     4  it under the terms of the Expat(MIT) License as published by
     5  the Free Software Foundation.
     6  
     7  Kuberpult is distributed in the hope that it will be useful,
     8  but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    10  MIT License for more details.
    11  
    12  You should have received a copy of the MIT License
    13  along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>.
    14  
    15  Copyright 2023 freiheit.com*/
    16  
    17  package auth
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/google/go-cmp/cmp"
    23  	"github.com/google/go-cmp/cmp/cmpopts"
    24  )
    25  
    26  func TestValidateRbacPermission(t *testing.T) {
    27  	tcs := []struct {
    28  		Name           string
    29  		Permission     string
    30  		WantError      error
    31  		WantPermission *Permission
    32  	}{
    33  		{
    34  			Name:       "Validating RBAC works as expected",
    35  			Permission: "Developer,CreateUndeploy,dev:*,*,allow",
    36  			WantPermission: &Permission{
    37  				Role:        "Developer",
    38  				Action:      "CreateUndeploy",
    39  				Application: "*",
    40  				Environment: "dev:*",
    41  			},
    42  		},
    43  		{
    44  			Name:       "Invalid permission Application",
    45  			Permission: "Developer,CreateLock,dev:development-d2,VeryLongAppWithInvalidName,allow",
    46  			WantError:  errMatcher{"invalid application VeryLongAppWithInvalidName"},
    47  		},
    48  		{
    49  			Name:       "Invalid permission Action",
    50  			Permission: "Developer,WRONG_ACTION,dev:development-d2,*,allow",
    51  			WantError:  errMatcher{"invalid action WRONG_ACTION"},
    52  		},
    53  		{
    54  			Name:       "Invalid permission Environment <ENVIRONMENT_GROUP:ENVIRONMENT>",
    55  			Permission: "Developer,CreateLock,dev:-foo,*,allow",
    56  			WantError:  errMatcher{"invalid environment dev:-foo"},
    57  		},
    58  		{
    59  			Name:       "Invalid permission Environment <ENVIRONMENT>",
    60  			Permission: "Developer,CreateLock,-foo,*,allow",
    61  			WantError:  errMatcher{"invalid environment -foo"},
    62  		},
    63  		{
    64  			Name:       "Invalid permission Empty Environment",
    65  			Permission: "Developer,CreateLock,,*,allow",
    66  			WantError:  errMatcher{"invalid environment "},
    67  		},
    68  		{
    69  			Name:       "Invalid permission for Environment Independent action <ENVIRONMENT_GROUP:*>",
    70  			Permission: "Developer,DeployUndeploy,dev:development-1,*,allow",
    71  			WantError:  errMatcher{"the action DeployUndeploy requires the environment * and got dev:development-1"},
    72  		},
    73  	}
    74  
    75  	for _, tc := range tcs {
    76  		tc := tc
    77  		t.Run(tc.Name, func(t *testing.T) {
    78  			permission, err := ValidateRbacPermission(tc.Permission)
    79  			if diff := cmp.Diff(tc.WantError, err, cmpopts.EquateErrors()); diff != "" {
    80  				t.Errorf("error mismatch (-want, +got):\n%s", diff)
    81  			}
    82  			if diff := cmp.Diff(permission, tc.WantPermission, cmpopts.EquateEmpty()); diff != "" {
    83  				t.Errorf("%s: unexpected result diff : %v", tc.Name, diff)
    84  			}
    85  		})
    86  	}
    87  }
    88  
    89  func TestCheckUserPermissions(t *testing.T) {
    90  	tcs := []struct {
    91  		Name        string
    92  		rbacConfig  RBACConfig
    93  		user        *User
    94  		env         string
    95  		envGroup    string
    96  		application string
    97  		action      string
    98  		team        string
    99  		WantError   error
   100  	}{
   101  		{
   102  			Name:        "Check user permission works as expected",
   103  			user:        &User{DexAuthContext: &DexAuthContext{Role: "Developer"}},
   104  			env:         "production",
   105  			envGroup:    "production",
   106  			application: "app1",
   107  			action:      PermissionCreateLock,
   108  			rbacConfig:  RBACConfig{DexEnabled: true, Policy: map[string]*Permission{"Developer,CreateLock,production:production,app1,allow": {Role: "Developer"}}},
   109  			team:        "",
   110  		},
   111  		{
   112  			Name:        "Environment independent works as expected",
   113  			user:        &User{Name: "user", DexAuthContext: &DexAuthContext{Role: "Developer"}},
   114  			env:         "production",
   115  			envGroup:    "production",
   116  			application: "app1",
   117  			action:      PermissionCreateUndeploy,
   118  			rbacConfig:  RBACConfig{DexEnabled: true, Policy: map[string]*Permission{"Developer,CreateUndeploy,production:*,*,allow": {Role: "Developer"}}},
   119  			team:        "team",
   120  		},
   121  		{
   122  			Name:        "User does not have permission: wrong environment/group",
   123  			user:        &User{DexAuthContext: &DexAuthContext{Role: "Developer"}},
   124  			env:         "production",
   125  			envGroup:    "staging",
   126  			application: "app1",
   127  			action:      PermissionCreateLock,
   128  			rbacConfig:  RBACConfig{DexEnabled: true, Policy: map[string]*Permission{"Developer,CreateLock,production:production,app1,allow": {Role: "Developer"}}},
   129  			team:        "random-team",
   130  			WantError: PermissionError{
   131  				Role:        "Developer",
   132  				Action:      "CreateLock",
   133  				Environment: "production",
   134  				Team:        "random-team",
   135  			},
   136  		},
   137  		{
   138  			Name:        "User does not have permission: wrong app",
   139  			user:        &User{DexAuthContext: &DexAuthContext{Role: "Developer"}},
   140  			env:         "production",
   141  			envGroup:    "production",
   142  			application: "app2",
   143  			action:      PermissionCreateLock,
   144  			team:        "other-team",
   145  			rbacConfig:  RBACConfig{DexEnabled: true, Policy: map[string]*Permission{"Developer,CreateLock,production:production,app1,allow": {Role: "Developer"}}},
   146  			WantError: PermissionError{
   147  				Role:        "Developer",
   148  				Action:      "CreateLock",
   149  				Environment: "production",
   150  				Team:        "other-team",
   151  			},
   152  		},
   153  	}
   154  
   155  	for _, tc := range tcs {
   156  		tc := tc
   157  		t.Run(tc.Name, func(t *testing.T) {
   158  			err := CheckUserPermissions(tc.rbacConfig, tc.user, tc.env, tc.team, tc.envGroup, tc.application, tc.action)
   159  			if diff := cmp.Diff(tc.WantError, err, cmpopts.EquateErrors()); diff != "" {
   160  				t.Errorf("Error mismatch (-want +got):\n%s", diff)
   161  			}
   162  		})
   163  	}
   164  }
   165  
   166  func TestValidateRbacPermissionWildcards(t *testing.T) {
   167  	tcs := []struct {
   168  		Name        string
   169  		permissions []string
   170  		WantError   error
   171  	}{
   172  		{
   173  			Name: "Check permission validation works for all wildcard combinations",
   174  			permissions: []string{
   175  				"Developer,CreateLock,production:production,app1,allow",
   176  				"Developer,CreateLock,production:production,*,allow",
   177  				"Developer,CreateLock,production:*,app1,allow",
   178  				"Developer,CreateLock,production:*,*,allow",
   179  				"Developer,CreateLock,*:production,app1,allow",
   180  				"Developer,CreateLock,*:production,*,allow",
   181  				"Developer,CreateLock,*:*,app1,allow",
   182  				"Developer,CreateLock,*:*,*,allow",
   183  			},
   184  		},
   185  	}
   186  	for _, tc := range tcs {
   187  		tc := tc
   188  		t.Run(tc.Name, func(t *testing.T) {
   189  			// Test all wildcard possible combinations (2^8).
   190  			for _, permission := range tc.permissions {
   191  				_, err := ValidateRbacPermission(permission)
   192  				if diff := cmp.Diff(tc.WantError, err, cmpopts.EquateErrors()); diff != "" {
   193  					t.Errorf("Error mismatch (-want +got):\n%s", diff)
   194  				}
   195  			}
   196  		})
   197  	}
   198  }
   199  
   200  func TestCheckUserPermissionsWildcards(t *testing.T) {
   201  	tcs := []struct {
   202  		Name        string
   203  		user        *User
   204  		env         string
   205  		envGroup    string
   206  		application string
   207  		action      string
   208  		policies    []map[string]*Permission
   209  		WantError   error
   210  	}{
   211  		{
   212  			Name:        "Check user permission works for all wildcard combinations",
   213  			user:        &User{DexAuthContext: &DexAuthContext{Role: "Developer"}},
   214  			env:         "production",
   215  			envGroup:    "production",
   216  			application: "app1",
   217  			action:      PermissionCreateLock,
   218  			policies: []map[string]*Permission{
   219  				{"Developer,CreateLock,production:production,app1,allow": {Role: "Developer"}},
   220  				{"Developer,CreateLock,production:production,*,allow": {Role: "Developer"}},
   221  				{"Developer,CreateLock,production:*,app1,allow": {Role: "Developer"}},
   222  				{"Developer,CreateLock,production:*,*,allow": {Role: "Developer"}},
   223  				{"Developer,CreateLock,*:production,app1,allow": {Role: "Developer"}},
   224  				{"Developer,CreateLock,*:production,*,allow": {Role: "Developer"}},
   225  				{"Developer,CreateLock,*:*,app1,allow": {Role: "Developer"}},
   226  				{"Developer,CreateLock,*:*,*,allow": {Role: "Developer"}},
   227  			},
   228  		},
   229  	}
   230  	for _, tc := range tcs {
   231  		tc := tc
   232  		t.Run(tc.Name, func(t *testing.T) {
   233  			// Test all wildcard possible combinations (2^8).
   234  			for _, policy := range tc.policies {
   235  				rbacConfig := RBACConfig{DexEnabled: true, Policy: policy}
   236  				err := CheckUserPermissions(rbacConfig, tc.user, tc.env, "", tc.envGroup, tc.application, tc.action)
   237  				if diff := cmp.Diff(tc.WantError, err, cmpopts.EquateErrors()); diff != "" {
   238  					t.Errorf("Error mismatch (-want +got):\n%s", diff)
   239  				}
   240  			}
   241  		})
   242  	}
   243  }
   244  
   245  func TestReadScopes(t *testing.T) {
   246  	tcs := []struct {
   247  		Name         string
   248  		ScopesString string
   249  		WantScopes   []string
   250  	}{
   251  		{
   252  			Name:         "Correctly parses the scopes string",
   253  			ScopesString: "groups, emails, profile, openID",
   254  			WantScopes:   []string{"groups", "emails", "profile", "openID"},
   255  		},
   256  	}
   257  	for _, tc := range tcs {
   258  		tc := tc
   259  		t.Run(tc.Name, func(t *testing.T) {
   260  			scopes := ReadScopes(tc.ScopesString)
   261  			if diff := cmp.Diff(tc.WantScopes, scopes); diff != "" {
   262  				t.Errorf("Error mismatch (-want +got):\n%s", diff)
   263  			}
   264  		})
   265  	}
   266  }