EnVisageOnline/Main/Source/EnVisage/Code/Security/SecurityManager.cs

322 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using EnVisage.Code.Cache;
using EnVisage.Models.Cache;
using EnVisage.Code.BLL;
using System.Web.Mvc;
using Prevu.Core.Main;
namespace EnVisage.Code
{
/// <summary>
/// Manager for check security operations
/// </summary>
/// <remarks>Created by SA</remarks>
public class SecurityManager
{
public class DefaultPageWithArea
{
public string Action;
public string Controller;
public Areas Area;
public object Parameters;
public DefaultPageWithArea(string action, string controller)
{
this.Action = action;
this.Controller = controller;
}
public DefaultPageWithArea(string action, string controller, Areas area)
{
this.Action = action;
this.Controller = controller;
this.Area = area;
}
public DefaultPageWithArea(string action, string controller, Areas area, object parameters)
{
this.Action = action;
this.Controller = controller;
this.Area = area;
this.Parameters = parameters;
}
}
public static void tryLogin()
{
Controllers.AccountController a = new Controllers.AccountController(DependencyResolver.Current.GetService<ApplicationUserManager>(), DependencyResolver.Current.GetService<IUserManager>());
a.SSOLoginConfirmation();
}
/// <summary>
/// Returns current user Principal Id (GUID)
/// </summary>
public static Guid GetUserPrincipal()
{
string userIdAsText = HttpContext.Current.User.Identity.GetID();
Guid userId = new Guid(userIdAsText);
return userId;
}
/// <summary>
/// Returns true if passed Area is allowed to be accessed by passed Principal with desired Type of access
/// </summary>
public static bool CheckSecurityObjectPermission(Areas area, AccessLevel type)
{
try
{
List<Areas> areas = new List<Areas> {area};
return CheckAnySecurityObjectPermission(areas, type);
}
catch
{
}
return false;
}
/// <summary>
/// Returns true if any of passed Areas is allowed to be accessed by passed Principal with desired Type of access
/// </summary>
public static bool CheckAnySecurityObjectPermission(List<Areas> areas, AccessLevel type)
{
var principalId = HttpContext.Current.User.Identity.GetID();
return CheckAnySecurityObjectPermissionForUser(areas, type, principalId);
}
private static bool CheckAnySecurityObjectPermissionForUser(List<Areas> areas, AccessLevel type, string userId)
{
List<string> stringAreas = new List<string>();
if (areas != null && areas.Count > 0)
areas.ForEach(a => stringAreas.Add(a.ToString()));
if (userId == null || userId == Guid.Empty.ToString()) return false;
SecurityAreasCache securityAreaCache = new SecurityAreasCache();
List<UserAreaAccess> s = new List<UserAreaAccess>();
foreach (var area in stringAreas)
s.AddRange(securityAreaCache.Value.Where(x => x.PrincipalId == new Guid(userId) && x.SecurityObject == area));
if (s.Any())
{
if (type == AccessLevel.Write)
{
return s.Any(x => x.Write == 1);
}
if (type == AccessLevel.Read)
{
return s.Any(x => (x.Read == 1 || x.Write == 1));
}
}
//var roles = user.AspNetRoles.Select(x => new Guid(x.Id));
var roles = new UsersCache().Value.FirstOrDefault(x => x.Id == new Guid(userId)).Roles;
s = new List<UserAreaAccess>();
foreach (var role in roles)
{
foreach (var area in stringAreas)
s.AddRange(securityAreaCache.Value.Where(x => x.PrincipalId == role && x.SecurityObject == area));
}
if (s.Any())
{
if (type == AccessLevel.Write)
return s.Any(x => x.Write == 1);
if (type == AccessLevel.Read)
return s.Any(x => (x.Read == 1 || x.Write == 1));
}
return false;
}
public static bool CheckProjectPermission(Guid ProjectId, AccessLevel Type)
{
var PrincipalId = HttpContext.Current.User.Identity.GetID();
if (PrincipalId == null || ProjectId == null || PrincipalId == Guid.Empty.ToString()) return false;
var userId = new Guid(PrincipalId);
EnVisageEntities dbContext = new EnVisageEntities();
var accessGranted=CheckProjectPermission(userId, ProjectId, Type, dbContext);
//EnVisageEntities dbContext = new EnVisageEntities();
//bool accessGranted = dbContext.VW_ProjectAccessByUserExtended.Where(x =>
// x.UserId.Equals(userId) &&
// x.Id.Equals(ProjectId) &&
// (((Type == AccessLevel.Read) && ((x.Read.HasValue && (x.Read.Value == 1)) || (x.Write.HasValue && (x.Write.Value == 1)))) ||
// ((Type == AccessLevel.Write) && x.Write.HasValue && (x.Write == 1)))).Any();
//if (!accessGranted)
//{
// ProjectManager prMngr = new ProjectManager(dbContext);
// var projectsByAllocations = prMngr.GetAssignedProjectsByUserId(new Guid(PrincipalId));
// accessGranted = (projectsByAllocations != null) && (projectsByAllocations.Contains(ProjectId));
//}
return accessGranted;
}
public static bool CheckProjectPermission(Guid userId, Guid ProjectId, AccessLevel Type, EnVisageEntities dbContext)
{
bool accessGranted = dbContext.VW_ProjectAccessByUserExtended.Where(x =>
x.UserId.Equals(userId) &&
x.Id.Equals(ProjectId) &&
(((Type == AccessLevel.Read) && ((x.Read.HasValue && (x.Read.Value == 1)) || (x.Write.HasValue && (x.Write.Value == 1)))) ||
((Type == AccessLevel.Write) && x.Write.HasValue && (x.Write == 1)))).Any();
if (!accessGranted)
{
ProjectManager prMngr = new ProjectManager(dbContext);
var projectsByAllocations = prMngr.GetAssignedProjectsByUserId(userId);
accessGranted = (projectsByAllocations != null) && (projectsByAllocations.Contains(ProjectId));
}
return accessGranted;
}
public static bool CheckScenarioPermission(Guid ScenarioId, AccessLevel Type)
{
// Get parent project (part) id for this scenario
EnVisageEntities dbContext = new EnVisageEntities();
var scenMngr = new ScenarioManager(dbContext);
Scenario scenario = scenMngr.GetScenario(ScenarioId);
if ((scenario != null) && scenario.ParentId.HasValue)
{
Guid projectId = scenario.ParentId.Value;
return CheckProjectPermission(projectId, Type);
}
return false;
}
/// <summary>
/// Gets a dictionary of teams which user has access to. Key=<see cref="Team.Id"/>, Value=<see cref="AccessLevel"/>.
/// Method returns data from database view VW_User2Team. If it returns more than one record for tuple [UserId, TeamId] (should not)
/// then method returns Max of provided values.
/// </summary>
/// <param name="dbContext">Database context.</param>
/// <param name="userId">User.Id</param>
/// <param name="teamsFilter">A list of Team.Id values to filter by.</param>
/// <returns><see cref="Dictionary{Guid, AccessLevel}"/></returns>
public static Dictionary<Guid, AccessLevel> GetTeamPermissionsForUser(EnVisageEntities dbContext, string userId, List<Guid> teamsFilter)
{
var teamManager = new TeamManager(dbContext);
var teams = teamManager.GetTeamsByUserFiltered(userId, teamsFilter, null, null);
return teams.GroupBy(t => t.TeamId).ToDictionary(gr => gr.Key, el => el.Max(t => t.AccessLevel));
}
/// <summary>
/// Returns first available Default Page for user depending on his security settings. Returnable Item format: Action, Controller
/// </summary>
public static DefaultPageWithArea RedirectToDefaultPage(string userId)
{
if (SecurityManager.IsResourceOnlyAccess(userId))
{
var mngr = new Code.BLL.AccountManager(new EnVisageEntities());
var resourceId = mngr.GetCurrentResourceId(userId);
if (resourceId.HasValue)
{
return new DefaultPageWithArea("Details", "PeopleResource", Areas.PeopleResourceDetails, new { resourceId = resourceId.Value.ToString() });
}
}
else
{
foreach (DefaultPageWithArea page in GetDefaultPages())
{
if (CheckAnySecurityObjectPermissionForUser(new List<Areas>() { page.Area }, AccessLevel.Read, userId))
{
return new DefaultPageWithArea(page.Action, page.Controller);
}
}
}
return new DefaultPageWithArea("AccessDenied", "Home");// this should always end with some landing page accessible by all users to prevent possible infinite cycles in client code
}
/// <summary>
/// Indicates that current user has access only to Resource Details page.
/// This way system should redirect him to the related page and deny access to all other areas.
/// </summary>
/// <returns><b>True</b> - if user has access only to resource details, otherwise - <b>false</b>.</returns>
public static bool IsResourceOnlyAccess(string userId)
{
var resourceDetailsGroupPermissions = new string[] { Areas.PeopleResourceDetails.ToString(), Areas.RD_ResourceInformation.ToString(),
Areas.RD_ResourceNonProjectTime.ToString(), Areas.RD_ResourceNPTAllocationCategory.ToString(), Areas.RD_ResourceSkills.ToString(),
Areas.RD_ResourceTimeAllocation.ToString(), Areas.RD_ResourceTimeAllocationActual.ToString(), Areas.RD_ResourceTimeAllocationProjected.ToString()
};
SecurityAreasCache securityAreaCache = new SecurityAreasCache();
var roles = new UsersCache().Value.FirstOrDefault(x => x.Id == new Guid(userId)).Roles;
// get all areas user has access to
var userAccessAreas = securityAreaCache.Value.Where(t => t.PrincipalId == new Guid(userId));
var roleAccessAreas = securityAreaCache.Value.Where(t => roles.Contains(t.PrincipalId));
var permissions = new Dictionary<string, AccessLevel>();
foreach (var item in roleAccessAreas)
{
if (!permissions.ContainsKey(item.SecurityObject))
{
if (item.Write == 1)
permissions.Add(item.SecurityObject, AccessLevel.Write);
else if (item.Read == 1)
permissions.Add(item.SecurityObject, AccessLevel.Read);
} else
{
if (item.Write == 1)
permissions[item.SecurityObject] = AccessLevel.Write;
else if (item.Read == 1)
permissions[item.SecurityObject] = AccessLevel.Read;
}
}
foreach (var item in userAccessAreas)
{
if (!permissions.ContainsKey(item.SecurityObject))
{
if (item.Write == 1)
permissions.Add(item.SecurityObject, AccessLevel.Write);
else if (item.Read == 1)
permissions.Add(item.SecurityObject, AccessLevel.Read);
}
else
{
if (item.Write == 1)
permissions[item.SecurityObject] = AccessLevel.Write;
else if (item.Read == 1)
permissions[item.SecurityObject] = AccessLevel.Read;
else if (item.Read == 0 && item.Write == 0)
permissions.Remove(item.SecurityObject);
}
}
// get user permissions other than resource details
var additionalAreas = permissions.Keys.Except(resourceDetailsGroupPermissions);
return !additionalAreas.Any();
}
/// <summary>
/// Checks if specific default page is accessible by current user
/// </summary>
/// <param name="userId">An ID of the current User</param>
/// <param name="action">Name of the action method of the default page</param>
/// <param name="controller">Name of the controller of the default page</param>
public static bool IsDefaultPageAvailable(string userId, string action, string controller)
{
List<DefaultPageWithArea> allPages = GetDefaultPages();
if (!allPages.Exists(p => p.Action.Equals(action, StringComparison.InvariantCultureIgnoreCase) && p.Controller.Equals(controller, StringComparison.InvariantCultureIgnoreCase)))
throw new ArgumentException(string.Format("There is no default page registered for given action ({0}) and controller ({1}) - check the GetDefaultPages method", action, controller));
DefaultPageWithArea page = allPages.First(p => p.Action.Equals(action, StringComparison.InvariantCultureIgnoreCase) && p.Controller.Equals(controller, StringComparison.InvariantCultureIgnoreCase));
return CheckAnySecurityObjectPermissionForUser(new List<Areas>() { page.Area }, AccessLevel.Read, userId);
}
/// <summary>
/// Returns a list of all system default landing pages users should go to upon login depending on their permissions. Item format: Action, Controller, required Security Area permission (can be null - means everyone has access)
/// </summary>
/// <returns></returns>
private static List<DefaultPageWithArea> GetDefaultPages()
{
List<DefaultPageWithArea> pages = new List<DefaultPageWithArea>();
pages.Add(new DefaultPageWithArea("Index", "Home", Areas.Dashboard));
pages.Add(new DefaultPageWithArea("Index", "Project", Areas.Projects));
pages.Add(new DefaultPageWithArea("ImportActuals", "Project", Areas.ImportActuals));
pages.Add(new DefaultPageWithArea("Details", "PeopleResource", Areas.PeopleResourceDetails));
return pages;
}
}
}