eintopf.info@v0.13.16/service/user/authorizer_test.go (about) 1 // Copyright (C) 2022 The Eintopf authors 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 16 package user_test 17 18 import ( 19 "context" 20 "log" 21 "testing" 22 23 "github.com/google/go-cmp/cmp" 24 "github.com/google/go-cmp/cmp/cmpopts" 25 26 "eintopf.info/service/auth" 27 "eintopf.info/service/user" 28 ) 29 30 var ( 31 roleAdmin = auth.RoleAdmin 32 roleModertor = auth.RoleModerator 33 roleNormal = auth.RoleNormal 34 roleInternal = auth.RoleInternal 35 ) 36 37 func TestAuthorizerCreate(t *testing.T) { 38 for _, test := range []struct { 39 name string 40 userID string 41 role *auth.Role 42 user *user.NewUser 43 wantError error 44 }{ 45 { 46 name: "UnauthorizedWithNoUserIDAndRole", 47 userID: "", 48 role: nil, 49 user: &user.NewUser{}, 50 wantError: auth.ErrUnauthorized, 51 }, { 52 name: "Authorized:Internal->Admin", 53 userID: "foo", 54 role: &roleInternal, 55 user: &user.NewUser{Role: auth.RoleAdmin, Password: "foo"}, 56 wantError: nil, 57 }, { 58 name: "Unauthorized:Admin->Internal", 59 userID: "foo", 60 role: &roleAdmin, 61 user: &user.NewUser{Role: auth.RoleInternal, Password: "foo"}, 62 wantError: auth.ErrUnauthorized, 63 }, { 64 name: "Authorized:Admin->Admin", 65 userID: "foo", 66 role: &roleAdmin, 67 user: &user.NewUser{Role: auth.RoleAdmin, Password: "foo"}, 68 wantError: nil, 69 }, { 70 name: "Unauthorized:Moderator->Internal", 71 userID: "foo", 72 role: &roleModertor, 73 user: &user.NewUser{Role: auth.RoleInternal, Password: "foo"}, 74 wantError: auth.ErrUnauthorized, 75 }, { 76 name: "Unauthorized:Moderator->Admin", 77 userID: "foo", 78 role: &roleModertor, 79 user: &user.NewUser{Role: auth.RoleAdmin, Password: "foo"}, 80 wantError: auth.ErrUnauthorized, 81 }, { 82 name: "Authorized:Moderator->Moderator", 83 userID: "foo", 84 role: &roleModertor, 85 user: &user.NewUser{Role: auth.RoleModerator, Password: "foo"}, 86 wantError: nil, 87 }, { 88 name: "Unauthorized:Normal->Internal", 89 userID: "foo", 90 role: &roleNormal, 91 user: &user.NewUser{Role: auth.RoleInternal, Password: "foo"}, 92 wantError: auth.ErrUnauthorized, 93 }, { 94 name: "Unauthorized:Normal->Admin", 95 userID: "foo", 96 role: &roleNormal, 97 user: &user.NewUser{Role: auth.RoleAdmin, Password: "foo"}, 98 wantError: auth.ErrUnauthorized, 99 }, { 100 name: "Unauthorized:Normal->Moderator", 101 userID: "foo", 102 role: &roleNormal, 103 user: &user.NewUser{Role: auth.RoleModerator, Password: "foo"}, 104 wantError: auth.ErrUnauthorized, 105 }, { 106 name: "Unauthorized:Normal->Normal", 107 userID: "foo", 108 role: &roleNormal, 109 user: &user.NewUser{Role: auth.RoleNormal, Password: "foo"}, 110 wantError: auth.ErrUnauthorized, 111 }, 112 } { 113 t.Run(test.name, func(tt *testing.T) { 114 ctx := auth.ContextWithID(context.Background(), test.userID) 115 if test.role != nil { 116 ctx = auth.ContextWithRole(ctx, *test.role) 117 } 118 119 store := user.NewMemoryStore() 120 121 u := &user.User{Role: test.user.Role, Password: test.user.Password} 122 authorizer := user.NewAuthorizer(store) 123 124 u, err := authorizer.Create(ctx, test.user) 125 if err != test.wantError { 126 tt.Errorf("err != test.wantError: %s != %s", err, test.wantError) 127 } 128 129 if test.wantError == nil { 130 if *test.role == auth.RoleInternal { 131 if u.Password != "foo" { 132 tt.Error("Password should not be empty for internal role") 133 } 134 } else { 135 if u.Password != "" { 136 tt.Error("Password should be empty for non internal role") 137 } 138 } 139 140 u, err := store.FindByID(context.Background(), "0") 141 if err != nil { 142 t.Error(err) 143 } 144 if u == nil { 145 t.Error("user should have been created") 146 } 147 } 148 }) 149 } 150 } 151 152 func TestAuthorizerUpdate(t *testing.T) { 153 for _, test := range []struct { 154 name string 155 userID string 156 role *auth.Role 157 user *user.User 158 wantError error 159 }{ 160 { 161 name: "UnauthorizedWithNoUserIDAndRole", 162 userID: "", 163 role: nil, 164 user: &user.User{}, 165 wantError: auth.ErrUnauthorized, 166 }, { 167 name: "Authorized:Internal->Admin", 168 role: &roleInternal, 169 user: &user.User{ID: "0", Role: auth.RoleAdmin, Password: "foo"}, 170 wantError: nil, 171 }, { 172 name: "Unauthorized:Admin->Internal", 173 userID: "0", 174 role: &roleAdmin, 175 user: &user.User{Role: auth.RoleInternal, Password: "foo"}, 176 wantError: auth.ErrUnauthorized, 177 }, { 178 name: "Authorized:Admin->Admin", 179 userID: "1", 180 role: &roleAdmin, 181 user: &user.User{ID: "0", Role: auth.RoleAdmin, Password: "foo"}, 182 wantError: nil, 183 }, { 184 name: "Unauthorized:Moderator->Internal", 185 userID: "1", 186 role: &roleModertor, 187 user: &user.User{ID: "0", Role: auth.RoleInternal, Password: "foo"}, 188 wantError: auth.ErrUnauthorized, 189 }, { 190 name: "Unauthorized:Moderator->Admin", 191 userID: "1", 192 role: &roleModertor, 193 user: &user.User{ID: "0", Role: auth.RoleAdmin, Password: "foo"}, 194 wantError: auth.ErrUnauthorized, 195 }, { 196 name: "Unauthorized:Moderator->Moderator", 197 userID: "1", 198 role: &roleModertor, 199 user: &user.User{ID: "0", Role: auth.RoleModerator}, 200 wantError: auth.ErrUnauthorized, 201 }, { 202 name: "Authorized:Moderator(self)->Moderator", 203 userID: "0", 204 role: &roleModertor, 205 user: &user.User{ID: "0", Role: auth.RoleModerator, Password: "foo"}, 206 wantError: nil, 207 }, { 208 name: "Authorized:Moderator->Normal (deactivated, role)", 209 userID: "0", 210 role: &roleModertor, 211 user: &user.User{ID: "0", Role: auth.RoleModerator, Nickname: "foo", Deactivated: true}, 212 wantError: nil, 213 }, { 214 name: "Unauthorized:Moderator->Normal (nickname, ..)", 215 userID: "1", 216 role: &roleModertor, 217 user: &user.User{ID: "0", Role: auth.RoleNormal, Nickname: "changed", Deactivated: false}, 218 wantError: auth.ErrUnauthorized, 219 }, { 220 name: "Unauthorized:Normal->Internal", 221 userID: "1", 222 role: &roleNormal, 223 user: &user.User{Role: auth.RoleInternal, Password: "foo"}, 224 wantError: auth.ErrUnauthorized, 225 }, { 226 name: "Unauthorized:Normal->Admin", 227 userID: "1", 228 role: &roleNormal, 229 user: &user.User{Role: auth.RoleAdmin, Password: "foo"}, 230 wantError: auth.ErrUnauthorized, 231 }, { 232 name: "Unauthorized:Normal->Moderator", 233 userID: "1", 234 role: &roleNormal, 235 user: &user.User{Role: auth.RoleModerator, Password: "foo"}, 236 wantError: auth.ErrUnauthorized, 237 }, { 238 name: "Unauthorized:Normal->Normal", 239 userID: "1", 240 role: &roleNormal, 241 user: &user.User{Role: auth.RoleNormal, Password: "foo"}, 242 wantError: auth.ErrUnauthorized, 243 }, { 244 name: "Authorized:Normal(self->Normal", 245 userID: "0", 246 role: &roleNormal, 247 user: &user.User{ID: "0", Role: auth.RoleNormal, Password: "foo"}, 248 wantError: nil, 249 }, 250 } { 251 t.Run(test.name, func(tt *testing.T) { 252 ctx := auth.ContextWithID(context.Background(), test.userID) 253 if test.role != nil { 254 ctx = auth.ContextWithRole(ctx, *test.role) 255 } 256 257 store := user.NewMemoryStore() 258 259 // ID: 0 test user that gets updated 260 _, err := store.Create(context.Background(), &user.NewUser{Role: test.user.Role}) 261 if err != nil { 262 t.Fatal(err) 263 } 264 // ID: 1 doer 265 if test.role != nil { 266 _, err = store.Create(context.Background(), &user.NewUser{Role: *test.role}) 267 if err != nil { 268 t.Fatal(err) 269 } 270 } 271 272 authorizer := user.NewAuthorizer(store) 273 274 u, err := authorizer.Update(ctx, test.user) 275 if err != test.wantError { 276 tt.Errorf("err != test.wantError: %s != %s", err, test.wantError) 277 } 278 279 if test.wantError == nil { 280 if *test.role == auth.RoleInternal { 281 if u.Password != "foo" { 282 tt.Error("Password should not be empty for internal role") 283 } 284 } else { 285 if u.Password != "" { 286 tt.Error("Password should be empty for non internal role") 287 } 288 } 289 290 u, err := store.FindByID(context.Background(), "0") 291 if err != nil { 292 t.Error(err) 293 } 294 if u == nil { 295 t.Error("user should have been created") 296 } 297 } 298 }) 299 } 300 } 301 302 func TestAuthorizerDelete(t *testing.T) { 303 for _, test := range []struct { 304 name string 305 userID string 306 role *auth.Role 307 wantError error 308 }{ 309 { 310 name: "UnauthorizedWithNoUserIDAndRole", 311 userID: "", 312 role: nil, 313 wantError: auth.ErrUnauthorized, 314 }, { 315 name: "Authorized:Internal", 316 userID: "1", 317 role: &roleInternal, 318 wantError: nil, 319 }, { 320 name: "Authorized:Admin", 321 userID: "1", 322 role: &roleAdmin, 323 wantError: nil, 324 }, { 325 name: "Unauthorized:Moderator", 326 userID: "1", 327 role: &roleModertor, 328 wantError: auth.ErrUnauthorized, 329 }, { 330 name: "Authorized:Moderator(self)", 331 userID: "0", 332 role: &roleModertor, 333 wantError: nil, 334 }, { 335 name: "Unauthorized:Normal", 336 userID: "1", 337 role: &roleNormal, 338 wantError: auth.ErrUnauthorized, 339 }, { 340 name: "Authorized:Normal(self)", 341 userID: "0", 342 role: &roleNormal, 343 wantError: nil, 344 }, 345 } { 346 t.Run(test.name, func(t *testing.T) { 347 ctx := auth.ContextWithID(context.Background(), test.userID) 348 if test.role != nil { 349 ctx = auth.ContextWithRole(ctx, *test.role) 350 } 351 352 store := user.NewMemoryStore() 353 // ID: 0 test user that gets deleted 354 _, err := store.Create(context.Background(), &user.NewUser{}) 355 if err != nil { 356 t.Fatal(err) 357 } 358 // ID: 1 doer 359 if test.role != nil { 360 _, err = store.Create(context.Background(), &user.NewUser{Role: *test.role}) 361 if err != nil { 362 t.Fatal(err) 363 } 364 } 365 366 authorizer := user.NewAuthorizer(store) 367 368 err = authorizer.Delete(ctx, "0") 369 if err != test.wantError { 370 t.Errorf("err != test.wantError: %s != %s", err, test.wantError) 371 } 372 373 if test.wantError == nil { 374 u, _ := store.FindByID(context.Background(), "0") 375 if u != nil { 376 t.Fatal("user should not exist anymore") 377 } 378 } 379 }) 380 } 381 } 382 383 func TestAuthorizerFindByID(t *testing.T) { 384 for _, test := range []struct { 385 name string 386 userID string 387 role *auth.Role 388 wantError error 389 }{ 390 { 391 name: "UnauthorizedWithNoUserIDAndRole", 392 userID: "", 393 role: nil, 394 wantError: auth.ErrUnauthorized, 395 }, { 396 name: "Authorized:Internal", 397 userID: "0", 398 role: &roleInternal, 399 wantError: nil, 400 }, { 401 name: "Authorized:Admin", 402 userID: "0", 403 role: &roleAdmin, 404 wantError: nil, 405 }, { 406 name: "Unauthorized:Moderator", 407 userID: "1", 408 role: &roleModertor, 409 wantError: nil, 410 }, { 411 name: "Unauthorized:Normal", 412 userID: "1", 413 role: &roleNormal, 414 wantError: auth.ErrUnauthorized, 415 }, { 416 name: "Authorized:Normal(self)", 417 userID: "0", 418 role: &roleNormal, 419 wantError: nil, 420 }, 421 } { 422 t.Run(test.name, func(tt *testing.T) { 423 ctx := auth.ContextWithID(context.Background(), test.userID) 424 if test.role != nil { 425 ctx = auth.ContextWithRole(ctx, *test.role) 426 } 427 428 memoryStore := user.NewMemoryStore() 429 role := auth.RoleNormal 430 if test.role != nil { 431 role = *test.role 432 } 433 _, err := memoryStore.Create(context.Background(), &user.NewUser{ 434 Role: role, 435 Password: "foo", 436 }) 437 if err != nil { 438 log.Fatal(err) 439 } 440 authorizer := user.NewAuthorizer(memoryStore) 441 442 u, err := authorizer.FindByID(ctx, "0") 443 if err != test.wantError { 444 tt.Errorf("err != test.wantError: %s != %s", err, test.wantError) 445 return 446 } 447 448 if test.wantError == nil { 449 if u == nil { 450 tt.Errorf("u should not be nil") 451 } else { 452 if *test.role == auth.RoleInternal { 453 if u.Password != "foo" { 454 tt.Error("Password should not be empty for internal role") 455 } 456 } else { 457 if u.Password != "" { 458 tt.Error("Password should be empty for non internal role") 459 } 460 } 461 } 462 } 463 }) 464 } 465 } 466 467 func TestAuthorizerFind(t *testing.T) { 468 user1 := &user.User{ 469 ID: "0", 470 Email: "secret", 471 Nickname: "bli", 472 Password: "foo", 473 Role: auth.RoleAdmin, 474 } 475 user2 := &user.User{ 476 ID: "1", 477 Email: "secret", 478 Nickname: "bla", 479 Password: "foo", 480 Role: auth.RoleModerator, 481 } 482 user3 := &user.User{ 483 ID: "2", 484 Email: "secret", 485 Nickname: "blub", 486 Password: "foo", 487 Role: auth.RoleNormal, 488 } 489 users := []*user.User{user1, user2, user3} 490 for _, test := range []struct { 491 name string 492 userID string 493 role *auth.Role 494 wantUsers []*user.User 495 wantError error 496 }{ 497 { 498 name: "UnauthorizedWithNoUserIDAndRole", 499 userID: "", 500 role: nil, 501 wantUsers: nil, 502 wantError: auth.ErrUnauthorized, 503 }, { 504 name: "Authorized:Internal", 505 userID: "foo", 506 role: &roleInternal, 507 wantUsers: users, 508 wantError: nil, 509 }, { 510 name: "Authorized:Admin", 511 userID: "foo", 512 role: &roleAdmin, 513 wantUsers: []*user.User{ 514 { 515 ID: "0", 516 Email: "secret", 517 Nickname: "bli", 518 Role: auth.RoleAdmin, 519 }, 520 { 521 ID: "1", 522 Email: "secret", 523 Nickname: "bla", 524 Role: auth.RoleModerator, 525 }, 526 { 527 ID: "2", 528 Email: "secret", 529 Nickname: "blub", 530 Role: auth.RoleNormal, 531 }, 532 }, 533 wantError: nil, 534 }, { 535 name: "Authorized:Moderator", 536 userID: "foo", 537 role: &roleModertor, 538 wantUsers: []*user.User{ 539 { 540 ID: "0", 541 Email: "secret", 542 Nickname: "bli", 543 Role: auth.RoleAdmin, 544 }, 545 { 546 ID: "1", 547 Email: "secret", 548 Nickname: "bla", 549 Role: auth.RoleModerator, 550 }, 551 { 552 ID: "2", 553 Email: "secret", 554 Nickname: "blub", 555 Role: auth.RoleNormal, 556 }, 557 }, 558 wantError: nil, 559 }, { 560 name: "Unauthorized:Normal", 561 userID: "foo", 562 role: &roleNormal, 563 wantUsers: []*user.User{ 564 {ID: "0", Nickname: "bli"}, 565 {ID: "1", Nickname: "bla"}, 566 {ID: "2", Nickname: "blub"}, 567 }, 568 wantError: nil, 569 }, 570 } { 571 t.Run(test.name, func(tt *testing.T) { 572 ctx := auth.ContextWithID(context.Background(), test.userID) 573 if test.role != nil { 574 ctx = auth.ContextWithRole(ctx, *test.role) 575 } 576 577 memoryStore := user.NewMemoryStore() 578 for _, u := range users { 579 memoryStore.Create(context.Background(), &user.NewUser{ 580 Nickname: u.Nickname, 581 Email: u.Email, 582 Password: u.Password, 583 Role: u.Role, 584 }) 585 } 586 authorizer := user.NewAuthorizer(memoryStore) 587 588 users, _, err := authorizer.Find(ctx, nil) 589 if err != test.wantError { 590 tt.Errorf("err != test.wantError: %s != %s", err, test.wantError) 591 } 592 593 if test.wantError == nil { 594 if diff := cmp.Diff(test.wantUsers, users, cmpopts.IgnoreFields(user.User{}, "CreatedAt")); diff != "" { 595 tt.Errorf("users return mismatch: %s", diff) 596 } 597 if *test.role == auth.RoleInternal { 598 for _, user := range users { 599 if user.Password != "foo" { 600 tt.Error("Password should not be empty for internal role") 601 } 602 } 603 } else { 604 for _, user := range users { 605 if user.Password != "" { 606 tt.Error("Password should be empty for non internal role") 607 } 608 } 609 } 610 } 611 }) 612 } 613 }