#region License Information
/* HeuristicLab
* Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using HeuristicLab.Clients.Access;
using HeuristicLab.Common.Resources;
using HeuristicLab.Core.Views;
using HeuristicLab.MainForm;
namespace HeuristicLab.Clients.Hive.Administrator.Views {
[View("ProjectView")]
[Content(typeof(Project), IsDefaultView = false)]
public partial class ProjectPermissionsView : ItemView {
private const int userImageIndex = 0;
private const int userGroupImageIndex = 1;
private readonly HashSet assignedPermissions = new HashSet();
private readonly HashSet inheritedPermissions = new HashSet();
private readonly HashSet newAssignedPermissions = new HashSet();
private readonly HashSet newInheritedPermissions = new HashSet();
private readonly Dictionary> userGroupAncestors = new Dictionary>();
private readonly Dictionary> userGroupDescendants = new Dictionary>();
private IEnumerable addedPermissions;
private IEnumerable removedPermissions;
private IEnumerable addedIncludes;
private IEnumerable removedIncludes;
private readonly Color addedAssignmentColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1
private readonly Color removedAssignmentColor = Color.FromArgb(255, 236, 159, 72); // #ec9f48
private readonly Color addedIncludeColor = Color.FromArgb(25, 169, 221, 221); // #a9dddd
private readonly Color removedIncludeColor = Color.FromArgb(25, 249, 210, 145); // #f9d291
public new Project Content {
get { return (Project)base.Content; }
set { base.Content = value; }
}
public ProjectPermissionsView() {
InitializeComponent();
treeView.ImageList.Images.Add(VSImageLibrary.User);
treeView.ImageList.Images.Add(VSImageLibrary.UserAccounts);
}
#region Overrides
protected override void OnContentChanged() {
base.OnContentChanged();
if (Content == null) {
assignedPermissions.Clear();
inheritedPermissions.Clear();
newAssignedPermissions.Clear();
newInheritedPermissions.Clear();
treeView.Nodes.Clear();
detailsViewHost.Content = null;
} else {
UpdatePermissionList();
detailsViewHost.ActiveView.Locked = true;
}
}
protected override void SetEnabledStateOfControls() {
base.SetEnabledStateOfControls();
bool enabled = Content != null && !Locked && !ReadOnly;
inheritButton.Enabled = enabled;
saveButton.Enabled = enabled;
treeView.Enabled = enabled;
}
#endregion
#region Event Handlers
private void ProjectPermissionsView_Load(object sender, EventArgs e) {
}
private void refreshButton_Click(object sender, EventArgs e) {
UpdatePermissionList();
}
private async void inheritButton_Click(object sender, EventArgs e) {
await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
action: () => SetGrantedProjectPermissions(Content.Id, newAssignedPermissions.Select(x => x.Id), false, true, false));
UpdatePermissionList();
}
private async void saveButton_Click(object sender, EventArgs e) {
await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
action: () => SetGrantedProjectPermissions(Content.Id, newAssignedPermissions.Select(x => x.Id), false, false, false));
UpdatePermissionList();
}
private void treeView_AfterSelect(object sender, TreeViewEventArgs e) {
var selectedPermission = (UserGroupBase)e.Node.Tag;
detailsViewHost.Content = selectedPermission;
}
private void treeView_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
var checkedPermission = (UserGroupBase)e.Node.Tag;
if (e.Node.Parent == null || newInheritedPermissions.Contains(checkedPermission) || checkedPermission.Id == Guid.Empty) e.Cancel = true;
}
private void treeView_AfterCheck(object sender, TreeViewEventArgs e) {
var checkedPermission = (UserGroupBase)e.Node.Tag;
if (e.Node.Checked)
newAssignedPermissions.Add(checkedPermission);
else
newAssignedPermissions.Remove(checkedPermission);
UpdateNewPermissionList();
}
#endregion
#region Helpers
private void UpdatePermissionList() {
UpdateUserGroupGenealogy();
UpdateAssignedPermissions();
UpdateInheritedPermissions();
var top = BuildPermissionsList(AccessClient.Instance.UsersAndGroups);
detailsViewHost.Content = top;
}
private void UpdateNewPermissionList() {
UpdateNewAssignedPermissions();
UpdateNewInheritedPermissions();
var top = BuildPermissionsList(AccessClient.Instance.UsersAndGroups);
detailsViewHost.Content = top;
}
private void UpdateAssignedPermissions() {
assignedPermissions.Clear();
newAssignedPermissions.Clear();
var grantedPermissions = GetGrantedPermissionsForProject(Content.Id);
foreach (var r in grantedPermissions) {
assignedPermissions.Add(r);
newAssignedPermissions.Add(r);
}
}
private void UpdateNewAssignedPermissions() {
for(int i = newAssignedPermissions.Count-1; i >= 0; i--) {
if(newAssignedPermissions.Intersect(userGroupAncestors[newAssignedPermissions.ElementAt(i).Id]).Any()) {
newAssignedPermissions.Remove(newAssignedPermissions.ElementAt(i));
}
}
}
private void UpdateInheritedPermissions() {
inheritedPermissions.Clear();
newInheritedPermissions.Clear();
foreach(var item in assignedPermissions) {
if(userGroupDescendants.ContainsKey(item.Id)) {
foreach(var descendant in userGroupDescendants[item.Id]) {
inheritedPermissions.Add(descendant);
newInheritedPermissions.Add(descendant);
}
}
}
}
private void UpdateNewInheritedPermissions() {
newInheritedPermissions.Clear();
foreach(var item in newAssignedPermissions) {
if(userGroupDescendants.ContainsKey(item.Id)) {
foreach(var descendant in userGroupDescendants[item.Id]) {
newInheritedPermissions.Add(descendant);
}
}
}
}
private void UpdateUserGroupGenealogy() {
userGroupAncestors.Clear();
userGroupDescendants.Clear();
var usersAndGroups = AccessClient.Instance.UsersAndGroups;
foreach (var ug in usersAndGroups) {
userGroupAncestors.Add(ug.Id, new HashSet());
userGroupDescendants.Add(ug.Id, new HashSet());
}
var userGroupTree = HiveServiceLocator.Instance.CallHiveService(s => s.GetUserGroupTree());
foreach(var branch in userGroupTree) {
var parent = usersAndGroups.Where(x => x.Id == branch.Key).SingleOrDefault();
if(parent != null) {
var userGroupsToAdd = usersAndGroups.Where(x => userGroupTree[parent.Id].Contains(x.Id));
foreach (var node in userGroupsToAdd) {
userGroupDescendants[parent.Id].Add(node);
userGroupAncestors[node.Id].Add(parent);
}
}
}
}
private static IEnumerable GetGrantedPermissionsForProject(Guid projectId) {
if (projectId == Guid.Empty) return Enumerable.Empty();
var projectPermissions = HiveServiceLocator.Instance.CallHiveService(s => s.GetProjectPermissions(projectId));
var userIds = new HashSet(projectPermissions.Select(x => x.GrantedUserId));
return AccessClient.Instance.UsersAndGroups.Where(x => userIds.Contains(x.Id));
}
private static void SetGrantedProjectPermissions(Guid projectId, IEnumerable userIds, bool reassign, bool cascading, bool reassignCascading) {
if (projectId == null || userIds == null) return;
HiveServiceLocator.Instance.CallHiveService(s => {
s.SaveProjectPermissions(projectId, userIds.ToList(), reassign, cascading, reassignCascading);
});
}
private UserGroupBase BuildPermissionsList(IEnumerable usersAndGroups) {
addedPermissions = newAssignedPermissions.Except(assignedPermissions);
removedPermissions = assignedPermissions.Except(newAssignedPermissions);
addedIncludes = newInheritedPermissions.Except(inheritedPermissions);
removedIncludes = inheritedPermissions.Except(newInheritedPermissions);
treeView.Nodes.Clear();
if (!usersAndGroups.Any()) return null;
treeView.BeforeCheck -= treeView_BeforeCheck;
treeView.AfterCheck -= treeView_AfterCheck;
var userGroups = new HashSet(usersAndGroups.OfType());
var users = new HashSet(usersAndGroups.OfType());
UserGroupBase first = null;
var groupsNode = new TreeNode("Groups") { ForeColor = SystemColors.GrayText };
groupsNode.ImageIndex = groupsNode.SelectedImageIndex = userGroupImageIndex;
foreach (var group in userGroups.OrderBy(x => x.Name)) {
var node = new TreeNode(group.Name) { Tag = group };
node.ImageIndex = userGroupImageIndex;
node.SelectedImageIndex = node.ImageIndex;
BuildNode(group, node);
groupsNode.Nodes.Add(node);
if (first == null) first = group;
}
var usersNode = new TreeNode("Users") { ForeColor = SystemColors.GrayText };
usersNode.ImageIndex = usersNode.SelectedImageIndex = userImageIndex;
foreach (var user in users.OrderBy(x => x.ToString())) {
var node = new TreeNode(user.ToString()) { Tag = user };
node.ImageIndex = userImageIndex;
node.SelectedImageIndex = node.ImageIndex;
BuildNode(user, node);
usersNode.Nodes.Add(node);
if (first == null) first = user;
}
treeView.Nodes.Add(groupsNode);
treeView.Nodes.Add(usersNode);
treeView.BeforeCheck += treeView_BeforeCheck;
treeView.AfterCheck += treeView_AfterCheck;
treeView.ExpandAll();
return first;
}
private void BuildNode(UserGroupBase ug, TreeNode node) {
if (newAssignedPermissions.Contains(ug)) {
node.Checked = true;
} else if (newInheritedPermissions.Contains(ug)) {
node.Checked = true;
node.ForeColor = SystemColors.GrayText;
}
if (inheritedPermissions.Contains(ug) && newInheritedPermissions.Contains(ug)) {
node.Text += " [included]";
} else if (addedIncludes.Contains(ug)) {
node.BackColor = addedIncludeColor;
node.ForeColor = SystemColors.GrayText;
node.Text += " [added include]";
} else if (removedIncludes.Contains(ug)) {
node.BackColor = removedIncludeColor;
node.ForeColor = SystemColors.GrayText;
node.Text += " [removed include]";
}
if (addedPermissions.Contains(ug)) {
node.BackColor = addedAssignmentColor;
node.ForeColor = SystemColors.ControlText;
node.Text += " [added assignment]";
} else if (removedPermissions.Contains(ug)) {
node.BackColor = removedAssignmentColor;
node.ForeColor = SystemColors.ControlText;
node.Text += " [removed assignment]";
}
}
#endregion
}
}