feature: flag permissions
feature: flag permissions
This commit is contained in:
@@ -78,14 +78,16 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateClaims(AuthorizationRole AuthorizationRole, string[] ClaimKeys)
|
||||
private void UpdateClaims(AuthorizationRole AuthorizationRole, string[] claimKeys)
|
||||
{
|
||||
var proposedClaims = Claims.BuildClaims(ClaimKeys);
|
||||
claimKeys = claimKeys ?? Array.Empty<string>();
|
||||
|
||||
var proposedClaims = Claims.BuildClaims(claimKeys);
|
||||
|
||||
var currentToken = RoleToken.FromAuthorizationRole(AuthorizationRole);
|
||||
var currentClaimKeys = Claims.GetClaimKeys(currentToken.Claims);
|
||||
var removedClaims = currentClaimKeys.Except(ClaimKeys).ToArray();
|
||||
var addedClaims = ClaimKeys.Except(currentClaimKeys).ToArray();
|
||||
var removedClaims = currentClaimKeys.Except(claimKeys).ToArray();
|
||||
var addedClaims = claimKeys.Except(currentClaimKeys).ToArray();
|
||||
|
||||
AuthorizationRole.SetClaims(proposedClaims);
|
||||
UserService.UpdateAuthorizationRole(Database, AuthorizationRole);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Web;
|
||||
using System;
|
||||
using System.Data.Entity;
|
||||
@@ -12,7 +11,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
public partial class DeviceFlagAssignmentController : AuthorizedDatabaseController
|
||||
{
|
||||
const string pComments = "comments";
|
||||
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult Update(int id, string key, string value = null, bool? redirect = null)
|
||||
{
|
||||
try
|
||||
@@ -21,7 +20,9 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
throw new ArgumentOutOfRangeException(nameof(id));
|
||||
if (string.IsNullOrEmpty(key))
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
var assignment = Database.DeviceFlagAssignments.FirstOrDefault(a => a.Id == id);
|
||||
var assignment = Database.DeviceFlagAssignments
|
||||
.Include(a => a.DeviceFlag)
|
||||
.FirstOrDefault(a => a.Id == id);
|
||||
if (assignment != null)
|
||||
{
|
||||
switch (key.ToLower())
|
||||
@@ -52,7 +53,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
|
||||
#region Update Shortcut Methods
|
||||
[DiscoAuthorizeAny(Claims.Device.Actions.EditFlags)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult UpdateComments(int id, string Comments = null, bool? redirect = null)
|
||||
{
|
||||
return Update(id, pComments, Comments, redirect);
|
||||
@@ -60,20 +61,19 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
#endregion
|
||||
|
||||
#region Update Properties
|
||||
private void UpdateComments(DeviceFlagAssignment assignment, string Comments)
|
||||
private void UpdateComments(DeviceFlagAssignment assignment, string comments)
|
||||
{
|
||||
if (!assignment.CanEditComments())
|
||||
if (!assignment.CanEdit())
|
||||
throw new InvalidOperationException("Editing comments for device flags is denied");
|
||||
|
||||
assignment.OnEditComments(Comments);
|
||||
assignment.OnEdit(comments);
|
||||
Database.SaveChanges();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Actions
|
||||
|
||||
[DiscoAuthorizeAny(Claims.Device.Actions.AddFlags)]
|
||||
public virtual ActionResult AddDevice(int id, string DeviceSerialNumber, string Comments)
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult AddDevice(int id, string deviceSerialNumber, string comments)
|
||||
{
|
||||
Database.Configuration.LazyLoadingEnabled = true;
|
||||
|
||||
@@ -81,37 +81,35 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
if (flag == null)
|
||||
throw new ArgumentException("Invalid Device Flag Id", nameof(id));
|
||||
|
||||
var device = Database.Devices.Include(u => u.DeviceFlagAssignments).FirstOrDefault(d => d.SerialNumber == DeviceSerialNumber);
|
||||
var device = Database.Devices.Include(u => u.DeviceFlagAssignments).FirstOrDefault(d => d.SerialNumber == deviceSerialNumber);
|
||||
if (device == null)
|
||||
throw new ArgumentException("Invalid Device Serial Number", nameof(DeviceSerialNumber));
|
||||
throw new ArgumentException("Invalid Device Serial Number", nameof(deviceSerialNumber));
|
||||
|
||||
if (!device.CanAddDeviceFlag(flag))
|
||||
throw new InvalidOperationException("Adding device flag is denied");
|
||||
return Unauthorized("Adding device flag is denied");
|
||||
|
||||
var addingUser = Database.Users.Find(CurrentUser.UserId);
|
||||
|
||||
var assignment = device.OnAddDeviceFlag(Database, flag, addingUser, Comments);
|
||||
var assignment = device.OnAddDeviceFlag(Database, flag, comments);
|
||||
|
||||
Database.SaveChanges();
|
||||
|
||||
return Redirect($"{Url.Action(MVC.Device.Show(device.SerialNumber))}#DeviceDetailTab-Flags");
|
||||
}
|
||||
|
||||
[DiscoAuthorizeAny(Claims.Device.Actions.RemoveFlags)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult RemoveDevice(int id)
|
||||
{
|
||||
Database.Configuration.LazyLoadingEnabled = true;
|
||||
|
||||
var assignment = Database.DeviceFlagAssignments.FirstOrDefault(a => a.Id == id);
|
||||
var assignment = Database.DeviceFlagAssignments
|
||||
.Include(a => a.DeviceFlag)
|
||||
.FirstOrDefault(a => a.Id == id);
|
||||
if (assignment == null)
|
||||
throw new ArgumentException("Invalid Device Flag Assignment Id", nameof(id));
|
||||
|
||||
if (!assignment.CanRemove())
|
||||
throw new InvalidOperationException("Removing device flag assignment is denied");
|
||||
return Unauthorized("Removing device flag assignment is denied");
|
||||
|
||||
var removingUser = Database.Users.Find(CurrentUser.UserId);
|
||||
|
||||
assignment.OnRemove(Database, removingUser);
|
||||
assignment.OnRemove(Database);
|
||||
Database.SaveChanges();
|
||||
|
||||
return Redirect($"{Url.Action(MVC.Device.Show(assignment.DeviceSerialNumber))}#DeviceDetailTab-Flags");
|
||||
@@ -120,4 +118,4 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ using Disco.Services.Devices.DeviceFlags;
|
||||
using Disco.Services.Exporting;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using Disco.Services.Tasks;
|
||||
using Disco.Services.Users.UserFlags;
|
||||
using Disco.Services.Web;
|
||||
using Disco.Web.Areas.API.Models.Shared;
|
||||
using Disco.Web.Areas.Config.Models.DeviceFlag;
|
||||
using Disco.Web.Extensions;
|
||||
using System;
|
||||
@@ -467,6 +469,24 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
return RedirectToAction(MVC.Config.Export.Create(savedExport.Id));
|
||||
}
|
||||
|
||||
[DiscoAuthorize(Claims.Config.DeviceFlag.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult Permission(int id, FlagPermissionModel model = null)
|
||||
{
|
||||
var deviceFlag = Database.DeviceFlags.Find(id);
|
||||
|
||||
if (deviceFlag == null)
|
||||
return NotFound();
|
||||
|
||||
if (model == null || !model.IsOverride)
|
||||
deviceFlag.Permissions = null;
|
||||
else
|
||||
deviceFlag.Permissions = model.ToFlagPermission(deviceFlag);
|
||||
|
||||
DeviceFlagService.Update(Database, deviceFlag);
|
||||
|
||||
return RedirectToAction(MVC.Config.DeviceFlag.Index(deviceFlag.Id));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,9 @@ using Disco.Services.Authorization;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using Disco.Services.Interop.DiscoServices;
|
||||
using Disco.Services.Messaging;
|
||||
using Disco.Services.Users;
|
||||
using Disco.Services.Web;
|
||||
using Disco.Web.Areas.API.Models.Shared;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
@@ -320,38 +322,64 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
};
|
||||
}
|
||||
|
||||
[DiscoAuthorizeAny(Claims.DiscoAdminAccount, Claims.Config.JobQueue.Configure)]
|
||||
public virtual ActionResult SearchSubjects(string term)
|
||||
[DiscoAuthorizeAny(Claims.DiscoAdminAccount, Claims.Config.JobQueue.Configure, Claims.Config.UserFlag.Configure, Claims.Config.DeviceFlag.Configure)]
|
||||
public virtual ActionResult SearchSubjects(string term, bool includeAuthorizationRoles = false)
|
||||
{
|
||||
var groupResults = ActiveDirectory.SearchADGroups(term).Cast<IADObject>();
|
||||
var userResults = ActiveDirectory.SearchADUserAccounts(term, true).Cast<IADObject>();
|
||||
var groupResults = ActiveDirectory.SearchADGroups(term).Select(r => SubjectDescriptorModel.FromActiveDirectoryObject(r));
|
||||
var userResults = ActiveDirectory.SearchADUserAccounts(term, true).Select(r => SubjectDescriptorModel.FromActiveDirectoryObject(r));
|
||||
|
||||
var results = groupResults.Concat(userResults).OrderBy(r => r.SamAccountName)
|
||||
.Select(r => Models.Shared.SubjectDescriptorModel.FromActiveDirectoryObject(r)).ToList();
|
||||
IEnumerable<SubjectDescriptorModel> roleResults;
|
||||
if (includeAuthorizationRoles)
|
||||
{
|
||||
roleResults = Database.AuthorizationRoles.AsNoTracking().Where(r => r.Name.Contains(term))
|
||||
.ToList()
|
||||
.Select(r => SubjectDescriptorModel.FromAuthorizationRole(r));
|
||||
}
|
||||
else
|
||||
roleResults = Enumerable.Empty<SubjectDescriptorModel>();
|
||||
|
||||
var results = groupResults.Concat(userResults).Concat(roleResults)
|
||||
.OrderBy(r => r.Id).ToList();
|
||||
|
||||
return Json(results, JsonRequestBehavior.AllowGet);
|
||||
}
|
||||
|
||||
[DiscoAuthorizeAny(Claims.Config.UserFlag.Configure)]
|
||||
[DiscoAuthorizeAny(Claims.DiscoAdminAccount, Claims.Config.DeviceProfile.Configure, Claims.Config.DocumentTemplate.Configure, Claims.Config.Plugin.Configure, Claims.Config.UserFlag.Configure, Claims.Config.DeviceFlag.Configure)]
|
||||
public virtual ActionResult SearchGroupSubjects(string term)
|
||||
{
|
||||
var groupResults = ActiveDirectory.SearchADGroups(term).Cast<IADObject>();
|
||||
|
||||
var results = groupResults.OrderBy(r => r.SamAccountName)
|
||||
.Select(r => Models.Shared.SubjectDescriptorModel.FromActiveDirectoryObject(r)).ToList();
|
||||
.Select(r => SubjectDescriptorModel.FromActiveDirectoryObject(r)).ToList();
|
||||
|
||||
return Json(results, JsonRequestBehavior.AllowGet);
|
||||
}
|
||||
|
||||
[DiscoAuthorizeAny(Claims.DiscoAdminAccount, Claims.Config.JobQueue.Configure)]
|
||||
public virtual ActionResult Subject(string Id)
|
||||
[DiscoAuthorizeAny(Claims.DiscoAdminAccount, Claims.Config.JobQueue.Configure, Claims.Config.UserFlag.Configure, Claims.Config.DeviceFlag.Configure)]
|
||||
public virtual ActionResult Subject(string Id, bool includeAuthorizationRoles = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Id))
|
||||
return Json(null, JsonRequestBehavior.AllowGet);
|
||||
|
||||
if (Id.StartsWith("[", StringComparison.Ordinal))
|
||||
{
|
||||
if (includeAuthorizationRoles && int.TryParse(Id.Trim('[', ']'), out var roleId))
|
||||
{
|
||||
var roleName = UserService.GetAuthorizationRoleName(roleId);
|
||||
if (roleName != null)
|
||||
{
|
||||
return Json(SubjectDescriptorModel.FromAuthorizationRole(roleId, roleName), JsonRequestBehavior.AllowGet);
|
||||
}
|
||||
}
|
||||
return Json(null, JsonRequestBehavior.AllowGet);
|
||||
}
|
||||
|
||||
var subject = ActiveDirectory.RetrieveADObject(Id, Quick: true);
|
||||
|
||||
if (subject == null)
|
||||
return Json(null, JsonRequestBehavior.AllowGet);
|
||||
else
|
||||
return Json(Models.Shared.SubjectDescriptorModel.FromActiveDirectoryObject(subject), JsonRequestBehavior.AllowGet);
|
||||
return Json(SubjectDescriptorModel.FromActiveDirectoryObject(subject), JsonRequestBehavior.AllowGet);
|
||||
}
|
||||
|
||||
[DiscoAuthorizeAny(Claims.Config.UserFlag.Configure, Claims.Config.DeviceFlag.Configure, Claims.Config.DeviceProfile.Configure, Claims.Config.DocumentTemplate.Configure)]
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
public partial class UserFlagAssignmentController : AuthorizedDatabaseController
|
||||
{
|
||||
const string pComments = "comments";
|
||||
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult Update(int id, string key, string value = null, bool? redirect = null)
|
||||
{
|
||||
try
|
||||
@@ -21,7 +21,9 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
throw new ArgumentOutOfRangeException(nameof(id));
|
||||
if (string.IsNullOrEmpty(key))
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
var userFlagAssignment = Database.UserFlagAssignments.FirstOrDefault(a => a.Id == id);
|
||||
var userFlagAssignment = Database.UserFlagAssignments
|
||||
.Include(a => a.UserFlag)
|
||||
.FirstOrDefault(a => a.Id == id);
|
||||
if (userFlagAssignment != null)
|
||||
{
|
||||
switch (key.ToLower())
|
||||
@@ -52,7 +54,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
|
||||
#region Update Shortcut Methods
|
||||
[DiscoAuthorizeAny(Claims.User.Actions.EditFlags)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult UpdateComments(int id, string Comments = null, bool? redirect = null)
|
||||
{
|
||||
return Update(id, pComments, Comments, redirect);
|
||||
@@ -60,19 +62,19 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
#endregion
|
||||
|
||||
#region Update Properties
|
||||
private void UpdateComments(UserFlagAssignment userFlagAssignment, string Comments)
|
||||
private void UpdateComments(UserFlagAssignment userFlagAssignment, string comments)
|
||||
{
|
||||
if (!userFlagAssignment.CanEditComments())
|
||||
if (!userFlagAssignment.CanEdit())
|
||||
throw new InvalidOperationException("Editing comments for user flags is denied");
|
||||
|
||||
userFlagAssignment.OnEditComments(Comments);
|
||||
userFlagAssignment.OnEdit(comments);
|
||||
Database.SaveChanges();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Actions
|
||||
|
||||
[DiscoAuthorizeAny(Claims.User.Actions.AddFlags)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult AddUser(int id, string UserId, string Comments)
|
||||
{
|
||||
Database.Configuration.LazyLoadingEnabled = true;
|
||||
@@ -86,32 +88,30 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
throw new ArgumentException("Invalid User Id", nameof(UserId));
|
||||
|
||||
if (!user.CanAddUserFlag(userFlag))
|
||||
throw new InvalidOperationException("Adding user flag is denied");
|
||||
return Unauthorized("Adding user flag is denied");
|
||||
|
||||
var addingUser = Database.Users.Find(CurrentUser.UserId);
|
||||
|
||||
var userFlagAssignment = user.OnAddUserFlag(Database, userFlag, addingUser, Comments);
|
||||
var userFlagAssignment = user.OnAddUserFlag(Database, userFlag, Comments);
|
||||
|
||||
Database.SaveChanges();
|
||||
|
||||
return Redirect($"{Url.Action(MVC.User.Show(user.UserId))}#UserDetailTab-Flags");
|
||||
}
|
||||
|
||||
[DiscoAuthorizeAny(Claims.User.Actions.RemoveFlags)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult RemoveUser(int id)
|
||||
{
|
||||
Database.Configuration.LazyLoadingEnabled = true;
|
||||
|
||||
var userFlagAssignment = Database.UserFlagAssignments.FirstOrDefault(a => a.Id == id);
|
||||
var userFlagAssignment = Database.UserFlagAssignments
|
||||
.Include(a => a.UserFlag)
|
||||
.FirstOrDefault(a => a.Id == id);
|
||||
if (userFlagAssignment == null)
|
||||
throw new ArgumentException("Invalid User Flag Assignment Id", nameof(id));
|
||||
|
||||
if (!userFlagAssignment.CanRemove())
|
||||
throw new InvalidOperationException("Removing user flag assignment is denied");
|
||||
return Unauthorized("Removing user flag assignment is denied");
|
||||
|
||||
var removingUser = Database.Users.Find(CurrentUser.UserId);
|
||||
|
||||
userFlagAssignment.OnRemove(Database, removingUser);
|
||||
userFlagAssignment.OnRemove(Database);
|
||||
Database.SaveChanges();
|
||||
|
||||
return Redirect($"{Url.Action(MVC.User.Show(userFlagAssignment.UserId))}#UserDetailTab-Flags");
|
||||
@@ -120,4 +120,4 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using Disco.Services.Interop.ActiveDirectory;
|
||||
using Disco.Services.Tasks;
|
||||
using Disco.Services.Users.UserFlags;
|
||||
using Disco.Services.Web;
|
||||
using Disco.Web.Areas.API.Models.Shared;
|
||||
using Disco.Web.Areas.Config.Models.UserFlag;
|
||||
using Disco.Web.Extensions;
|
||||
using System;
|
||||
@@ -467,6 +468,24 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
return RedirectToAction(MVC.Config.Export.Create(savedExport.Id));
|
||||
}
|
||||
|
||||
[DiscoAuthorize(Claims.Config.UserFlag.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult Permission(int id, FlagPermissionModel model = null)
|
||||
{
|
||||
var userFlag = Database.UserFlags.Find(id);
|
||||
|
||||
if (userFlag == null)
|
||||
return NotFound();
|
||||
|
||||
if (model == null || !model.IsOverride)
|
||||
userFlag.Permissions = null;
|
||||
else
|
||||
userFlag.Permissions = model.ToFlagPermission(userFlag);
|
||||
|
||||
UserFlagService.Update(Database, userFlag);
|
||||
|
||||
return RedirectToAction(MVC.Config.UserFlag.Index(userFlag.Id));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Disco.Models.Repository;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Disco.Web.Areas.API.Models.Shared
|
||||
{
|
||||
public class FlagPermissionModel
|
||||
{
|
||||
public bool IsOverride { get; set; }
|
||||
public bool Inherit { get; set; }
|
||||
public List<string> CanShow { get; set; }
|
||||
public List<string> CanAssign { get; set; }
|
||||
public List<string> CanEdit { get; set; }
|
||||
public List<string> CanRemove { get; set; }
|
||||
|
||||
public FlagPermission ToFlagPermission(UserFlag userFlag)
|
||||
=> FlagPermission.Create(userFlag, Inherit, CanShow, CanAssign, CanEdit, CanRemove);
|
||||
|
||||
public FlagPermission ToFlagPermission(DeviceFlag deviceFlag)
|
||||
=> FlagPermission.Create(deviceFlag, Inherit, CanShow, CanAssign, CanEdit, CanRemove);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
|
||||
namespace Disco.Web.Areas.API.Models.Shared
|
||||
{
|
||||
@@ -43,5 +44,19 @@ namespace Disco.Web.Areas.API.Models.Shared
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static SubjectDescriptorModel FromAuthorizationRole(int roleId, string roleName)
|
||||
{
|
||||
return new SubjectDescriptorModel()
|
||||
{
|
||||
Id = $"[{roleId}]",
|
||||
Name = roleName,
|
||||
Type = "role",
|
||||
IsGroup = true
|
||||
};
|
||||
}
|
||||
|
||||
public static SubjectDescriptorModel FromAuthorizationRole(AuthorizationRole role)
|
||||
=> FromAuthorizationRole(role.Id, role.Name);
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,9 @@ namespace Disco.Web.Areas.Config.Controllers
|
||||
m.ThemeColours = UIHelpers.ThemeColours;
|
||||
}
|
||||
|
||||
var (_, permission) = DeviceFlagService.GetDeviceFlag(m.DeviceFlag.Id);
|
||||
m.Permission = permission;
|
||||
|
||||
// UI Extensions
|
||||
UIExtensions.ExecuteExtensions<ConfigDeviceFlagShowModel>(ControllerContext, m);
|
||||
|
||||
@@ -121,7 +124,7 @@ namespace Disco.Web.Areas.Config.Controllers
|
||||
var m = new ExportModel()
|
||||
{
|
||||
Options = Database.DiscoConfiguration.DeviceFlags.LastExportOptions,
|
||||
DeviceFlags = DeviceFlagService.GetDeviceFlags(),
|
||||
DeviceFlags = DeviceFlagService.GetDeviceFlags().Select(f => f.flag).ToList(),
|
||||
};
|
||||
|
||||
m.Fields = ExportFieldsModel.Create(m.Options, DeviceFlagExportOptions.DefaultOptions(), nameof(DeviceFlagExportOptions.CurrentOnly));
|
||||
|
||||
@@ -46,6 +46,9 @@ namespace Disco.Web.Areas.Config.Controllers
|
||||
m.ThemeColours = UIHelpers.ThemeColours;
|
||||
}
|
||||
|
||||
var (flag, permission) = UserFlagService.GetUserFlag(m.UserFlag.Id);
|
||||
m.Permission = permission;
|
||||
|
||||
// UI Extensions
|
||||
UIExtensions.ExecuteExtensions<ConfigUserFlagShowModel>(ControllerContext, m);
|
||||
|
||||
@@ -122,7 +125,7 @@ namespace Disco.Web.Areas.Config.Controllers
|
||||
var m = new ExportModel()
|
||||
{
|
||||
Options = Database.DiscoConfiguration.UserFlags.LastExportOptions,
|
||||
UserFlags = UserFlagService.GetUserFlags(),
|
||||
UserFlags = UserFlagService.GetUserFlags().Select(f => f.flag).ToList(),
|
||||
};
|
||||
|
||||
m.Fields = ExportFieldsModel.Create(m.Options, UserFlagExportOptions.DefaultOptions(), nameof(UserFlagExportOptions.CurrentOnly));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Disco.Models.UI.Config.DeviceFlag;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.UI.Config.DeviceFlag;
|
||||
using Disco.Services.Devices.DeviceFlags;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -16,5 +17,7 @@ namespace Disco.Web.Areas.Config.Models.DeviceFlag
|
||||
|
||||
public IEnumerable<KeyValuePair<string, string>> Icons { get; set; }
|
||||
public IEnumerable<KeyValuePair<string, string>> ThemeColours { get; set; }
|
||||
|
||||
public FlagPermission Permission { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Disco.Models.UI.Config.UserFlag;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.UI.Config.UserFlag;
|
||||
using Disco.Services.Users.UserFlags;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -16,5 +17,7 @@ namespace Disco.Web.Areas.Config.Models.UserFlag
|
||||
|
||||
public IEnumerable<KeyValuePair<string, string>> Icons { get; set; }
|
||||
public IEnumerable<KeyValuePair<string, string>> ThemeColours { get; set; }
|
||||
|
||||
public FlagPermission Permission { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -237,7 +237,7 @@
|
||||
<div id="Config_AuthRoles_Claims_Tree">
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" id="Config_AuthRoles_Claims_SaveChanges" class="button small disabled" data-saveurl="@Url.Action(MVC.API.AuthorizationRole.UpdateClaims(Model.Token.Role.Id))">Save Changes</button>@AjaxHelpers.AjaxLoader()
|
||||
<button type="button" typeof="button" id="Config_AuthRoles_Claims_SaveChanges" class="button small disabled" data-saveurl="@Url.Action(MVC.API.AuthorizationRole.UpdateClaims(Model.Token.Role.Id))">Save Changes</button>@AjaxHelpers.AjaxLoader()
|
||||
</div>
|
||||
<script id="Config_AuthRoles_Claims_NodesJson" type="application/json">
|
||||
@Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.ClaimNavigatorFancyTreeNodes))
|
||||
@@ -286,7 +286,7 @@
|
||||
saveButton.addClass('disabled');
|
||||
ajaxLoading.next('.ajaxOk').show().delay('fast').fadeOut('slow');
|
||||
} else {
|
||||
alert('Unable to save changes:\n' + response);
|
||||
alert('Unable to save changes:\n' + response.statusText);
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Error: ' + e);
|
||||
|
||||
@@ -656,6 +656,8 @@ WriteLiteral(">\r\n </div>\r\n <div>\r\n
|
||||
|
||||
WriteLiteral(" type=\"button\"");
|
||||
|
||||
WriteLiteral(" typeof=\"button\"");
|
||||
|
||||
WriteLiteral(" id=\"Config_AuthRoles_Claims_SaveChanges\"");
|
||||
|
||||
WriteLiteral(" class=\"button small disabled\"");
|
||||
@@ -664,7 +666,7 @@ WriteLiteral(" data-saveurl=\"");
|
||||
|
||||
|
||||
#line 240 "..\..\Areas\Config\Views\AuthorizationRole\Show.cshtml"
|
||||
Write(Url.Action(MVC.API.AuthorizationRole.UpdateClaims(Model.Token.Role.Id)));
|
||||
Write(Url.Action(MVC.API.AuthorizationRole.UpdateClaims(Model.Token.Role.Id)));
|
||||
|
||||
|
||||
#line default
|
||||
@@ -675,7 +677,7 @@ WriteLiteral(">Save Changes</button>");
|
||||
|
||||
|
||||
#line 240 "..\..\Areas\Config\Views\AuthorizationRole\Show.cshtml"
|
||||
Write(AjaxHelpers.AjaxLoader());
|
||||
Write(AjaxHelpers.AjaxLoader());
|
||||
|
||||
|
||||
#line default
|
||||
@@ -728,13 +730,13 @@ WriteLiteral("\r\n </script>\r\n <script>\r\n
|
||||
"dClass(\'disabled\');\r\n ajaxLoading.nex" +
|
||||
"t(\'.ajaxOk\').show().delay(\'fast\').fadeOut(\'slow\');\r\n " +
|
||||
" } else {\r\n alert(\'Unable " +
|
||||
"to save changes:\\n\' + response);\r\n }\r\n " +
|
||||
" } catch (e) {\r\n " +
|
||||
" alert(\'Error: \' + e);\r\n }\r\n " +
|
||||
" ajaxLoading.hide();\r\n }" +
|
||||
"\r\n });\r\n });\r\n " +
|
||||
" })();\r\n </script>\r\n </td>\r\n </tr>\r\n </ta" +
|
||||
"ble>\r\n</div>\r\n<div");
|
||||
"to save changes:\\n\' + response.statusText);\r\n " +
|
||||
" }\r\n } catch (e) {\r\n " +
|
||||
" alert(\'Error: \' + e);\r\n }\r" +
|
||||
"\n ajaxLoading.hide();\r\n " +
|
||||
" }\r\n });\r\n });\r\n " +
|
||||
" })();\r\n </script>\r\n </td>\r\n </tr" +
|
||||
">\r\n </table>\r\n</div>\r\n<div");
|
||||
|
||||
WriteLiteral(" class=\"actionBar\"");
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
var canExportAll = Model.TotalAssignmentCount > 0 && Authorization.Has(Claims.Config.DeviceFlag.Export);
|
||||
|
||||
var hideAdvanced =
|
||||
Model.Permission.IsDefault() &&
|
||||
Model.DeviceFlag.DevicesLinkedGroup == null &&
|
||||
Model.DeviceFlag.DeviceUsersLinkedGroup == null &&
|
||||
Model.DeviceFlag.OnAssignmentExpression == null &&
|
||||
@@ -38,10 +39,11 @@
|
||||
</th>
|
||||
<td>
|
||||
@if (canConfig)
|
||||
{@Html.EditorFor(model => model.DeviceFlag.Name)
|
||||
@AjaxHelpers.AjaxSave()
|
||||
@AjaxHelpers.AjaxLoader()
|
||||
<script type="text/javascript">
|
||||
{
|
||||
@Html.EditorFor(model => model.DeviceFlag.Name)
|
||||
@AjaxHelpers.AjaxSave()
|
||||
@AjaxHelpers.AjaxLoader()
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
document.DiscoFunctions.PropertyChangeHelper(
|
||||
$('#DeviceFlag_Name'),
|
||||
@@ -50,12 +52,12 @@
|
||||
'FlagName'
|
||||
);
|
||||
});
|
||||
</script>
|
||||
}
|
||||
else
|
||||
{
|
||||
@Model.DeviceFlag.Name
|
||||
}
|
||||
</script>
|
||||
}
|
||||
else
|
||||
{
|
||||
@Model.DeviceFlag.Name
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -227,6 +229,340 @@
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
<tr class="Config_HideAdvanced_Item">
|
||||
<th>
|
||||
Assignment Permission<br />
|
||||
Override:
|
||||
</th>
|
||||
<td>
|
||||
@if (!Model.Permission.IsDefault())
|
||||
{
|
||||
var permission = Model.Permission;
|
||||
<div>
|
||||
@if (permission.Inherit)
|
||||
{
|
||||
<span><i class="fa fa-check-square-o"></i> Inheriting from Authorization Roles</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span><i class="fa fa-square-o"></i> Authorization Roles are Ignored</span>
|
||||
}
|
||||
</div>
|
||||
if (!permission.HasSubjects())
|
||||
{
|
||||
<span class="smallMessage">There are no users/groups associated with this permission override</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (permission.IsSimple())
|
||||
{
|
||||
<table class="tableData">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Users/Groups/Roles</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var subjectId in permission.CanShowSubjectIds)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@{
|
||||
int roleId;
|
||||
if (subjectId.StartsWith("[") && int.TryParse(subjectId.Trim('[', ']'), out roleId))
|
||||
{
|
||||
<span>@Disco.Services.Users.UserService.GetAuthorizationRoleName(roleId) @subjectId</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@subjectId</span>
|
||||
}
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="info-box">
|
||||
<p class="fa-p">
|
||||
<i class="fa fa-fw fa-info-circle"></i> All users/groups/roles can view, assign, edit assignments, and remove assignments for this flag.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
var subjects = permission.AllSubjects();
|
||||
<table class="tableData">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Users/Groups/Roles</th>
|
||||
<th>View</th>
|
||||
<th>Assign</th>
|
||||
<th>Edit</th>
|
||||
<th>Remove</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var subjectId in subjects.OrderBy(s => s))
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
<i class="fa"></i> @{
|
||||
int roleId;
|
||||
if (subjectId.StartsWith("[") && int.TryParse(subjectId.Trim('[', ']'), out roleId))
|
||||
{
|
||||
<span>@Disco.Services.Users.UserService.GetAuthorizationRoleName(roleId) @subjectId</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@subjectId</span>
|
||||
}
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (permission.CanShowSubjectIds.Contains(subjectId))
|
||||
{
|
||||
<i class="fa fa-fw fa-check"></i>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (permission.CanAssignSubjectIds.Contains(subjectId))
|
||||
{
|
||||
<i class="fa fa-fw fa-check"></i>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (permission.CanEditSubjectIds.Contains(subjectId))
|
||||
{
|
||||
<i class="fa fa-fw fa-check"></i>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (permission.CanRemoveSubjectIds.Contains(subjectId))
|
||||
{
|
||||
<i class="fa fa-fw fa-check"></i>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@if (canConfig)
|
||||
{
|
||||
var permission = Model.Permission;
|
||||
<button id="Config_Flag_Permission_Edit" class="button small">@(permission.IsDefault() ? "Override" : "Edit") Permission</button>
|
||||
|
||||
<div id="Config_Flag_Permissions" class="dialog" title="Flag Assignment Permission Override">
|
||||
@using (Html.BeginForm(MVC.API.DeviceFlag.Permission(Model.DeviceFlag.Id)))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<input type="hidden" name="IsOverride" value="true" />
|
||||
<div id="Config_Flag_Permissions_Inherit_Container">
|
||||
<label>
|
||||
<input id="Config_Flag_Permissions_Inherit" type="checkbox" name="Inherit" value="true" @(permission.Inherit ? "checked" : null) /> Inherit Authorization from Authorization Roles
|
||||
</label>
|
||||
</div>
|
||||
<div class="tableDataContainer">
|
||||
<table class="tableData">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User/Group/Role</th>
|
||||
<th>View</th>
|
||||
<th>Assign</th>
|
||||
<th>Edit</th>
|
||||
<th>Remove</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@{
|
||||
var subjects = permission.AllSubjects();
|
||||
|
||||
foreach (var subjectId in subjects.OrderBy(s => s))
|
||||
{
|
||||
<tr data-subjectid="@subjectId">
|
||||
<td><i class="fa type"></i> <span>@subjectId</span><i class="fa fa-times-circle remove"></i></td>
|
||||
<td>
|
||||
<input type="checkbox" name="CanShow" value="@subjectId" @(permission.CanShowSubjectIds.Contains(subjectId) ? " checked" : null) />
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" name="CanAssign" value="@subjectId" @(permission.CanAssignSubjectIds.Contains(subjectId) ? " checked" : null) />
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" name="CanEdit" value="@subjectId" @(permission.CanEditSubjectIds.Contains(subjectId) ? " checked" : null) />
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" name="CanRemove" value="@subjectId" @(permission.CanRemoveSubjectIds.Contains(subjectId) ? " checked" : null) />
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<input type="text" id="Config_Flag_Permissions_Subject_Input" placeholder="Search Users/Groups/Roles" data-autocompleteurl="@(Url.Action(MVC.API.System.SearchSubjects(null, includeAuthorizationRoles: true)))" />
|
||||
<button type="button" id="Config_Flag_Permissions_Subject_Add" class="button small" data-subjecturl="@Url.Action(MVC.API.System.Subject(null, includeAuthorizationRoles: true))">Add</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<script>
|
||||
$(function () {
|
||||
let $dialog = null;
|
||||
let originalSubjects = null;
|
||||
let originalInherit = null;
|
||||
$('#Config_Flag_Permission_Edit').on('click', async function () {
|
||||
if (!$dialog) {
|
||||
$dialog = $('#Config_Flag_Permissions');
|
||||
const $tbody = $dialog.find('tbody');
|
||||
const $inherit = $dialog.find('#Config_Flag_Permissions_Inherit');
|
||||
const $input = $dialog.find('#Config_Flag_Permissions_Subject_Input');
|
||||
const $add = $dialog.find('#Config_Flag_Permissions_Subject_Add');
|
||||
|
||||
$dialog.dialog({
|
||||
resizable: false,
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 550,
|
||||
height: 420,
|
||||
buttons: {
|
||||
Cancel: function () {
|
||||
$(this).dialog('close');
|
||||
$tbody.html(originalSubjects);
|
||||
$inherit.prop('checked', originalInherit);
|
||||
},
|
||||
'Remove Override': function () {
|
||||
const $this = $(this);
|
||||
$(this).dialog('option', 'buttons', null);
|
||||
$this.find('input[name="IsOverride"]').val('false');
|
||||
$this.find('form').trigger('submit');
|
||||
},
|
||||
'Save Changes': function () {
|
||||
$(this).dialog('option', 'buttons', null);
|
||||
$(this).find('form').trigger('submit');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$tbody.on('click', 'i.remove', function () {
|
||||
$(this).closest('tr').remove();
|
||||
});
|
||||
|
||||
$input
|
||||
.autocomplete({
|
||||
source: $input.attr('data-autocompleteurl'),
|
||||
minLength: 2,
|
||||
focus: function (e, ui) {
|
||||
$input.val(ui.item.Id);
|
||||
return false;
|
||||
},
|
||||
select: function (e, ui) {
|
||||
$input.val(ui.item.Id).blur();
|
||||
$add.trigger('click');
|
||||
$input.val('');
|
||||
return false;
|
||||
}
|
||||
}).data('ui-autocomplete')._renderItem = function (ul, item) {
|
||||
return $("<li></li>")
|
||||
.data('item.autocomplete', item)
|
||||
.append('<a><strong>' + item.Name + '</strong><br>' + item.Id + ' (' + item.Type + ')</a>')
|
||||
.appendTo(ul);
|
||||
};
|
||||
|
||||
$add.on('click', async function () {
|
||||
const value = $input.val();
|
||||
if (!value) {
|
||||
$input.focus();
|
||||
return;
|
||||
}
|
||||
const existing = $tbody.find('tr').filter(function () {
|
||||
return $(this).attr('data-subjectid') === value;
|
||||
});
|
||||
if (existing.length !== 0) {
|
||||
$input.val('');
|
||||
$input.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const body = new FormData();
|
||||
body.append('id', value);
|
||||
const response = await fetch($add.attr('data-subjecturl'), {
|
||||
body: body,
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
alert('Unable to lookup User/Group/Role: ' + response.statusText);
|
||||
$input.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const subject = await response.json();
|
||||
|
||||
if (!subject) {
|
||||
alert('Invalid User/Group/Role');
|
||||
$input.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const $record = $('<tr><td><i class="fa type"></i> <span></span><i class="fa fa-times-circle remove"></i></td><td><input type="checkbox" name="CanShow" checked /></td><td><input type="checkbox" name="CanAssign" checked /></td><td><input type="checkbox" name="CanEdit" checked /></td><td><input type="checkbox" name="CanRemove" checked /></td></tr>');
|
||||
$record.attr('data-subjectid', subject.Id);
|
||||
$record.find('span').text(subject.Name + ' ' + subject.Id);
|
||||
$record.find('i.type').addClass(subject.Type === 'role' ? 'fa-lock' : subject.IsGroup ? 'fa-users' : 'fa-user');
|
||||
$record.find('input').val(subject.Id);
|
||||
$tbody.append($record);
|
||||
$input.val('');
|
||||
$input.focus();
|
||||
});
|
||||
|
||||
const $records = $tbody.find('tr');
|
||||
for (var i = 0; i < $records.length; i++) {
|
||||
const $record = $($records[i]);
|
||||
const body = new FormData();
|
||||
body.append('id', $record.attr('data-subjectid'));
|
||||
const response = await fetch($add.attr('data-subjecturl'), {
|
||||
body: body,
|
||||
method: 'POST'
|
||||
});
|
||||
if (response.ok) {
|
||||
const subject = await response.json();
|
||||
if (subject) {
|
||||
$record.find('span').text(subject.Name + ' ' + subject.Id);
|
||||
$record.find('i.type').addClass(subject.Type === 'role' ? 'fa-lock' : subject.IsGroup ? 'fa-users' : 'fa-user');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$record.remove();
|
||||
}
|
||||
originalInherit = $inherit.prop('checked');
|
||||
originalSubjects = $tbody.html();
|
||||
$dialog.dialog('open');
|
||||
} else {
|
||||
$dialog.dialog('open');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
}
|
||||
<div class="info-box">
|
||||
<p class="fa-p">
|
||||
<i class="fa fa-fw fa-info-circle"></i> Flag actions are normally authorized globally by
|
||||
@if (Authorization.Has(Claims.DiscoAdminAccount))
|
||||
{
|
||||
<span><a href="@Url.Action(MVC.Config.AuthorizationRole.Index(null))">Authorization Roles</a>.</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>Authorization Roles.</span>
|
||||
}
|
||||
Overriding individual flag permissions allows for targeted authorization.
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="Config_HideAdvanced_Item">
|
||||
<th>
|
||||
On Assignment<br />Expression:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@
|
||||
var canExportAll = Model.TotalAssignmentCount > 0 && Authorization.Has(Claims.Config.UserFlag.Export);
|
||||
|
||||
var hideAdvanced =
|
||||
Model.Permission.IsDefault() &&
|
||||
Model.UserFlag.UserDevicesLinkedGroup == null &&
|
||||
Model.UserFlag.UsersLinkedGroup == null &&
|
||||
Model.UserFlag.OnAssignmentExpression == null &&
|
||||
@@ -229,6 +230,340 @@
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
<tr class="Config_HideAdvanced_Item">
|
||||
<th>
|
||||
Assignment Permission<br />
|
||||
Override:
|
||||
</th>
|
||||
<td>
|
||||
@if (!Model.Permission.IsDefault())
|
||||
{
|
||||
var permission = Model.Permission;
|
||||
<div>
|
||||
@if (permission.Inherit)
|
||||
{
|
||||
<span><i class="fa fa-check-square-o"></i> Inheriting from Authorization Roles</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span><i class="fa fa-square-o"></i> Authorization Roles are Ignored</span>
|
||||
}
|
||||
</div>
|
||||
if (!permission.HasSubjects())
|
||||
{
|
||||
<span class="smallMessage">There are no users/groups associated with this permission override</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (permission.IsSimple())
|
||||
{
|
||||
<table class="tableData">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Users/Groups/Roles</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var subjectId in permission.CanShowSubjectIds)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@{
|
||||
int roleId;
|
||||
if (subjectId.StartsWith("[") && int.TryParse(subjectId.Trim('[', ']'), out roleId))
|
||||
{
|
||||
<span>@Disco.Services.Users.UserService.GetAuthorizationRoleName(roleId) @subjectId</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@subjectId</span>
|
||||
}
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="info-box">
|
||||
<p class="fa-p">
|
||||
<i class="fa fa-fw fa-info-circle"></i> All users/groups/roles can view, assign, edit assignments, and remove assignments for this flag.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
var subjects = permission.AllSubjects();
|
||||
<table class="tableData">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Users/Groups/Roles</th>
|
||||
<th>View</th>
|
||||
<th>Assign</th>
|
||||
<th>Edit</th>
|
||||
<th>Remove</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var subjectId in subjects.OrderBy(s => s))
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
<i class="fa"></i> @{
|
||||
int roleId;
|
||||
if (subjectId.StartsWith("[") && int.TryParse(subjectId.Trim('[', ']'), out roleId))
|
||||
{
|
||||
<span>@Disco.Services.Users.UserService.GetAuthorizationRoleName(roleId) @subjectId</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@subjectId</span>
|
||||
}
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (permission.CanShowSubjectIds.Contains(subjectId))
|
||||
{
|
||||
<i class="fa fa-fw fa-check"></i>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (permission.CanAssignSubjectIds.Contains(subjectId))
|
||||
{
|
||||
<i class="fa fa-fw fa-check"></i>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (permission.CanEditSubjectIds.Contains(subjectId))
|
||||
{
|
||||
<i class="fa fa-fw fa-check"></i>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (permission.CanRemoveSubjectIds.Contains(subjectId))
|
||||
{
|
||||
<i class="fa fa-fw fa-check"></i>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@if (canConfig)
|
||||
{
|
||||
var permission = Model.Permission;
|
||||
<button id="Config_Flag_Permission_Edit" class="button small">@(permission.IsDefault() ? "Override" : "Edit") Permission</button>
|
||||
|
||||
<div id="Config_Flag_Permissions" class="dialog" title="Flag Assignment Permission Override">
|
||||
@using (Html.BeginForm(MVC.API.UserFlag.Permission(Model.UserFlag.Id)))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<input type="hidden" name="IsOverride" value="true" />
|
||||
<div id="Config_Flag_Permissions_Inherit_Container">
|
||||
<label>
|
||||
<input id="Config_Flag_Permissions_Inherit" type="checkbox" name="Inherit" value="true" @(permission.Inherit ? "checked" : null) /> Inherit Authorization from Authorization Roles
|
||||
</label>
|
||||
</div>
|
||||
<div class="tableDataContainer">
|
||||
<table class="tableData">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User/Group/Role</th>
|
||||
<th>View</th>
|
||||
<th>Assign</th>
|
||||
<th>Edit</th>
|
||||
<th>Remove</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@{
|
||||
var subjects = permission.AllSubjects();
|
||||
|
||||
foreach (var subjectId in subjects.OrderBy(s => s))
|
||||
{
|
||||
<tr data-subjectid="@subjectId">
|
||||
<td><i class="fa type"></i> <span>@subjectId</span><i class="fa fa-times-circle remove"></i></td>
|
||||
<td>
|
||||
<input type="checkbox" name="CanShow" value="@subjectId" @(permission.CanShowSubjectIds.Contains(subjectId) ? " checked" : null) />
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" name="CanAssign" value="@subjectId" @(permission.CanAssignSubjectIds.Contains(subjectId) ? " checked" : null) />
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" name="CanEdit" value="@subjectId" @(permission.CanEditSubjectIds.Contains(subjectId) ? " checked" : null) />
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" name="CanRemove" value="@subjectId" @(permission.CanRemoveSubjectIds.Contains(subjectId) ? " checked" : null) />
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<input type="text" id="Config_Flag_Permissions_Subject_Input" placeholder="Search Users/Groups" data-autocompleteurl="@(Url.Action(MVC.API.System.SearchSubjects(null, includeAuthorizationRoles: true)))" />
|
||||
<button type="button" id="Config_Flag_Permissions_Subject_Add" class="button small" data-subjecturl="@Url.Action(MVC.API.System.Subject(null, includeAuthorizationRoles: true))">Add</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<script>
|
||||
$(function () {
|
||||
let $dialog = null;
|
||||
let originalSubjects = null;
|
||||
let originalInherit = null;
|
||||
$('#Config_Flag_Permission_Edit').on('click', async function () {
|
||||
if (!$dialog) {
|
||||
$dialog = $('#Config_Flag_Permissions');
|
||||
const $tbody = $dialog.find('tbody');
|
||||
const $inherit = $dialog.find('#Config_Flag_Permissions_Inherit');
|
||||
const $input = $dialog.find('#Config_Flag_Permissions_Subject_Input');
|
||||
const $add = $dialog.find('#Config_Flag_Permissions_Subject_Add');
|
||||
|
||||
$dialog.dialog({
|
||||
resizable: false,
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 550,
|
||||
height: 420,
|
||||
buttons: {
|
||||
Cancel: function () {
|
||||
$(this).dialog('close');
|
||||
$tbody.html(originalSubjects);
|
||||
$inherit.prop('checked', originalInherit);
|
||||
},
|
||||
'Remove Override': function () {
|
||||
const $this = $(this);
|
||||
$(this).dialog('option', 'buttons', null);
|
||||
$this.find('input[name="IsOverride"]').val('false');
|
||||
$this.find('form').trigger('submit');
|
||||
},
|
||||
'Save Changes': function () {
|
||||
$(this).dialog('option', 'buttons', null);
|
||||
$(this).find('form').trigger('submit');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$tbody.on('click', 'i.remove', function () {
|
||||
$(this).closest('tr').remove();
|
||||
});
|
||||
|
||||
$input
|
||||
.autocomplete({
|
||||
source: $input.attr('data-autocompleteurl'),
|
||||
minLength: 2,
|
||||
focus: function (e, ui) {
|
||||
$input.val(ui.item.Id);
|
||||
return false;
|
||||
},
|
||||
select: function (e, ui) {
|
||||
$input.val(ui.item.Id).blur();
|
||||
$add.trigger('click');
|
||||
$input.val('');
|
||||
return false;
|
||||
}
|
||||
}).data('ui-autocomplete')._renderItem = function (ul, item) {
|
||||
return $("<li></li>")
|
||||
.data('item.autocomplete', item)
|
||||
.append('<a><strong>' + item.Name + '</strong><br>' + item.Id + ' (' + item.Type + ')</a>')
|
||||
.appendTo(ul);
|
||||
};
|
||||
|
||||
$add.on('click', async function () {
|
||||
const value = $input.val();
|
||||
if (!value) {
|
||||
$input.focus();
|
||||
return;
|
||||
}
|
||||
const existing = $tbody.find('tr').filter(function () {
|
||||
return $(this).attr('data-subjectid') === value;
|
||||
});
|
||||
if (existing.length !== 0) {
|
||||
$input.val('');
|
||||
$input.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const body = new FormData();
|
||||
body.append('id', value);
|
||||
const response = await fetch($add.attr('data-subjecturl'), {
|
||||
body: body,
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
alert('Unable to lookup User/Group/Role: ' + response.statusText);
|
||||
$input.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const subject = await response.json();
|
||||
|
||||
if (!subject) {
|
||||
alert('Invalid User/Group');
|
||||
$input.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const $record = $('<tr><td><i class="fa type"></i> <span></span><i class="fa fa-times-circle remove"></i></td><td><input type="checkbox" name="CanShow" checked /></td><td><input type="checkbox" name="CanAssign" checked /></td><td><input type="checkbox" name="CanEdit" checked /></td><td><input type="checkbox" name="CanRemove" checked /></td></tr>');
|
||||
$record.attr('data-subjectid', subject.Id);
|
||||
$record.find('span').text(subject.Name + ' ' + subject.Id);
|
||||
$record.find('i.type').addClass(subject.Type === 'role' ? 'fa-lock' : subject.IsGroup ? 'fa-users' : 'fa-user');
|
||||
$record.find('input').val(subject.Id);
|
||||
$tbody.append($record);
|
||||
$input.val('');
|
||||
$input.focus();
|
||||
});
|
||||
|
||||
const $records = $tbody.find('tr');
|
||||
for (var i = 0; i < $records.length; i++) {
|
||||
const $record = $($records[i]);
|
||||
const body = new FormData();
|
||||
body.append('id', $record.attr('data-subjectid'));
|
||||
const response = await fetch($add.attr('data-subjecturl'), {
|
||||
body: body,
|
||||
method: 'POST'
|
||||
});
|
||||
if (response.ok) {
|
||||
const subject = await response.json();
|
||||
if (subject) {
|
||||
$record.find('span').text(subject.Name + ' ' + subject.Id);
|
||||
$record.find('i.type').addClass(subject.Type === 'role' ? 'fa-lock' : subject.IsGroup ? 'fa-users' : 'fa-user');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$record.remove();
|
||||
}
|
||||
originalInherit = $inherit.prop('checked');
|
||||
originalSubjects = $tbody.html();
|
||||
$dialog.dialog('open');
|
||||
} else {
|
||||
$dialog.dialog('open');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
}
|
||||
<div class="info-box">
|
||||
<p class="fa-p">
|
||||
<i class="fa fa-fw fa-info-circle"></i> Flag actions are normally authorized globally by
|
||||
@if (Authorization.Has(Claims.DiscoAdminAccount))
|
||||
{
|
||||
<span><a href="@Url.Action(MVC.Config.AuthorizationRole.Index(null))">Authorization Roles</a>.</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>Authorization Roles.</span>
|
||||
}
|
||||
Overriding individual flag permissions allows for targeted authorization.
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="Config_HideAdvanced_Item">
|
||||
<th>
|
||||
On Assignment<br />Expression:
|
||||
@@ -369,7 +704,6 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="Config_HideAdvanced_Item">
|
||||
<th>
|
||||
Linked Groups:
|
||||
@@ -605,4 +939,4 @@
|
||||
@Html.ActionLinkButton(string.Format("Show {0} user{1}", Model.CurrentAssignmentCount, (Model.CurrentAssignmentCount == 1 ? null : "s")), MVC.Search.Query(Model.UserFlag.Id.ToString(), "UserFlag"), "Config_UserFlags_Actions_ShowUsers_Button")
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user