github.com/greenpau/go-authcrunch@v1.1.4/pkg/authn/transformer/transformer_test.go (about) 1 // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package transformer 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "github.com/greenpau/go-authcrunch/internal/tests" 21 "github.com/greenpau/go-authcrunch/pkg/acl" 22 "testing" 23 ) 24 25 func TestFactory(t *testing.T) { 26 var testcases = []struct { 27 name string 28 configs []*Config 29 user map[string]interface{} 30 keys []string 31 // Expected results. 32 want map[string]interface{} 33 shouldErr bool 34 err error 35 }{ 36 { 37 name: "add authp/admin role to greenpau@outlook.com", 38 user: map[string]interface{}{ 39 "email": "greenpau@outlook.com", 40 "roles": "editor", 41 }, 42 keys: []string{ 43 "challenges", 44 "roles", 45 }, 46 configs: []*Config{ 47 { 48 Matchers: []string{ 49 "exact match email greenpau@outlook.com", 50 }, 51 Actions: []string{ 52 "add role authp/admin authp/viewer", 53 "add role authp/editor", 54 "require mfa", 55 }, 56 }, 57 }, 58 want: map[string]interface{}{ 59 "roles": []string{ 60 "editor", 61 "authp/admin", 62 "authp/viewer", 63 "authp/editor", 64 }, 65 "challenges": []string{ 66 "mfa", 67 }, 68 }, 69 }, 70 { 71 name: "drop existing authp/viewer role", 72 user: map[string]interface{}{ 73 "email": "greenpau@outlook.com", 74 "roles": []string{"authp/admin", "authp/editor", "authp/viewer"}, 75 }, 76 keys: []string{ 77 "roles", 78 }, 79 configs: []*Config{ 80 { 81 Matchers: []string{ 82 "regex match role viewer", 83 }, 84 Actions: []string{ 85 "action drop matched role", 86 }, 87 }, 88 }, 89 want: map[string]interface{}{ 90 "roles": []string{ 91 "authp/admin", 92 "authp/editor", 93 }, 94 }, 95 }, 96 { 97 name: "drop any role without words authp/admin or authp/user", 98 user: map[string]interface{}{ 99 "email": "greenpau@outlook.com", 100 "roles": []string{ 101 "authp/admin", 102 "authp/editor", 103 "authp/viewer", 104 "authp/user", 105 }, 106 }, 107 keys: []string{ 108 "roles", 109 }, 110 configs: []*Config{ 111 { 112 Matchers: []string{ 113 "no regex match any role ^authp/(admin|user)$", 114 }, 115 Actions: []string{ 116 "action drop matched role", 117 }, 118 }, 119 }, 120 want: map[string]interface{}{ 121 "roles": []string{ 122 "authp/admin", 123 "authp/user", 124 }, 125 }, 126 }, 127 { 128 name: "drop any role without words authp/admin or authp/user and no roles found", 129 user: map[string]interface{}{ 130 "email": "greenpau@outlook.com", 131 "roles": []string{"authp/editor"}, 132 }, 133 keys: []string{ 134 "roles", 135 }, 136 configs: []*Config{ 137 { 138 Matchers: []string{ 139 "no regex match any role ^authp/(admin|user)$", 140 }, 141 Actions: []string{ 142 "action drop matched role", 143 }, 144 }, 145 }, 146 want: map[string]interface{}{ 147 "roles": []string(nil), 148 }, 149 }, 150 } 151 for _, tc := range testcases { 152 t.Run(tc.name, func(t *testing.T) { 153 msgs := []string{fmt.Sprintf("test name: %s", tc.name)} 154 tr, err := NewFactory(tc.configs) 155 if err != nil { 156 if tests.EvalErrWithLog(t, err, "transformer", tc.shouldErr, tc.err, msgs) { 157 return 158 } 159 } 160 if err := tr.Transform(tc.user); err != nil { 161 if tests.EvalErrWithLog(t, err, "transformer", tc.shouldErr, tc.err, msgs) { 162 return 163 } 164 } 165 got := make(map[string]interface{}) 166 for _, k := range tc.keys { 167 if v, exists := tc.user[k]; exists { 168 got[k] = v 169 } 170 } 171 tests.EvalObjectsWithLog(t, "transformer", tc.want, got, msgs) 172 }) 173 } 174 } 175 176 func TestTransformData(t *testing.T) { 177 var testcases = []struct { 178 name string 179 args []string 180 matcher *acl.AccessList 181 user map[string]interface{} 182 want map[string]interface{} 183 shouldErr bool 184 err error 185 }{ 186 { 187 name: "add role authp/user with webadmin", 188 args: []string{"add", "role", "authp/user"}, 189 user: map[string]interface{}{ 190 "sub": "webadmin", 191 "roles": []string{"authp/admin"}, 192 }, 193 want: map[string]interface{}{ 194 "sub": "webadmin", 195 "roles": []string{"authp/admin", "authp/user"}, 196 }, 197 }, 198 { 199 name: "add add _couchdb.roles _admin with webadmin", 200 args: []string{"add", "_couchdb.roles", "_admin", "as", "string", "list"}, 201 user: map[string]interface{}{ 202 "sub": "webadmin", 203 "roles": []string{"authp/admin", "authp/user"}, 204 }, 205 want: map[string]interface{}{ 206 "sub": "webadmin", 207 "roles": []interface{}{string("authp/admin"), string("authp/user")}, 208 "_couchdb.roles": []string{"_admin"}, 209 }, 210 }, 211 { 212 name: "add add _couchdb.db _admin with webadmin", 213 args: []string{"add", "_couchdb.db", "accounts", "as", "string"}, 214 user: map[string]interface{}{ 215 "sub": "webadmin", 216 "roles": []string{"authp/admin", "authp/user"}, 217 }, 218 want: map[string]interface{}{ 219 "sub": "webadmin", 220 "roles": []interface{}{string("authp/admin"), string("authp/user")}, 221 "_couchdb.db": "accounts", 222 }, 223 }, 224 { 225 name: "as type directive is too short", 226 args: []string{"add", "_couchdb.roles", "_admin", "as"}, 227 user: map[string]interface{}{ 228 "sub": "webadmin", 229 "roles": []string{"authp/admin", "authp/user"}, 230 }, 231 shouldErr: true, 232 err: fmt.Errorf( 233 "failed transforming %q field for %q action in %v: %v", 234 "_couchdb.roles", "add", []string{"add", "_couchdb.roles", "_admin", "as"}, 235 "as type directive is too short", 236 ), 237 }, 238 { 239 name: "unsupported data type", 240 args: []string{"add", "_couchdb.roles", "_admin", "as", "foo"}, 241 user: map[string]interface{}{ 242 "sub": "webadmin", 243 "roles": []string{"authp/admin", "authp/user"}, 244 }, 245 shouldErr: true, 246 err: fmt.Errorf( 247 "failed transforming %q field for %q action in %v: %v", 248 "_couchdb.roles", "add", []string{"add", "_couchdb.roles", "_admin", "as", "foo"}, 249 "unsupported \"foo\" data type", 250 ), 251 }, 252 { 253 name: "add matrix_id claim with replacer from sub claim", 254 args: []string{"add", "matrix_id", "@{claims.sub}:matrix.foo.bar", "as", "string"}, 255 user: map[string]interface{}{ 256 "sub": "webadmin", 257 "roles": []string{"authp/admin"}, 258 }, 259 want: map[string]interface{}{ 260 "sub": "webadmin", 261 "roles": []interface{}{"authp/admin"}, 262 "matrix_id": "@webadmin:matrix.foo.bar", 263 }, 264 }, 265 { 266 name: "add matrix_id claim with replacer from sub and email claims", 267 args: []string{"add", "matrix_id", "@{claims.sub}:{claims.email}:matrix.foo.bar", "as", "string"}, 268 user: map[string]interface{}{ 269 "sub": "webadmin", 270 "email": "webadmin@localdomain.local", 271 "roles": []string{"authp/admin"}, 272 }, 273 want: map[string]interface{}{ 274 "sub": "webadmin", 275 "roles": []interface{}{"authp/admin"}, 276 "email": "webadmin@localdomain.local", 277 "matrix_id": "@webadmin:webadmin@localdomain.local:matrix.foo.bar", 278 }, 279 }, 280 { 281 name: "add roles based on replacer from realm claim", 282 args: []string{ 283 "add", "roles", "{claims.realm}/admin", "{claims.realm}/user"}, 284 user: map[string]interface{}{ 285 "sub": "webadmin", 286 "realm": "local", 287 "email": "webadmin@localdomain.local", 288 "roles": []string{"authp/admin"}, 289 }, 290 want: map[string]interface{}{ 291 "sub": "webadmin", 292 "realm": "local", 293 "roles": []string{"authp/admin", "local/admin", "local/user"}, 294 "email": "webadmin@localdomain.local", 295 }, 296 }, 297 { 298 name: "add email claim based on replacer from sub and realm claims", 299 args: []string{ 300 "add", "email", "{claims.sub}@{claims.realm}"}, 301 user: map[string]interface{}{ 302 "sub": "webadmin", 303 "realm": "localdomain.local", 304 "roles": []string{"authp/admin"}, 305 }, 306 want: map[string]interface{}{ 307 "sub": "webadmin", 308 "realm": "localdomain.local", 309 "roles": []interface{}{"authp/admin"}, 310 "email": "webadmin@localdomain.local", 311 }, 312 }, 313 } 314 for _, tc := range testcases { 315 t.Run(tc.name, func(t *testing.T) { 316 msgs := []string{fmt.Sprintf("test name: %s", tc.name)} 317 got := deepCopy(tc.user) 318 if err := transformData(tc.args, got, tc.matcher); err != nil { 319 if tests.EvalErrWithLog(t, err, "transformer", tc.shouldErr, tc.err, msgs) { 320 return 321 } 322 } 323 tests.EvalObjectsWithLog(t, "transformer", tc.want, got, msgs) 324 }) 325 } 326 } 327 328 func deepCopy(src map[string]interface{}) map[string]interface{} { 329 if src == nil { 330 return nil 331 } 332 j, _ := json.Marshal(src) 333 m := make(map[string]interface{}) 334 json.Unmarshal(j, &m) 335 return m 336 }