user_resource_guard_database_test.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. //go:build protectionintegration
  2. package repository
  3. import (
  4. "context"
  5. "testing"
  6. "time"
  7. entschema "entgo.io/ent/dialect/sql/schema"
  8. "github.com/Wei-Shaw/sub2api/ent/migrate"
  9. infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
  10. "github.com/Wei-Shaw/sub2api/internal/service"
  11. "github.com/stretchr/testify/require"
  12. )
  13. func TestUserResourceRolesAtFinalDatabaseWrite(t *testing.T) {
  14. client, db, _ := hierarchyTestDatabase(t)
  15. ctx := context.Background()
  16. require.NoError(t, migrate.Create(ctx, client.Schema, []*entschema.Table{migrate.UsersTable, migrate.GroupsTable, migrate.APIKeysTable, migrate.UserSubscriptionsTable}))
  17. admin := createHierarchyUser(t, client, "resource-admin@example.test", service.RoleAdmin)
  18. super := createHierarchyUser(t, client, "resource-super@example.test", service.RoleSuperAdmin)
  19. user := createHierarchyUser(t, client, "resource-user@example.test", service.RoleUser)
  20. group, err := client.Group.Create().SetName("subscription").SetSubscriptionType("subscription").Save(ctx)
  21. require.NoError(t, err)
  22. keys := &apiKeyRepository{client: client}
  23. subs := &userSubscriptionRepository{client: client}
  24. for _, target := range []struct {
  25. id int64
  26. want int
  27. }{{super.ID, 403}, {admin.ID, 403}, {user.ID, 200}} {
  28. key, err := client.APIKey.Create().SetName("key").SetKey("test-only-resource-key-" + time.Now().Format("150405.000000000")).SetUserID(target.id).SetGroupID(group.ID).Save(ctx)
  29. require.NoError(t, err)
  30. adminCtx := service.WithUserManagementActor(ctx, admin.ID)
  31. err = keys.Update(adminCtx, &service.APIKey{ID: key.ID, GroupID: nil}, service.APIKeyUpdateFields{GroupID: true})
  32. if target.want == 403 {
  33. require.Equal(t, 403, infraerrors.Code(err))
  34. } else {
  35. require.NoError(t, err)
  36. }
  37. persisted, err := client.APIKey.Get(ctx, key.ID)
  38. require.NoError(t, err)
  39. if target.want == 403 {
  40. require.Equal(t, group.ID, *persisted.GroupID)
  41. } else {
  42. require.Nil(t, persisted.GroupID)
  43. }
  44. sub, err := client.UserSubscription.Create().SetUserID(target.id).SetGroupID(group.ID).SetStartsAt(time.Now()).SetExpiresAt(time.Now().Add(time.Hour)).Save(ctx)
  45. require.NoError(t, err)
  46. err = subs.Delete(adminCtx, sub.ID)
  47. if target.want == 403 {
  48. require.Equal(t, 403, infraerrors.Code(err))
  49. } else {
  50. require.NoError(t, err)
  51. }
  52. if target.want == 403 {
  53. require.NoError(t, keys.Update(service.WithUserManagementActor(ctx, super.ID), &service.APIKey{ID: key.ID}, service.APIKeyUpdateFields{GroupID: true}))
  54. require.NoError(t, subs.Delete(service.WithUserManagementActor(ctx, super.ID), sub.ID))
  55. }
  56. }
  57. // Authorization is re-read after waiting for a concurrent role transition.
  58. key, err := client.APIKey.Create().SetName("late").SetKey("test-only-late-resource-key").SetUserID(user.ID).SetGroupID(group.ID).Save(ctx)
  59. require.NoError(t, err)
  60. gate, err := db.BeginTx(ctx, nil)
  61. require.NoError(t, err)
  62. defer gate.Rollback()
  63. _, err = gate.ExecContext(ctx, `SELECT pg_advisory_xact_lock($1)`, advisoryLockHash(userHierarchyLockKey))
  64. require.NoError(t, err)
  65. done := make(chan error, 1)
  66. go func() {
  67. done <- keys.Update(service.WithUserManagementActor(ctx, admin.ID), &service.APIKey{ID: key.ID}, service.APIKeyUpdateFields{GroupID: true})
  68. }()
  69. waitHierarchyLockWaiters(t, db, 1)
  70. _, err = gate.ExecContext(ctx, `UPDATE users SET role='user' WHERE id=$1`, admin.ID)
  71. require.NoError(t, err)
  72. require.NoError(t, gate.Commit())
  73. require.Equal(t, 403, infraerrors.Code(<-done))
  74. persisted, err := client.APIKey.Get(ctx, key.ID)
  75. require.NoError(t, err)
  76. require.Equal(t, group.ID, *persisted.GroupID)
  77. }