Php ACL排序和评估。什么应该优先考虑客户或集团、aco或父aco
我将根据本文创建自己的acl实现 我已经理解了ACO-ARO和ACO_-ARO的概念。我想实现检查方法,它将决定aro是否有权访问aco。由于存在ARO树和ACO树,如何计算ACO对ARO的最有效权限 我还发现了下面的文章,其中实现了check方法,但这是在php中实现的 简言之,什么应该优先考虑帐户或组、aco或父aco 像这样的 更新 我现在一直到这里 我创建了一个accessControlEntry类,如下所示Php ACL排序和评估。什么应该优先考虑客户或集团、aco或父aco,php,.net,tree,acl,Php,.net,Tree,Acl,我将根据本文创建自己的acl实现 我已经理解了ACO-ARO和ACO_-ARO的概念。我想实现检查方法,它将决定aro是否有权访问aco。由于存在ARO树和ACO树,如何计算ACO对ARO的最有效权限 我还发现了下面的文章,其中实现了check方法,但这是在php中实现的 简言之,什么应该优先考虑帐户或组、aco或父aco 像这样的 更新 我现在一直到这里 我创建了一个accessControlEntry类,如下所示 public class AccessControlEntry {
public class AccessControlEntry
{
public BsonObjectId AccessControlEntryId { get; set; }
public BsonObjectId AccessRequestObjectId { get; set; }
public BsonObjectId AccessControlObjectId { get; set; }
public bool CanView { get; set; }
public bool CanEdit { get; set; }
public bool CanDelete { get; set; }
public bool CanAdministrate { get; set; }
}
public bool Check(Usercontext usercontext, BsonObjectId acoId, string permission)
{
//aco id is accessControlObjectId like in cakephp acl
Account acc = _usercontextService.GetAccountByUserContext(usercontext);
//getting ACE eg X account has CanRead=true on Y object
AccessControlEntry entry = _accessControlEntryRepository.GetAccessControlEntry(acc.AccountId, acoId);
if (entry != null)
{
bool value = (bool)entry.GetType().GetProperty(permission).GetValue(entry, null);
return value;
}
//account entry not found ...search in groups
bool groupEntryFound = false;
bool effectiveValue = false;
Group[] groups = _usercontextService.GetGroupsForAccount(acc.AccountId);
foreach (Group group in groups)
{
AccessControlEntry entryGroup = _accessControlEntryRepository.GetAccessControlEntry(group.GroupId, acoId);
if (entryGroup != null)
{
groupEntryFound = true;
effectiveValue |= (bool)entryGroup.GetType().GetProperty(permission).GetValue(entryGroup, null);
}
}
//ACE found in group ..return most privilged value
if (groupEntryFound)
return effectiveValue;
//entry not found for account nor for group..return false
return false;
}
我从其他服务调用check方法,比如
Check(context,44556,"CanRead")
check方法查找帐户的AccessControlEntry,如果找不到帐户的任何条目,则查找组。在实现内容管理系统/文档管理系统模型时,我在.Net中遇到了相同的问题 我发现,在最简单的形式中,实际上有两棵树,根据继承的权限进行动态“有效权限”计算对应用程序的可伸缩性来说是不健康的,但这并不意味着这是不可能的 这意味着您基本上可以基于当前节点计算有效权限,但这只是为了简化模型 例如:(让我们使用“页面”作为节点) 在复杂模型中,要计算用户在第4页上的权限,您需要有效地获取分配给第1、3和4页的所有权限,然后执行“添加合并”
在简化模型中,我们只考虑用户为第4页< /P >添加的权限。
Page 1
Page 2
Page 3
Page 4
为了使我的问题尽可能简单,从而尽可能避免bug,我决定采用一种模型,在该模型中,只有角色/组可以使用相关的acl条目添加到树节点
这意味着要找出我想要的权限,我将有效地运行一个查询,如(seudo代码,只有您的实现可能会有所不同):
这就剩下了1个需要考虑的问题,即拒绝规则,如果该规则是拒绝规则,典型的惯例是它会覆盖一个允许规则
因此,再次返回循环并评估拒绝:
allAcls.Each(acl => {
resultAcl.Delete = (acl.Delete == deny ? resultAcl.Delete == deny);
resultAcl.Update = (acl.Update == deny ? resultAcl.Update == deny);
resultAcl.Read = (acl.Read == deny ? resultAcl.Read == deny);
....
});
因此,您基本上是说获取用户和页面的所有角色,其中页面具有任何这些角色的acl条目。将其添加到结果权限中,然后删除定义了显式拒绝的任何权限
我相信,如果您希望重新运行该过程,以获得用户特定的权限,并匹配当前页面中应用于当前用户的所有权限,并覆盖基于角色的设置,则可以进一步扩展该过程
作为一般的经验法则,我倾向于遵循这样一种逻辑,即规则越具体,它就越相关
所以你可能会说像。。。
所有经理都可以访问整个网站
经理被剥夺了对网站管理部分的任何权利
所有销售人员都可以访问“销售”部分
所有营销都可以访问营销
营销用户“Bob”可以访问sales
上述逻辑将涵盖所有这些,并有效地应用如下访问:
用户有权访问其部门的部门(销售用户=销售等)
经理可以获得额外的权限和访问所有区域的权限,并接受管理(仅此而已?)
鲍勃是我们的特例,虽然他从事市场营销,但我们授予他销售权
这意味着什么:
1.用户可以添加到角色中
2.可以将页面添加到角色中,然后向该角色授予“acl”权限
这意味着我可以说:
如果用户担任销售角色,请授予他们“读取、更新”
3.根据定义,页面本身并不是“在角色中”,而只是知道授予角色多少访问权限
4.扩展此模型意味着您可以为特定用户指定acl条目
5.用户acl条目覆盖角色acl条目
6.到目前为止,所考虑的Acl条目适用于整个树
7.拒绝规则覆盖允许规则
那么,如果父页面显示“销售用户被完全拒绝访问”,而我们所在的页面显示“当前用户bob拥有完全访问权限”,会发生什么情况呢
这取决于开发人员/业务部门对如何处理此类场景的选择
我的想法是:
用户的作用域比角色更为局部
拒绝规则适用于父级,而允许规则适用于页面
我会接受允许规则
但是,如果父规则是针对用户的,而角色的页面规则是针对当前页面的,那么我将使用相同的逻辑来获取角色的规则
很多ACL内容都是主观的,我倾向于依赖于人们习惯的东西,例如:windows中的文件系统权限,这样应用程序看起来就像用户认为的“正常”一样,这将使你在未来不会收到20个问题
基本上都是这样。看看这个url,这是cakephp 2.0:-
http://book.cakephp.org/2.0/en/tutorials-and-examples/simple-acl-controlled-application/simple-acl-controlled-application.html
http://book.cakephp.org/2.0/en/tutorials-and-examples/simple-acl-controlled-application/part-two.html
我从中学到了很多。现在有点老了,但概念在今天仍然完全有效
他们帮助我了解了从基础到更复杂问题的所有内容。鉴于上述信息,我们可以编写一些简单的工具,在我的情况下,我正在使用内容管理系统,因此我的ACL是关于页面访问的 首先,我定义ACL条目的外观
using System;
namespace Core.Objects.CMS
{
/// <summary>
/// Represents a record on an access control list
/// </summary>
public class PageACLEntry
{
/// <summary>
/// Gets or sets the access control entry id.
/// </summary>
/// <value>
/// The access control entry id.
/// </value>
public int PageACLEntryId { get; set; }
/// <summary>
/// Gets or sets the page id.
/// </summary>
/// <value>
/// The page id.
/// </value>
public int PageId { get; set; }
/// <summary>
/// Gets or sets the name of the role.
/// </summary>
/// <value>
/// The name of the role.
/// </value>
public string RoleName { get; set; }
/// <summary>
/// Gets or sets the read.
/// </summary>
/// <value>
/// The read.
/// </value>
public bool? Read { get; set; }
/// <summary>
/// Gets or sets the content of the update.
/// </summary>
/// <value>
/// The content of the update.
/// </value>
public bool? UpdateContent { get; set; }
/// <summary>
/// Gets or sets the update meta.
/// </summary>
/// <value>
/// The update meta.
/// </value>
public bool? UpdateMeta { get; set; }
/// <summary>
/// Gets or sets the delete.
/// </summary>
/// <value>
/// The delete.
/// </value>
public bool? Delete { get; set; }
/// <summary>
/// Gets or sets the full control.
/// </summary>
/// <value>
/// The full control.
/// </value>
public bool? FullControl { get; set; }
}
}
似乎有很多代码,但如果你去掉格式和注释(以满足编码标准),实际上只有很少的代码,如果你将其复制到VisualStudio中的一个类文件中,它实际上很容易理解和维护
您可能还注意到,我没有一次传入存储库或服务类来获取数据,这是因为我使用它来构建使用代码优先EF建模的对象,而我使用的是延迟加载,所以您的实现可能需要的不仅仅是页面。
using System;
namespace Core.Objects.CMS
{
/// <summary>
/// Represents a record on an access control list
/// </summary>
public class PageACLEntry
{
/// <summary>
/// Gets or sets the access control entry id.
/// </summary>
/// <value>
/// The access control entry id.
/// </value>
public int PageACLEntryId { get; set; }
/// <summary>
/// Gets or sets the page id.
/// </summary>
/// <value>
/// The page id.
/// </value>
public int PageId { get; set; }
/// <summary>
/// Gets or sets the name of the role.
/// </summary>
/// <value>
/// The name of the role.
/// </value>
public string RoleName { get; set; }
/// <summary>
/// Gets or sets the read.
/// </summary>
/// <value>
/// The read.
/// </value>
public bool? Read { get; set; }
/// <summary>
/// Gets or sets the content of the update.
/// </summary>
/// <value>
/// The content of the update.
/// </value>
public bool? UpdateContent { get; set; }
/// <summary>
/// Gets or sets the update meta.
/// </summary>
/// <value>
/// The update meta.
/// </value>
public bool? UpdateMeta { get; set; }
/// <summary>
/// Gets or sets the delete.
/// </summary>
/// <value>
/// The delete.
/// </value>
public bool? Delete { get; set; }
/// <summary>
/// Gets or sets the full control.
/// </summary>
/// <value>
/// The full control.
/// </value>
public bool? FullControl { get; set; }
}
}
using System.Collections.Generic;
using System.Security.Principal;
using Core.Objects.CMS;
namespace Core.Utilities
{
/// <summary>
/// Tools for permission calculation
/// </summary>
public static class PermissionHelper
{
/// <summary>
/// Calculates the page permissions the given user has on the given page.
/// </summary>
/// <param name="page">The page.</param>
/// <param name="user">The user.</param>
/// <returns>the effective permissions</returns>
private static PageACLEntry CalculatePagePermissions(Page page, IPrincipal user)
{
PageACLEntry result = new PageACLEntry();
// start with acl for the current page
List<PageACLEntry> acl = new List<PageACLEntry>(page.AclRules);
// append all the way up the tree until parent == null
acl = AppendTreePermissions(acl, page, user);
// reverse the list so we evaluate root first then work up to here
acl.Reverse();
// because of the order in which these are applied the most local rules overrule the less local
// the wider the scope the less it applies
acl.ForEach(ace =>
{
// only apply rules that apply to roles that our current user is in
if (user.IsInRole(ace.RoleName))
{
result.Read = Eval(result.Read, ace.Read);
result.Delete = Eval(result.Delete, ace.Delete);
result.UpdateMeta = Eval(result.UpdateMeta, ace.UpdateMeta);
result.UpdateContent = Eval(result.UpdateContent, ace.UpdateContent);
result.FullControl = Eval(result.FullControl, ace.FullControl);
}
});
return result;
}
/// <summary>
/// Evaluates the specified permission level.
/// </summary>
/// <param name="target">The target.</param>
/// <param name="suggestion">The suggestion.</param>
/// <returns>evaluation result</returns>
private static bool? Eval(bool? target, bool? suggestion)
{
bool? result = null;
switch (target)
{
case false:
result = false;
break;
case true:
result = true;
break;
case null:
break;
}
return result;
}
/// <summary>
/// Appends the tree acl from the tree root up to this point.
/// </summary>
/// <param name="acl">The acl.</param>
/// <param name="page">The page.</param>
/// <param name="user">The user.</param>
/// <returns>the complete acl</returns>
private static List<PageACLEntry> AppendTreePermissions(List<PageACLEntry> acl, Page page, IPrincipal user)
{
Page currentPage = page.Parent;
while (currentPage != null)
{
acl.AddRange(currentPage.AclRules);
currentPage = page.Parent;
}
return acl;
}
/// <summary>
/// Determines if the current User can read the given page.
/// Unless an explicit deny rule is in place the default is to make everything read only.
/// </summary>
/// <param name="page">The page.</param>
/// <param name="user">The user.</param>
/// <returns>
/// access right indication as bool
/// </returns>
public static bool UserCanRead(Page page, IPrincipal user)
{
PageACLEntry permissions = CalculatePagePermissions(page, user);
if (permissions.Read != false)
{
return true;
}
return false;
}
/// <summary>
/// Determines if the current User can delete the given page.
/// </summary>
/// <param name="page">The page.</param>
/// <param name="user">The user.</param>
/// <returns>
/// access right indication as bool
/// </returns>
public static bool UserCanDelete(Page page, IPrincipal user)
{
PageACLEntry permissions = CalculatePagePermissions(page, user);
if (permissions.FullControl == true || permissions.Delete == true)
{
return true;
}
return false;
}
/// <summary>
/// Determines if the current User can update the given page.
/// </summary>
/// <param name="page">The page.</param>
/// <param name="user">The user.</param>
/// <returns>
/// access right indication as bool
/// </returns>
public static bool UserCanUpdate(Page page, IPrincipal user)
{
PageACLEntry permissions = CalculatePagePermissions(page, user);
if (permissions.FullControl == true || permissions.UpdateMeta == true)
{
return true;
}
return false;
}
public static bool UserCanUpdateContent(Page page, IPrincipal user)
{
PageACLEntry permissions = CalculatePagePermissions(page, user);
if (permissions.FullControl == true || permissions.UpdateContent == true)
{
return true;
}
return false;
}
/// <summary>
/// Determines if the current User can append children to the given page.
/// </summary>
/// <param name="page">The page.</param>
/// <param name="user">The user.</param>
/// <returns>
/// access right indication as bool
/// </returns>
public static bool UserCanAddChildTo(Page page, IPrincipal user)
{
PageACLEntry permissions = CalculatePagePermissions(page, user);
if (permissions.FullControl == true || permissions.UpdateMeta == true)
{
return true;
}
return false;
}
}
}
bool result = PermissionsHelper.UserCanRead(page, user);
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Core.Objects.CMS
{
/// <summary>
/// Represents a managed CMS page
/// </summary>
[Table("Pages")]
public class Page
{
/// <summary>
/// Gets or sets the page id.
/// </summary>
/// <value>
/// The page id.
/// </value>
public int PageId { get; set; }
/// <summary>
/// Gets or sets the version.
/// </summary>
/// <value>
/// The version.
/// </value>
public int Version { get; set; }
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>
/// The title.
/// </value>
public string Title { get; set; }
/// <summary>
/// Gets or sets the template.
/// </summary>
/// <value>
/// The template.
/// </value>
public string Template { get; set; }
/// <summary>
/// Gets or sets the path.
/// </summary>
/// <value>
/// The path.
/// </value>
public string Path { get; set; }
/// <summary>
/// Gets or sets the parent.
/// </summary>
/// <value>
/// The parent.
/// </value>
public virtual Page Parent { get; set; }
/// <summary>
/// Gets or sets the children.
/// </summary>
/// <value>
/// The children.
/// </value>
public virtual List<Page> Children { get; set; }
/// <summary>
/// Gets or sets the content.
/// </summary>
/// <value>
/// The content.
/// </value>
public virtual List<PageContent> Content { get; set; }
/// <summary>
/// Gets or sets the component stacks.
/// </summary>
/// <value>
/// The component stacks.
/// </value>
public virtual List<Stack> ComponentStacks { get; set; }
/// <summary>
/// Gets or sets the acl rules.
/// </summary>
/// <value>
/// The acl rules.
/// </value>
public virtual List<PageACLEntry> AclRules { get; set; }
}
}