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 { /// /// This permission allows to manage user (creating, deleting or modifying). /// UserManagement, /// /// This permission allows to view and modify all timelines. /// AllTimelineManagement, /// /// This permission allow to add or remove highlight timelines. /// HighlightTimelineManagement } /// /// Represents a user's permissions. /// public class UserPermissions : IEnumerable, IEquatable { public static UserPermissions AllPermissions { get; } = new UserPermissions(Enum.GetValues()); /// /// Create an instance containing given permissions. /// /// Permission list. public UserPermissions(params UserPermission[] permissions) : this(permissions as IEnumerable) { } /// /// Create an instance containing given permissions. /// /// Permission list. /// Thrown when is null. public UserPermissions(IEnumerable permissions) { if (permissions == null) throw new ArgumentNullException(nameof(permissions)); _permissions = new SortedSet(permissions); } private readonly SortedSet _permissions = new(); /// /// Check if a permission is contained in the list. /// /// The permission to check. /// True if contains. Otherwise false. public bool Contains(UserPermission permission) { return _permissions.Contains(permission); } /// /// To a serializable string list. /// /// A string list. public List ToStringList() { return _permissions.Select(p => p.ToString()).ToList(); } /// /// Convert a string list to user permissions. /// /// The string list. /// An instance. /// Thrown when is null. /// Thrown when there is unknown permission name. public static UserPermissions FromStringList(IEnumerable list) { List permissions = new(); foreach (var value in list) { if (Enum.TryParse(value, false, out var result)) { permissions.Add(result); } else { throw new ArgumentException("Unknown permission name.", nameof(list)); } } return new UserPermissions(permissions); } public IEnumerator GetEnumerator() { return _permissions.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_permissions).GetEnumerator(); } public bool Equals(UserPermissions? other) { if (other == null) return false; return _permissions.SequenceEqual(other._permissions); } public override bool Equals(object? obj) { return Equals(obj as UserPermissions); } public override int GetHashCode() { int result = 0; foreach (var permission in Enum.GetValues()) { if (_permissions.Contains(permission)) { result += 1; } result <<= 1; } return result; } } public interface IUserPermissionService { /// /// Get permissions of a user. /// /// The id of the user. /// Whether check the user's existence. /// The permission list. /// Thrown when is true and user does not exist. Task GetPermissionsOfUserAsync(long userId, bool checkUserExistence = true); /// /// Add a permission to user. /// /// The id of the user. /// The new permission. /// Thrown when user does not exist. /// Thrown when change root user's permission. Task AddPermissionToUserAsync(long userId, UserPermission permission); /// /// Remove a permission from user. /// /// The id of the user. /// The permission. /// Whether check the user's existence. /// Thrown when is true and user does not exist. /// Thrown when change root user's permission. 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 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) { if (userId == 1) throw new InvalidOperationOnRootUserException("Can't change root user's permission."); await CheckUserExistence(userId, true); var alreadyHas = await _database.UserPermission .AnyAsync(e => e.UserId == userId && e.Permission == permission.ToString()); 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 = true) { if (userId == 1) throw new InvalidOperationOnRootUserException("Can't change root user's permission."); await CheckUserExistence(userId, checkUserExistence); var entity = await _database.UserPermission .Where(e => e.UserId == userId && e.Permission == permission.ToString()) .SingleOrDefaultAsync(); if (entity == null) return; _database.UserPermission.Remove(entity); await _database.SaveChangesAsync(); } } }