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 }