diff --git a/Gotrue/User.cs b/Gotrue/User.cs index 023fd2c..a6f2454 100644 --- a/Gotrue/User.cs +++ b/Gotrue/User.cs @@ -61,6 +61,9 @@ public class User [JsonProperty("updated_at")] public DateTime? UpdatedAt { get; set; } + [JsonProperty("banned_until")] + public DateTime? BannedUntil { get; set; } + [JsonProperty("is_anonymous")] public bool IsAnonymous { get; set; } @@ -103,6 +106,19 @@ public class AdminUserAttributes : UserAttributes /// [JsonProperty("phone_confirm")] public bool? PhoneConfirm { get; set; } + + /// + /// Determines how long a user is banned for. + /// This property is ignored when creating a user. + /// If you want to create a user banned, first create the user then update it sending this property. + /// The format for the ban duration follows a strict sequence of decimal numbers with a unit suffix. + /// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + /// For example, some possible durations include: '300ms', '2h45m', '1200s'. + /// Setting the ban duration to "none" lifts the ban on the user. + /// Only a service role can modify. + /// + [JsonProperty("ban_duration")] + public string? BanDuration { get; set; } } /// diff --git a/GotrueExample/GotrueExample.csproj b/GotrueExample/GotrueExample.csproj index 7436345..b43c349 100644 --- a/GotrueExample/GotrueExample.csproj +++ b/GotrueExample/GotrueExample.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/GotrueTests/GotrueTests.csproj b/GotrueTests/GotrueTests.csproj index d965618..381f704 100644 --- a/GotrueTests/GotrueTests.csproj +++ b/GotrueTests/GotrueTests.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 false diff --git a/GotrueTests/ServiceRoleTests.cs b/GotrueTests/ServiceRoleTests.cs index 6973e49..4c0b8ca 100644 --- a/GotrueTests/ServiceRoleTests.cs +++ b/GotrueTests/ServiceRoleTests.cs @@ -140,6 +140,30 @@ public async Task UpdateUserById() AreNotEqual(createdUser.Email, updatedUser.Email); } + [TestMethod("Service Role: Ban User by Id")] + public async Task BanUserById() + { + var createdUser = await _client.CreateUser($"{RandomString(12)}@supabase.io", PASSWORD); + + IsNotNull(createdUser); + + int banDurationSeconds = RandomNumber(); + DateTime bannedUntil = DateTime.UtcNow + TimeSpan.FromSeconds(banDurationSeconds); + var updatedUser = await _client.UpdateUserById(createdUser.Id ?? throw new InvalidOperationException(), new AdminUserAttributes { BanDuration = $"{banDurationSeconds}s" }); + + IsNotNull(updatedUser); + + AreEqual(createdUser.Id, updatedUser.Id); + IsNotNull(updatedUser.BannedUntil); + IsTrue((updatedUser.BannedUntil.Value - bannedUntil).Duration().TotalSeconds < 1); + + updatedUser = await _client.UpdateUserById(createdUser.Id ?? throw new InvalidOperationException(), new AdminUserAttributes { BanDuration = "none" }); + IsNotNull(updatedUser); + + AreEqual(createdUser.Id, updatedUser.Id); + IsFalse(updatedUser.BannedUntil.HasValue); + } + [TestMethod("Service Role: Delete User")] public async Task DeletesUser() { diff --git a/GotrueTests/TestUtils.cs b/GotrueTests/TestUtils.cs index d155edf..f1b40db 100644 --- a/GotrueTests/TestUtils.cs +++ b/GotrueTests/TestUtils.cs @@ -35,6 +35,17 @@ public static string GetRandomPhoneNumber() return $"+1{inner}"; } + /// + /// Returns a random number within the limits specified via parameters. + /// + /// Minimum value. Default 0. + /// Maximum value. Default 1000. + /// Integer within the range. + public static int RandomNumber(int minValue = 0, int maxValue = 1000) + { + return Random.Next(minValue, maxValue); + } + public static string GenerateServiceRoleToken() { var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("37c304f8-51aa-419a-a1af-06154e63707a")); // using GOTRUE_JWT_SECRET