aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BackEnd/Timeline.Tests/Services/DatabaseBasedTest.cs47
-rw-r--r--BackEnd/Timeline.Tests/Services/TimelineServiceTest.cs25
-rw-r--r--BackEnd/Timeline.Tests/Services/UserPermissionTest.cs28
-rw-r--r--BackEnd/Timeline/Entities/DatabaseContext.cs1
-rw-r--r--BackEnd/Timeline/Services/UserPermissionService.cs213
5 files changed, 295 insertions, 19 deletions
diff --git a/BackEnd/Timeline.Tests/Services/DatabaseBasedTest.cs b/BackEnd/Timeline.Tests/Services/DatabaseBasedTest.cs
new file mode 100644
index 00000000..838787e9
--- /dev/null
+++ b/BackEnd/Timeline.Tests/Services/DatabaseBasedTest.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Timeline.Entities;
+using Timeline.Tests.Helpers;
+using Xunit;
+
+namespace Timeline.Tests.Services
+{
+ public abstract class DatabaseBasedTest : IAsyncLifetime
+ {
+ protected TestDatabase TestDatabase { get; } = new TestDatabase();
+ protected DatabaseContext Database { get; private set; }
+
+ public async Task InitializeAsync()
+ {
+ await TestDatabase.InitializeAsync();
+ Database = TestDatabase.CreateContext();
+ await OnDatabaseCreatedAsync();
+ OnDatabaseCreated();
+ }
+
+ public async Task DisposeAsync()
+ {
+ BeforeDatabaseDestroy();
+ await BeforeDatabaseDestroyAsync();
+ await Database.DisposeAsync();
+ await TestDatabase.DisposeAsync();
+ }
+
+
+ protected virtual void OnDatabaseCreated() { }
+ protected virtual void BeforeDatabaseDestroy() { }
+
+
+ protected virtual Task OnDatabaseCreatedAsync()
+ {
+ return Task.CompletedTask;
+ }
+
+ protected virtual Task BeforeDatabaseDestroyAsync()
+ {
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/BackEnd/Timeline.Tests/Services/TimelineServiceTest.cs b/BackEnd/Timeline.Tests/Services/TimelineServiceTest.cs
index 5a774b78..19d2781a 100644
--- a/BackEnd/Timeline.Tests/Services/TimelineServiceTest.cs
+++ b/BackEnd/Timeline.Tests/Services/TimelineServiceTest.cs
@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using Timeline.Entities;
using Timeline.Models;
using Timeline.Services;
using Timeline.Services.Exceptions;
@@ -13,12 +12,8 @@ using Xunit;
namespace Timeline.Tests.Services
{
- public class TimelineServiceTest : IAsyncLifetime, IDisposable
+ public class TimelineServiceTest : DatabaseBasedTest, IDisposable
{
- private readonly TestDatabase _testDatabase = new TestDatabase();
-
- private DatabaseContext _databaseContext;
-
private readonly PasswordService _passwordService = new PasswordService();
private readonly ETagGenerator _eTagGenerator = new ETagGenerator();
@@ -39,20 +34,12 @@ namespace Timeline.Tests.Services
{
}
- public async Task InitializeAsync()
- {
- await _testDatabase.InitializeAsync();
- _databaseContext = _testDatabase.CreateContext();
- _dataManager = new DataManager(_databaseContext, _eTagGenerator);
- _userService = new UserService(NullLogger<UserService>.Instance, _databaseContext, _passwordService, _clock);
- _timelineService = new TimelineService(NullLogger<TimelineService>.Instance, _databaseContext, _dataManager, _userService, _imageValidator, _clock);
- _userDeleteService = new UserDeleteService(NullLogger<UserDeleteService>.Instance, _databaseContext, _timelineService);
- }
-
- public async Task DisposeAsync()
+ protected override void OnDatabaseCreated()
{
- await _testDatabase.DisposeAsync();
- await _databaseContext.DisposeAsync();
+ _dataManager = new DataManager(Database, _eTagGenerator);
+ _userService = new UserService(NullLogger<UserService>.Instance, Database, _passwordService, _clock);
+ _timelineService = new TimelineService(NullLogger<TimelineService>.Instance, Database, _dataManager, _userService, _imageValidator, _clock);
+ _userDeleteService = new UserDeleteService(NullLogger<UserDeleteService>.Instance, Database, _timelineService);
}
public void Dispose()
diff --git a/BackEnd/Timeline.Tests/Services/UserPermissionTest.cs b/BackEnd/Timeline.Tests/Services/UserPermissionTest.cs
new file mode 100644
index 00000000..4bcbc633
--- /dev/null
+++ b/BackEnd/Timeline.Tests/Services/UserPermissionTest.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Timeline.Entities;
+using Timeline.Services;
+using Timeline.Tests.Helpers;
+using Xunit;
+
+namespace Timeline.Tests.Services
+{
+ public class UserPermissionTest : DatabaseBasedTest
+ {
+ private UserPermissionService _service;
+
+ public UserPermissionTest()
+ {
+
+ }
+
+ protected override void OnDatabaseCreated()
+ {
+ _service = new UserPermissionService(Database);
+ }
+
+
+ }
+}
diff --git a/BackEnd/Timeline/Entities/DatabaseContext.cs b/BackEnd/Timeline/Entities/DatabaseContext.cs
index ecadd703..e4203392 100644
--- a/BackEnd/Timeline/Entities/DatabaseContext.cs
+++ b/BackEnd/Timeline/Entities/DatabaseContext.cs
@@ -25,6 +25,7 @@ namespace Timeline.Entities
public DbSet<UserEntity> Users { get; set; } = default!;
public DbSet<UserAvatarEntity> UserAvatars { get; set; } = default!;
+ public DbSet<UserPermissionEntity> UserPermission { get; set; } = default!;
public DbSet<TimelineEntity> Timelines { get; set; } = default!;
public DbSet<TimelinePostEntity> TimelinePosts { get; set; } = default!;
public DbSet<TimelineMemberEntity> TimelineMembers { get; set; } = default!;
diff --git a/BackEnd/Timeline/Services/UserPermissionService.cs b/BackEnd/Timeline/Services/UserPermissionService.cs
new file mode 100644
index 00000000..466ee252
--- /dev/null
+++ b/BackEnd/Timeline/Services/UserPermissionService.cs
@@ -0,0 +1,213 @@
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Timeline.Entities;
+using Timeline.Services.Exceptions;
+
+namespace Timeline.Services
+{
+ public enum UserPermission
+ {
+ /// <summary>
+ /// This permission allows to manage user (creating, deleting or modifying).
+ /// </summary>
+ UserManagement,
+ /// <summary>
+ /// This permission allows to view and modify all timelines.
+ /// </summary>
+ AllTimelineManagement,
+ /// <summary>
+ /// This permission allow to add or remove highlight timelines.
+ /// </summary>
+ HighlightTimelineManangement
+ }
+
+ /// <summary>
+ /// Represents a user's permissions.
+ /// </summary>
+ public class UserPermissions : IEnumerable<UserPermission>
+ {
+ public static UserPermissions AllPermissions { get; } = new UserPermissions(Enum.GetValues<UserPermission>());
+
+ /// <summary>
+ /// Create an instance containing given permissions.
+ /// </summary>
+ /// <param name="permissions">Permission list.</param>
+ public UserPermissions(params UserPermission[] permissions) : this(permissions as IEnumerable<UserPermission>)
+ {
+
+ }
+
+ /// <summary>
+ /// Create an instance containing given permissions.
+ /// </summary>
+ /// <param name="permissions">Permission list.</param>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="permissions"/> is null.</exception>
+ public UserPermissions(IEnumerable<UserPermission> permissions)
+ {
+ if (permissions == null) throw new ArgumentNullException(nameof(permissions));
+ _permissions = new HashSet<UserPermission>(permissions);
+ }
+
+ private readonly HashSet<UserPermission> _permissions = new();
+
+ /// <summary>
+ /// Check if a permission is contained in the list.
+ /// </summary>
+ /// <param name="permission">The permission to check.</param>
+ /// <returns>True if contains. Otherwise false.</returns>
+ public bool Contains(UserPermission permission)
+ {
+ return _permissions.Contains(permission);
+ }
+
+ /// <summary>
+ /// To a serializable string list.
+ /// </summary>
+ /// <returns>A string list.</returns>
+ public List<string> ToStringList()
+ {
+ return _permissions.Select(p => p.ToString().ToUpperInvariant()).ToList();
+ }
+
+ /// <summary>
+ /// Convert a string list to user permissions.
+ /// </summary>
+ /// <param name="list">The string list.</param>
+ /// <returns>An instance.</returns>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="list"/> is null.</exception>
+ /// <exception cref="ArgumentException">Thrown when there is unknown permission name.</exception>
+ public static UserPermissions FromStringList(IEnumerable<string> list)
+ {
+ List<UserPermission> permissions = new();
+
+ foreach (var value in list)
+ {
+ if (Enum.TryParse<UserPermission>(value, false, out var result))
+ {
+ permissions.Add(result);
+ }
+ else
+ {
+ throw new ArgumentException("Unknown permission name.", nameof(list));
+ }
+ }
+
+ return new UserPermissions(permissions);
+ }
+
+ public IEnumerator<UserPermission> GetEnumerator()
+ {
+ return _permissions.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((IEnumerable)_permissions).GetEnumerator();
+ }
+ }
+
+ public interface IUserPermissionService
+ {
+ /// <summary>
+ /// Get permissions of a user.
+ /// </summary>
+ /// <param name="userId">The id of the user.</param>
+ /// <param name="checkUserExistence">Whether check the user's existence.</param>
+ /// <returns>The permission list.</returns>
+ /// <exception cref="UserNotExistException">Thrown when <paramref name="checkUserExistence"/> is true and user does not exist.</exception>
+ Task<UserPermissions> GetPermissionsOfUserAsync(long userId, bool checkUserExistence = true);
+
+ /// <summary>
+ /// Add a permission to user.
+ /// </summary>
+ /// <param name="userId">The id of the user.</param>
+ /// <param name="permission">The new permission.</param>
+ /// <param name="checkUserExistence">Whether check the user's existence.</param>
+ /// <exception cref="UserNotExistException">Thrown when <paramref name="checkUserExistence"/> is true and user does not exist.</exception>
+ Task AddPermissionToUserAsync(long userId, UserPermission permission, bool checkUserExistence = true);
+
+ /// <summary>
+ /// Remove a permission from user.
+ /// </summary>
+ /// <param name="userId">The id of the user.</param>
+ /// <param name="permission">The permission.</param>
+ /// <param name="checkUserExistence">Whether check the user's existence.</param>
+ /// <exception cref="UserNotExistException">Thrown when <paramref name="checkUserExistence"/> is true and user does not exist.</exception>
+ Task RemovePermissionFromUserAsync(long userId, UserPermission permission, bool checkUserExistence = true);
+ }
+
+ public class UserPermissionService : IUserPermissionService
+ {
+ private readonly DatabaseContext _database;
+
+ public UserPermissionService(DatabaseContext database)
+ {
+ _database = database;
+ }
+
+ private async Task CheckUserExistence(long userId, bool checkUserExistence)
+ {
+ if (checkUserExistence)
+ {
+ var existence = await _database.Users.AnyAsync(u => u.Id == userId);
+ if (!existence)
+ {
+ throw new UserNotExistException(userId);
+ }
+ }
+ }
+
+ public async Task<UserPermissions> GetPermissionsOfUserAsync(long userId, bool checkUserExistence = true)
+ {
+ if (userId == 1) // The init administrator account.
+ {
+ return UserPermissions.AllPermissions;
+ }
+
+ await CheckUserExistence(userId, checkUserExistence);
+
+ var permissionNameList = await _database.UserPermission.Where(e => e.UserId == userId).Select(e => e.Permission).ToListAsync();
+
+ return UserPermissions.FromStringList(permissionNameList);
+ }
+
+ public async Task AddPermissionToUserAsync(long userId, UserPermission permission, bool checkUserExistence)
+ {
+ if (userId == 1) // The init administrator account.
+ return;
+
+ await CheckUserExistence(userId, checkUserExistence);
+
+ var alreadyHas = await _database.UserPermission
+ .AnyAsync(e => e.UserId == userId && e.Permission.Equals(permission.ToString(), StringComparison.InvariantCultureIgnoreCase));
+
+ if (alreadyHas) return;
+
+ _database.UserPermission.Add(new UserPermissionEntity { UserId = userId, Permission = permission.ToString() });
+
+ await _database.SaveChangesAsync();
+ }
+
+ public async Task RemovePermissionFromUserAsync(long userId, UserPermission permission, bool checkUserExistence)
+ {
+ if (userId == 1) // The init administrator account.
+ return;
+
+ await CheckUserExistence(userId, checkUserExistence);
+
+ var entity = await _database.UserPermission
+ .Where(e => e.UserId == userId && e.Permission.Equals(permission.ToString(), StringComparison.InvariantCultureIgnoreCase))
+ .SingleOrDefaultAsync();
+
+ if (entity == null) return;
+
+ _database.UserPermission.Remove(entity);
+
+ await _database.SaveChangesAsync();
+ }
+ }
+}