using Disco.Models.Repository; using Disco.Services; using Disco.Services.Authorization; 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.UserFlag; using Disco.Web.Extensions; using System; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web.Mvc; namespace Disco.Web.Areas.API.Controllers { public partial class UserFlagController : AuthorizedDatabaseController { private const string pName = "name"; private const string pDescription = "description"; private const string pIcon = "icon"; private const string pIconColour = "iconcolour"; private const string pDefaultRemoveDays = "defaultremovedays"; private const string pOnAssignmentExpression = "onassignmentexpression"; private const string pOnUnassignmentExpression = "onunassignmentexpression"; [DiscoAuthorize(Claims.Config.UserFlag.Configure)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult Update(int id, string key, string value = null, bool? redirect = null) { Authorization.Require(Claims.Config.UserFlag.Configure); try { if (id < 0) throw new ArgumentOutOfRangeException("id"); if (string.IsNullOrEmpty(key)) throw new ArgumentNullException("key"); var flag = Database.UserFlags.Find(id); if (flag != null) { switch (key.ToLower()) { case pName: UpdateName(flag, value); break; case pDescription: UpdateDescription(flag, value); break; case pIcon: UpdateIcon(flag, value); break; case pIconColour: UpdateIconColour(flag, value); break; case pDefaultRemoveDays: UpdateDefaultRemoveDays(flag, value); break; case pOnAssignmentExpression: UpdateOnAssignmentExpression(flag, value); break; case pOnUnassignmentExpression: UpdateOnUnassignmentExpression(flag, value); break; default: throw new Exception("Invalid Update Key"); } } else { throw new Exception("Invalid User Flag Id"); } if (redirect.HasValue && redirect.Value) return RedirectToAction(MVC.Config.UserFlag.Index(flag.Id)); else return Ok(); } catch (Exception ex) { if (redirect.HasValue && redirect.Value) throw; else return BadRequest(ex.Message); } } #region Update Shortcut Methods [DiscoAuthorize(Claims.Config.UserFlag.Configure)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult UpdateName(int id, string FlagName = null, bool? redirect = null) { return Update(id, pName, FlagName, redirect); } [DiscoAuthorize(Claims.Config.UserFlag.Configure)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult UpdateDescription(int id, string Description = null, bool? redirect = null) { return Update(id, pDescription, Description, redirect); } [DiscoAuthorize(Claims.Config.UserFlag.Configure)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult UpdateIcon(int id, string icon = null, bool? redirect = null) { return Update(id, pIcon, icon, redirect); } [DiscoAuthorize(Claims.Config.UserFlag.Configure)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult UpdateIconColour(int id, string iconColour = null, bool? redirect = null) { return Update(id, pIconColour, iconColour, redirect); } [DiscoAuthorize(Claims.Config.UserFlag.Configure)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult UpdateIconAndColour(int id, string icon = null, string iconColour = null, bool redirect = false) { try { if (id < 0) throw new ArgumentOutOfRangeException(nameof(id)); var UserFlag = Database.UserFlags.Find(id); if (UserFlag != null) { UpdateIconAndColour(UserFlag, icon, iconColour); } else { throw new ArgumentException("Invalid User Flag Id", nameof(id)); } if (redirect) return RedirectToAction(MVC.Config.UserFlag.Index(UserFlag.Id)); else return Ok(); } catch (Exception ex) { if (redirect) throw; else return BadRequest(ex.Message); } } [DiscoAuthorize(Claims.Config.UserFlag.Configure)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult UpdateDefaultRemoveDays(int id, [Range(1, int.MaxValue)] int? defaultRemoveDays = null, bool? redirect = null) { return Update(id, pDefaultRemoveDays, defaultRemoveDays?.ToString(), redirect); } [DiscoAuthorize(Claims.Config.UserFlag.Configure)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult UpdateOnAssignmentExpression(int id, string OnAssignmentExpression = null, bool redirect = false) { return Update(id, pOnAssignmentExpression, OnAssignmentExpression, redirect); } [DiscoAuthorize(Claims.Config.UserFlag.Configure)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult UpdateOnUnassignmentExpression(int id, string OnUnassignmentExpression = null, bool redirect = false) { return Update(id, pOnUnassignmentExpression, OnUnassignmentExpression, redirect); } [DiscoAuthorize(Claims.Config.UserFlag.Configure)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult UpdateAssignedUsersLinkedGroup(int id, string groupId = null, DateTime? filterBeginDate = null, bool? updateDescription = null, bool redirect = false) { try { if (id < 0) throw new ArgumentOutOfRangeException(nameof(id)); var userFlag = Database.UserFlags.Find(id) ?? throw new ArgumentException("Invalid User Flag Id", nameof(id)); var syncTaskStatus = UpdateAssignedUsersLinkedGroup(userFlag, groupId, filterBeginDate, updateDescription ?? true); if (redirect) if (syncTaskStatus == null) return RedirectToAction(MVC.Config.UserFlag.Index(userFlag.Id)); else { syncTaskStatus.SetFinishedUrl(Url.Action(MVC.Config.UserFlag.Index(userFlag.Id))); return RedirectToAction(MVC.Config.Logging.TaskStatus(syncTaskStatus.SessionId)); } else return Ok(); } catch (Exception ex) { if (redirect) throw; else return BadRequest(ex.Message); } } [DiscoAuthorize(Claims.Config.UserFlag.Configure)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult UpdateAssignedUserDevicesLinkedGroup(int id, string groupId = null, DateTime? filterBeginDate = null, bool? updateDescription = null, bool redirect = false) { try { if (id < 0) throw new ArgumentOutOfRangeException(nameof(id)); var userFlag = Database.UserFlags.Find(id) ?? throw new ArgumentException("Invalid User Flag Id", nameof(id)); var syncTaskStatus = UpdateAssignedUserDevicesLinkedGroup(userFlag, groupId, filterBeginDate, updateDescription ?? true); if (redirect) if (syncTaskStatus == null) return RedirectToAction(MVC.Config.UserFlag.Index(userFlag.Id)); else { syncTaskStatus.SetFinishedUrl(Url.Action(MVC.Config.UserFlag.Index(userFlag.Id))); return RedirectToAction(MVC.Config.Logging.TaskStatus(syncTaskStatus.SessionId)); } else return Ok(); } catch (Exception ex) { if (redirect) throw; else return BadRequest(ex.Message); } } #endregion #region Update Properties private void UpdateIconAndColour(UserFlag UserFlag, string Icon, string IconColour) { if (string.IsNullOrWhiteSpace(Icon)) throw new ArgumentNullException("Icon"); if (string.IsNullOrWhiteSpace(IconColour)) throw new ArgumentNullException("IconColour"); if (UserFlag.Icon != Icon || UserFlag.IconColour != IconColour) { UserFlag.Icon = Icon; UserFlag.IconColour = IconColour; UserFlagService.Update(Database, UserFlag); } } private void UpdateIcon(UserFlag UserFlag, string Icon) { if (string.IsNullOrWhiteSpace(Icon)) throw new ArgumentNullException("Icon"); if (UserFlag.Icon != Icon) { UserFlag.Icon = Icon; UserFlagService.Update(Database, UserFlag); } } private void UpdateIconColour(UserFlag UserFlag, string IconColour) { if (string.IsNullOrWhiteSpace(IconColour)) throw new ArgumentNullException("IconColour"); if (UserFlag.IconColour != IconColour) { UserFlag.IconColour = IconColour; UserFlagService.Update(Database, UserFlag); } } private void UpdateName(UserFlag UserFlag, string Name) { if (UserFlag.Name != Name) { UserFlag.Name = Name; UserFlagService.Update(Database, UserFlag); } } private void UpdateDescription(UserFlag UserFlag, string Description) { if (UserFlag.Description != Description) { UserFlag.Description = Description; UserFlagService.Update(Database, UserFlag); } } private void UpdateDefaultRemoveDays(UserFlag userFlag, string defaultRemoveDays) { if (string.IsNullOrWhiteSpace(defaultRemoveDays)) { userFlag.DefaultRemoveDays = null; } else { if (!int.TryParse(defaultRemoveDays, out var days) || days < 1) throw new ArgumentOutOfRangeException(nameof(defaultRemoveDays), "Unable to parse days"); userFlag.DefaultRemoveDays = days; } UserFlagService.Update(Database, userFlag); } private void UpdateOnAssignmentExpression(UserFlag UserFlag, string OnAssignmentExpression) { if (string.IsNullOrWhiteSpace(OnAssignmentExpression)) { UserFlag.OnAssignmentExpression = null; } else { UserFlag.OnAssignmentExpression = OnAssignmentExpression.Trim(); } // Invalidate Cache UserFlag.OnAssignmentExpressionInvalidateCache(); UserFlagService.Update(Database, UserFlag); } private void UpdateOnUnassignmentExpression(UserFlag UserFlag, string OnUnassignmentExpression) { if (string.IsNullOrWhiteSpace(OnUnassignmentExpression)) { UserFlag.OnUnassignmentExpression = null; } else { UserFlag.OnUnassignmentExpression = OnUnassignmentExpression.Trim(); } // Invalidate Cache UserFlag.OnUnassignmentExpressionInvalidateCache(); UserFlagService.Update(Database, UserFlag); } private ScheduledTaskStatus UpdateAssignedUsersLinkedGroup(UserFlag userFlag, string assignedUsersLinkedGroup, DateTime? filterBeginDate, bool updateDescription) { var configJson = ADManagedGroup.ValidConfigurationToJson(UserFlagUsersManagedGroup.GetKey(userFlag), assignedUsersLinkedGroup, filterBeginDate, updateDescription); if (userFlag.UsersLinkedGroup != configJson) { userFlag.UsersLinkedGroup = configJson; UserFlagService.Update(Database, userFlag); if (userFlag.UsersLinkedGroup != null) { // Sync Group if (UserFlagUsersManagedGroup.TryGetManagedGroup(userFlag, out var managedGroup)) { return ADManagedGroupsSyncTask.ScheduleSync(managedGroup); } } } return null; } private ScheduledTaskStatus UpdateAssignedUserDevicesLinkedGroup(UserFlag userFlag, string assignedUserDevicesLinkedGroup, DateTime? filterBeginDate, bool updateDescription) { var configJson = ADManagedGroup.ValidConfigurationToJson(UserFlagUserDevicesManagedGroup.GetKey(userFlag), assignedUserDevicesLinkedGroup, filterBeginDate, updateDescription); if (userFlag.UserDevicesLinkedGroup != configJson) { userFlag.UserDevicesLinkedGroup = configJson; UserFlagService.Update(Database, userFlag); if (userFlag.UserDevicesLinkedGroup != null) { // Sync Group if (UserFlagUserDevicesManagedGroup.TryGetManagedGroup(userFlag, out var managedGroup)) { return ADManagedGroupsSyncTask.ScheduleSync(managedGroup); } } } return null; } #endregion #region Actions [DiscoAuthorizeAll(Claims.Config.UserFlag.Configure, Claims.Config.UserFlag.Delete)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult Delete(int id, bool? redirect = false) { try { var uf = Database.UserFlags.FirstOrDefault(f => f.Id == id); if (uf != null) { var status = UserFlagDeleteTask.ScheduleNow(uf.Id); status.SetFinishedUrl(Url.Action(MVC.Config.UserFlag.Index(null))); if (redirect.HasValue && redirect.Value) return RedirectToAction(MVC.Config.Logging.TaskStatus(status.SessionId)); else return Ok(); } throw new Exception("Invalid User Flag Id"); } catch (Exception ex) { if (redirect.HasValue && redirect.Value) throw; else return BadRequest(ex.Message); } } [DiscoAuthorizeAll(Claims.Config.UserFlag.Configure, Claims.User.Actions.AddFlags, Claims.User.Actions.RemoveFlags, Claims.User.ShowFlagAssignments)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult BulkAssignUsers(int id, bool Override, string UserIds = null, string Comments = null) { if (id < 0) throw new ArgumentNullException("id"); var userFlag = Database.UserFlags.FirstOrDefault(f => f.Id == id); if (userFlag == null) throw new ArgumentException("Invalid User Flag Id", "id"); var userIds = UserIds.Split(new string[] { Environment.NewLine, ",", ";" }, StringSplitOptions.RemoveEmptyEntries).Select(d => d.Trim()).Where(d => !string.IsNullOrEmpty(d)).ToList(); var taskStatus = UserFlagBulkAssignTask.ScheduleBulkAssignUsers(userFlag, CurrentUser, Comments, userIds, Override); taskStatus.SetFinishedUrl(Url.Action(MVC.Config.UserFlag.Index(userFlag.Id))); return RedirectToAction(MVC.Config.Logging.TaskStatus(taskStatus.SessionId)); } [DiscoAuthorizeAll(Claims.Config.UserFlag.Configure, Claims.User.Actions.AddFlags, Claims.User.Actions.RemoveFlags, Claims.User.ShowFlagAssignments)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult AssignedUsers(int id) { if (id < 0) throw new ArgumentNullException("id"); var userFlag = Database.UserFlags.FirstOrDefault(f => f.Id == id); if (userFlag == null) throw new ArgumentException("Invalid User Flag Id", "id"); var assignedUsers = Database.UserFlagAssignments.Where(a => a.UserFlagId == userFlag.Id && !a.RemovedDate.HasValue).OrderBy(a => a.UserId).Select(a => a.UserId).ToList(); return Json(assignedUsers, JsonRequestBehavior.AllowGet); } #endregion #region Exporting [DiscoAuthorize(Claims.Config.UserFlag.Export)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult Export(ExportModel model) { if (model == null || model.Options == null) throw new ArgumentNullException(nameof(model)); Database.DiscoConfiguration.UserFlags.LastExportOptions = model.Options; Database.SaveChanges(); // Start Export var exportContext = new UserFlagExport(model.Options); var taskContext = ExportTask.ScheduleNowCacheResult(exportContext, id => Url.Action(MVC.Config.UserFlag.Export(id, null, null))); // Try waiting for completion if (taskContext.TaskStatus.WaitUntilFinished(TimeSpan.FromSeconds(2))) return RedirectToAction(MVC.Config.UserFlag.Export(taskContext.Id, null, null)); else return RedirectToAction(MVC.Config.Logging.TaskStatus(taskContext.TaskStatus.SessionId)); } [DiscoAuthorize(Claims.Config.UserFlag.Export)] public virtual ActionResult ExportRetrieve(Guid id) { if (!ExportTask.TryFromCache(id, out var context)) throw new ArgumentException("The export id specified is invalid, or the export data expired (60 minutes)", nameof(id)); if (context.Result == null || context.Result.Result == null) throw new ArgumentException("The export session is still running, or failed to complete successfully", nameof(id)); if (context.Result.RecordCount == 0) throw new ArgumentException("No records were found to export", nameof(id)); var fileStream = context.Result.Result; return this.File(fileStream.GetBuffer(), 0, (int)fileStream.Length, context.Result.MimeType, context.Result.Filename); } [DiscoAuthorizeAll(Claims.Config.ManageSavedExports, Claims.Config.UserFlag.Export)] [HttpPost, ValidateAntiForgeryToken] public virtual ActionResult SaveExport(ExportModel model) { Database.DiscoConfiguration.UserFlags.LastExportOptions = model.Options; var export = new UserFlagExport(model.Options); var savedExport = SavedExports.SaveExport(export, Database, CurrentUser); 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 } }