feature: flag permissions
feature: flag permissions
This commit is contained in:
@@ -8,7 +8,7 @@ namespace Disco.Services.Devices.DeviceFlags
|
||||
{
|
||||
internal class Cache
|
||||
{
|
||||
private ConcurrentDictionary<int, DeviceFlag> _Cache;
|
||||
private ConcurrentDictionary<int, (DeviceFlag, FlagPermission permission)> cache;
|
||||
|
||||
public Cache(DiscoDataContext Database)
|
||||
{
|
||||
@@ -26,36 +26,30 @@ namespace Disco.Services.Devices.DeviceFlags
|
||||
var flags = Database.DeviceFlags.ToList();
|
||||
|
||||
// Add Queues to In-Memory Cache
|
||||
_Cache = new ConcurrentDictionary<int, DeviceFlag>(flags.Select(f => new KeyValuePair<int, DeviceFlag>(f.Id, f)));
|
||||
cache = new ConcurrentDictionary<int, (DeviceFlag, FlagPermission permission)>(flags.Select(f => new KeyValuePair<int, (DeviceFlag, FlagPermission permission)>(f.Id, (f, f.Permissions))));
|
||||
}
|
||||
|
||||
public DeviceFlag GetDeviceFlag(int deviceFlagId)
|
||||
public (DeviceFlag flag, FlagPermission permission) GetDeviceFlag(int deviceFlagId)
|
||||
{
|
||||
if (_Cache.TryGetValue(deviceFlagId, out var item))
|
||||
if (cache.TryGetValue(deviceFlagId, out var item))
|
||||
return item;
|
||||
else
|
||||
return null;
|
||||
return (null, null);
|
||||
}
|
||||
public List<DeviceFlag> GetDeviceFlags()
|
||||
public List<(DeviceFlag flag, FlagPermission permission)> GetDeviceFlags()
|
||||
{
|
||||
return _Cache.Values.ToList();
|
||||
return cache.Values.ToList();
|
||||
}
|
||||
|
||||
public void AddOrUpdate(DeviceFlag flag)
|
||||
{
|
||||
_Cache.AddOrUpdate(flag.Id, flag, (key, existingItem) => flag);
|
||||
var value = (flag, flag.Permissions);
|
||||
cache.AddOrUpdate(flag.Id, value, (key, existingItem) => value);
|
||||
}
|
||||
|
||||
public DeviceFlag Remove(int deviceFlagId)
|
||||
public void Remove(int deviceFlagId)
|
||||
{
|
||||
if (_Cache.TryRemove(deviceFlagId, out var item))
|
||||
return item;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
public DeviceFlag Remove(DeviceFlag deviceFlag)
|
||||
{
|
||||
return Remove(deviceFlag.Id);
|
||||
cache.TryRemove(deviceFlagId, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Devices.DeviceFlags;
|
||||
using Disco.Services.Expressions;
|
||||
using Disco.Services.Logging;
|
||||
using Disco.Services.Users;
|
||||
@@ -15,16 +16,18 @@ namespace Disco.Services
|
||||
{
|
||||
|
||||
#region Edit Comments
|
||||
public static bool CanEditComments(this DeviceFlagAssignment fa)
|
||||
public static bool CanEdit(this DeviceFlagAssignment fa)
|
||||
{
|
||||
return UserService.CurrentAuthorization.Has(Claims.Device.Actions.EditFlags);
|
||||
var (_, permission) = DeviceFlagService.GetDeviceFlag(fa.DeviceFlagId);
|
||||
|
||||
return permission.CanEdit();
|
||||
}
|
||||
public static void OnEditComments(this DeviceFlagAssignment fa, string Comments)
|
||||
public static void OnEdit(this DeviceFlagAssignment fa, string comments)
|
||||
{
|
||||
if (!fa.CanEditComments())
|
||||
if (!fa.CanEdit())
|
||||
throw new InvalidOperationException("Editing comments for device flags is denied");
|
||||
|
||||
fa.Comments = string.IsNullOrWhiteSpace(Comments) ? null : Comments.Trim();
|
||||
fa.Comments = string.IsNullOrWhiteSpace(comments) ? null : comments.Trim();
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -34,36 +37,38 @@ namespace Disco.Services
|
||||
if (fa.RemovedDate.HasValue)
|
||||
return false;
|
||||
|
||||
return UserService.CurrentAuthorization.Has(Claims.Device.Actions.RemoveFlags);
|
||||
var (_, permission) = DeviceFlagService.GetDeviceFlag(fa.DeviceFlagId);
|
||||
|
||||
return permission.CanRemove();
|
||||
}
|
||||
public static void OnRemove(this DeviceFlagAssignment fa, DiscoDataContext Database, User RemovingUser)
|
||||
public static void OnRemove(this DeviceFlagAssignment fa, DiscoDataContext database)
|
||||
{
|
||||
if (!fa.CanRemove())
|
||||
throw new InvalidOperationException("Removing device flags is denied");
|
||||
|
||||
fa.OnRemoveUnsafe(Database, RemovingUser);
|
||||
fa.OnRemoveUnsafe(database, UserService.CurrentUser);
|
||||
}
|
||||
|
||||
public static void OnRemoveUnsafe(this DeviceFlagAssignment fa, DiscoDataContext Database, User RemovingUser)
|
||||
public static void OnRemoveUnsafe(this DeviceFlagAssignment fa, DiscoDataContext database, User removingUser)
|
||||
{
|
||||
fa = Database.DeviceFlagAssignments
|
||||
fa = database.DeviceFlagAssignments
|
||||
.Include(a => a.DeviceFlag)
|
||||
.First(a => a.Id == fa.Id);
|
||||
RemovingUser = Database.Users.First(u => u.UserId == RemovingUser.UserId);
|
||||
removingUser = database.Users.First(u => u.UserId == removingUser.UserId);
|
||||
|
||||
fa.RemovedDate = DateTime.Now;
|
||||
fa.RemovedUserId = RemovingUser.UserId;
|
||||
fa.RemovedUserId = removingUser.UserId;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(fa.DeviceFlag.OnUnassignmentExpression))
|
||||
{
|
||||
try
|
||||
{
|
||||
Database.SaveChanges();
|
||||
var expressionResult = fa.EvaluateOnUnassignmentExpression(Database, RemovingUser, fa.AddedDate);
|
||||
database.SaveChanges();
|
||||
var expressionResult = fa.EvaluateOnUnassignmentExpression(database, removingUser, fa.AddedDate);
|
||||
if (!string.IsNullOrWhiteSpace(expressionResult))
|
||||
{
|
||||
fa.OnUnassignmentExpressionResult = expressionResult;
|
||||
Database.SaveChanges();
|
||||
database.SaveChanges();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -75,58 +80,52 @@ namespace Disco.Services
|
||||
#endregion
|
||||
|
||||
#region Add
|
||||
public static bool CanAddDeviceFlags(this Device d)
|
||||
{
|
||||
return UserService.CurrentAuthorization.Has(Claims.Device.Actions.AddFlags);
|
||||
}
|
||||
public static bool CanAddDeviceFlag(this Device d, DeviceFlag flag)
|
||||
{
|
||||
// Shortcut
|
||||
if (!d.CanAddDeviceFlags())
|
||||
return false;
|
||||
|
||||
// Already has Device Flag?
|
||||
if (d.DeviceFlagAssignments.Any(fa => !fa.RemovedDate.HasValue && fa.DeviceFlagId == flag.Id))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
var (_, permission) = DeviceFlagService.GetDeviceFlag(flag.Id);
|
||||
|
||||
return permission.CanAssign();
|
||||
}
|
||||
public static DeviceFlagAssignment OnAddDeviceFlag(this Device d, DiscoDataContext Database, DeviceFlag flag, User AddingUser, string Comments)
|
||||
public static DeviceFlagAssignment OnAddDeviceFlag(this Device d, DiscoDataContext database, DeviceFlag flag, string comments)
|
||||
{
|
||||
if (!d.CanAddDeviceFlag(flag))
|
||||
throw new InvalidOperationException("Adding device flag is denied");
|
||||
|
||||
return d.OnAddDeviceFlagUnsafe(Database, flag, AddingUser, Comments);
|
||||
return d.OnAddDeviceFlagUnsafe(database, flag, UserService.CurrentUser, comments);
|
||||
}
|
||||
|
||||
public static DeviceFlagAssignment OnAddDeviceFlagUnsafe(this Device d, DiscoDataContext Database, DeviceFlag flag, User AddingUser, string Comments)
|
||||
public static DeviceFlagAssignment OnAddDeviceFlagUnsafe(this Device d, DiscoDataContext database, DeviceFlag flag, User addingUser, string comments)
|
||||
{
|
||||
flag = Database.DeviceFlags.First(f => f.Id == flag.Id);
|
||||
d = Database.Devices.First(de => de.SerialNumber == d.SerialNumber);
|
||||
AddingUser = Database.Users.First(user => user.UserId == AddingUser.UserId);
|
||||
flag = database.DeviceFlags.First(f => f.Id == flag.Id);
|
||||
d = database.Devices.First(de => de.SerialNumber == d.SerialNumber);
|
||||
addingUser = database.Users.First(user => user.UserId == addingUser.UserId);
|
||||
|
||||
var fa = new DeviceFlagAssignment()
|
||||
{
|
||||
DeviceFlag = flag,
|
||||
Device = d,
|
||||
AddedDate = DateTime.Now,
|
||||
AddedUser = AddingUser,
|
||||
AddedUserId = AddingUser.UserId,
|
||||
Comments = string.IsNullOrWhiteSpace(Comments) ? null : Comments.Trim()
|
||||
AddedUser = addingUser,
|
||||
AddedUserId = addingUser.UserId,
|
||||
Comments = string.IsNullOrWhiteSpace(comments) ? null : comments.Trim()
|
||||
};
|
||||
|
||||
Database.DeviceFlagAssignments.Add(fa);
|
||||
database.DeviceFlagAssignments.Add(fa);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(flag.OnAssignmentExpression))
|
||||
{
|
||||
try
|
||||
{
|
||||
Database.SaveChanges();
|
||||
var expressionResult = fa.EvaluateOnAssignmentExpression(Database, AddingUser, fa.AddedDate);
|
||||
database.SaveChanges();
|
||||
var expressionResult = fa.EvaluateOnAssignmentExpression(database, addingUser, fa.AddedDate);
|
||||
if (!string.IsNullOrWhiteSpace(expressionResult))
|
||||
{
|
||||
fa.OnAssignmentExpressionResult = expressionResult;
|
||||
Database.SaveChanges();
|
||||
database.SaveChanges();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Disco.Services.Devices.DeviceFlags
|
||||
{
|
||||
public static class DeviceFlagService
|
||||
{
|
||||
private static Cache _cache;
|
||||
private static Cache cache;
|
||||
internal static Lazy<IObservable<RepositoryMonitorEvent>> DeviceFlagAssignmentRepositoryEvents;
|
||||
|
||||
static DeviceFlagService()
|
||||
@@ -31,18 +31,51 @@ namespace Disco.Services.Devices.DeviceFlags
|
||||
|
||||
public static void Initialize(DiscoDataContext database)
|
||||
{
|
||||
_cache = new Cache(database);
|
||||
cache = new Cache(database);
|
||||
|
||||
// Initialize Managed Groups (if configured)
|
||||
_cache.GetDeviceFlags().ForEach(uf =>
|
||||
cache.GetDeviceFlags().ForEach(uf =>
|
||||
{
|
||||
DeviceFlagDevicesManagedGroup.Initialize(uf);
|
||||
DeviceFlagDeviceAssignedUsersManagedGroup.Initialize(uf);
|
||||
DeviceFlagDevicesManagedGroup.Initialize(uf.flag);
|
||||
DeviceFlagDeviceAssignedUsersManagedGroup.Initialize(uf.flag);
|
||||
});
|
||||
}
|
||||
|
||||
public static List<DeviceFlag> GetDeviceFlags() { return _cache.GetDeviceFlags(); }
|
||||
public static DeviceFlag GetDeviceFlag(int deviceFlagId) { return _cache.GetDeviceFlag(deviceFlagId); }
|
||||
public static IEnumerable<(DeviceFlag flag, FlagPermission permission)> GetDeviceFlags() { return cache.GetDeviceFlags(); }
|
||||
public static (DeviceFlag flag, FlagPermission permission) GetDeviceFlag(int deviceFlagId) { return cache.GetDeviceFlag(deviceFlagId); }
|
||||
|
||||
public static DeviceFlag GetAvailableUserFlag(int deviceFlagId, Device targetDevice)
|
||||
{
|
||||
var (deviceFlag, permission) = cache.GetDeviceFlag(deviceFlagId);
|
||||
|
||||
if (targetDevice.DeviceFlagAssignments
|
||||
.Where(a => a.DeviceFlagId == deviceFlagId && !a.RemovedDate.HasValue).Any())
|
||||
return null;
|
||||
|
||||
if (permission.CanAssign())
|
||||
return deviceFlag;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IEnumerable<DeviceFlag> GetAvailableDeviceFlags(Device targetDevice)
|
||||
{
|
||||
var records = cache.GetDeviceFlags();
|
||||
|
||||
var usedFlags = targetDevice.DeviceFlagAssignments
|
||||
.Where(a => !a.RemovedDate.HasValue)
|
||||
.Select(a => a.DeviceFlagId)
|
||||
.ToList();
|
||||
|
||||
foreach (var (flag, permission) in records)
|
||||
{
|
||||
if (usedFlags.Contains(flag.Id))
|
||||
continue;
|
||||
|
||||
if (permission.CanAssign())
|
||||
yield return flag;
|
||||
}
|
||||
}
|
||||
|
||||
#region Device Flag Maintenance
|
||||
public static DeviceFlag CreateDeviceFlag(DiscoDataContext database, string name, string description)
|
||||
@@ -52,7 +85,7 @@ namespace Disco.Services.Devices.DeviceFlags
|
||||
throw new ArgumentException("The Device Flag Name is required", nameof(name));
|
||||
|
||||
// Name Unique
|
||||
if (_cache.GetDeviceFlags().Any(f => f.Name.Equals(name, StringComparison.Ordinal)))
|
||||
if (cache.GetDeviceFlags().Any(f => f.flag.Name.Equals(name, StringComparison.Ordinal)))
|
||||
throw new ArgumentException("Another Device Flag already exists with that name", nameof(name));
|
||||
|
||||
// Clone to break reference
|
||||
@@ -67,7 +100,7 @@ namespace Disco.Services.Devices.DeviceFlags
|
||||
database.DeviceFlags.Add(flag);
|
||||
database.SaveChanges();
|
||||
|
||||
_cache.AddOrUpdate(flag);
|
||||
cache.AddOrUpdate(flag);
|
||||
|
||||
return flag;
|
||||
}
|
||||
@@ -78,12 +111,12 @@ namespace Disco.Services.Devices.DeviceFlags
|
||||
throw new ArgumentException("The Device Flag Name is required", nameof(deviceFlag));
|
||||
|
||||
// Name Unique
|
||||
if (_cache.GetDeviceFlags().Any(f => f.Id != deviceFlag.Id && f.Name == deviceFlag.Name))
|
||||
if (cache.GetDeviceFlags().Any(f => f.flag.Id != deviceFlag.Id && f.flag.Name == deviceFlag.Name))
|
||||
throw new ArgumentException("Another Device Flag already exists with that name", nameof(deviceFlag));
|
||||
|
||||
database.SaveChanges();
|
||||
|
||||
_cache.AddOrUpdate(deviceFlag);
|
||||
cache.AddOrUpdate(deviceFlag);
|
||||
DeviceFlagDevicesManagedGroup.Initialize(deviceFlag);
|
||||
DeviceFlagDeviceAssignedUsersManagedGroup.Initialize(deviceFlag);
|
||||
|
||||
@@ -113,7 +146,7 @@ namespace Disco.Services.Devices.DeviceFlags
|
||||
database.SaveChanges();
|
||||
|
||||
// Remove from Cache
|
||||
_cache.Remove(deviceFlagId);
|
||||
cache.Remove(deviceFlagId);
|
||||
|
||||
status.Finished($"Successfully Deleted Device Flag: '{flag.Name}' [{flag.Id}]");
|
||||
}
|
||||
@@ -140,7 +173,7 @@ namespace Disco.Services.Devices.DeviceFlags
|
||||
{
|
||||
status.UpdateStatus((chunkIndexOffset + index) * progressInterval, $"Assigning Flag: {device}");
|
||||
|
||||
return device.OnAddDeviceFlag(database, deviceFlag, technician, comments);
|
||||
return device.OnAddDeviceFlagUnsafe(database, deviceFlag, technician, comments);
|
||||
}).ToList();
|
||||
|
||||
// Save Chunk Items to Database
|
||||
@@ -206,7 +239,7 @@ namespace Disco.Services.Devices.DeviceFlags
|
||||
{
|
||||
status.UpdateStatus((chunkIndexOffset + index) * progressInterval, $"Assigning Flag: {device}");
|
||||
|
||||
return device.OnAddDeviceFlag(database, deviceFlag, technician, comments);
|
||||
return device.OnAddDeviceFlagUnsafe(database, deviceFlag, technician, comments);
|
||||
}).ToList();
|
||||
|
||||
// Save Chunk Items to Database
|
||||
@@ -229,11 +262,11 @@ namespace Disco.Services.Devices.DeviceFlags
|
||||
|
||||
public static string RandomUnusedIcon()
|
||||
{
|
||||
return UIHelpers.RandomIcon(_cache.GetDeviceFlags().Select(f => f.Icon));
|
||||
return UIHelpers.RandomIcon(cache.GetDeviceFlags().Select(f => f.flag.Icon));
|
||||
}
|
||||
public static string RandomUnusedThemeColour()
|
||||
{
|
||||
return UIHelpers.RandomThemeColour(_cache.GetDeviceFlags().Select(f => f.IconColour));
|
||||
return UIHelpers.RandomThemeColour(cache.GetDeviceFlags().Select(f => f.flag.IconColour));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user