gpt4 book ai didi

design-patterns - 使用 ServiceStack 在 "per object per user"基础上控制权限的最佳设计模式?

转载 作者:行者123 更新时间:2023-12-03 12:46:33 25 4
gpt4 key购买 nike

我知道 ServiceStack 提供了一个 RequiredRole 属性来控制权限,但是,这并不完全适用于我的用例。我有一个包含大量用户生成内容的网站。用户只能编辑他们有明确权限的文档。权限是按对象或对象组控制的。因此,如果用户是某个组的管理员,则他们可以编辑该组管理的所有文档。

控制对 per object per user 上的请求的访问的最佳设计模式是什么?基础?我想用尽可能 DRY 的方法来解决这个问题,因为它会影响我所有 API 端点的 95%。

此外,这是否可以与 FluentValidation 集成并返回适当的 HTTP 响应?

非常感谢,

理查德.

最佳答案

我在我的 ServiceStack 应用程序中使用每个对象的权限。实际上,这是一个访问控制列表 (ACL)。

我创建了一个 Working Self Hosted Console Example你可以在 GitHub 上 fork。

ACL 模式:

我使用下图所示的数据库结构,其中我的数据库中的资源,如文档、文件、联系人等(我想保护的任何资源)都被赋予了 ObjectType ID。

Database

权限表包含适用于特定用户、特定组、特定对象和特定对象类型的规则,并且可以灵活地接受它们的组合,其中 null value 将被视为通配符。

保护服务和路由:

我发现处理它们的最简单方法是使用请求过滤器属性。使用我的解决方案,我只需在请求路由声明中添加几个属性:

[RequirePermission(ObjectType.Document)]
[Route("/Documents/{Id}", "GET")]
public class DocumentRequest : IReturn<string>
{
[ObjectId]
public int Id { get; set; }
}

[Authenticate]
public class DocumentService : Service
{
public string Get(DocumentRequest request)
{
// We have permission to access this document
}
}

我有一个过滤器属性调用 RequirePermission ,这将执行检查以查看当前用户请求 DTO DocumentRequest可以访问 Document ObjectId 的对象由属性 Id 给出.这就是在我的路线上连接检查的全部内容,所以它非常干燥。
RequirePermission请求过滤器属性:

在到达服务的操作方法之前,权限测试工作在过滤器属性中完成。它具有最低优先级,这意味着它将在验证过滤器之前运行。

此方法将获取事件 session ,即自定义 session 类型(详情如下),它提供事件用户的 Id 和他们被允许访问的组 Id。它还将确定请求中的 objectId(如果有)。

它通过检查请求 DTO 的属性以找到具有 [ObjectId] 的值来确定对象 ID。属性。

有了这些信息,它将查询权限源以找到最合适的权限。

public class RequirePermissionAttribute : Attribute, IHasRequestFilter
{
readonly int objectType;

public RequirePermissionAttribute(int objectType)
{
// Set the object type
this.objectType = objectType;
}

IHasRequestFilter IHasRequestFilter.Copy()
{
return this;
}

public void RequestFilter(IRequest req, IResponse res, object requestDto)
{
// Get the active user's session
var session = req.GetSession() as MyServiceUserSession;
if(session == null || session.UserAuthId == 0)
throw HttpError.Unauthorized("You do not have a valid session");

// Determine the Id of the requested object, if applicable
int? objectId = null;
var property = requestDto.GetType().GetPublicProperties().FirstOrDefault(p=>Attribute.IsDefined(p, typeof(ObjectIdAttribute)));
if(property != null)
objectId = property.GetValue(requestDto,null) as int?;

// You will want to use your database here instead to the Mock database I'm using
// So resolve it from the container
// var db = HostContext.TryResolve<IDbConnectionFactory>().OpenDbConnection());
// You will need to write the equivalent 'hasPermission' query with your provider

// Get the most appropriate permission
// The orderby clause ensures that priority is given to object specific permissions first, belonging to the user, then to groups having the permission
// descending selects int value over null
var hasPermission = session.IsAdministrator ||
(from p in Db.Permissions
where p.ObjectType == objectType && ((p.ObjectId == objectId || p.ObjectId == null) && (p.UserId == session.UserAuthId || p.UserId == null) && (session.Groups.Contains(p.GroupId) || p.GroupId == null))
orderby p.ObjectId descending, p.UserId descending, p.Permitted, p.GroupId descending
select p.Permitted).FirstOrDefault();

if(!hasPermission)
throw new HttpError(System.Net.HttpStatusCode.Forbidden, "Forbidden", "You do not have permission to access the requested object");
}

public int Priority { get { return int.MinValue; } }
}

权限优先级:

当从权限表中读取权限时,使用优先级最高的权限来判断是否有访问权限。权限条目越具体,排序结果时的优先级就越高。
  • 与当前用户匹配的权限比所有用户的一般权限具有更高的优先级,即 UserId == null .类似地,特定请求对象的权限比该对象类型的一般权限具有更高的优先级。
  • 用户特定权限优先于组权限。这意味着,可以通过组权限授予用户访问权限,但在用户级别拒绝访问,反之亦然。
  • 如果用户属于允许他们访问资源的组和拒绝他们访问的另一个组,则该用户将具有访问权限。
  • 默认规则是拒绝访问。

  • 执行:

    在我上面的示例代码中,我使用了这个 linq 查询来确定用户是否有权限。该示例使用模拟数据库,您需要将其替换为您自己的提供程序。

    session.IsAdministrator || 
    (from p in Db.Permissions
    where p.ObjectType == objectType &&
    ((p.ObjectId == objectId || p.ObjectId == null) &&
    (p.UserId == session.UserAuthId || p.UserId == null) &&
    (session.Groups.Contains(p.GroupId) || p.GroupId == null))
    orderby p.ObjectId descending, p.UserId descending, p.Permitted, p.GroupId descending
    select p.Permitted).FirstOrDefault();

    自定义 session :

    我使用了一个自定义 session 对象来存储组成员身份,当用户通过身份验证时,这些对象会被查找并添加到 session 中。

    // Custom session handles adding group membership information to our session
    public class MyServiceUserSession : AuthUserSession
    {
    public int?[] Groups { get; set; }
    public bool IsAdministrator { get; set; }

    // The int value of our UserId is converted to a string!?! :( by ServiceStack, we want an int
    public new int UserAuthId {
    get { return base.UserAuthId == null ? 0 : int.Parse(base.UserAuthId); }
    set { base.UserAuthId = value.ToString(); }
    }


    // Helper method to convert the int[] to int?[]
    // Groups needs to allow for null in Contains method check in permissions
    // Never set a member of Groups to null
    static T?[] ConvertArray<T>(T[] array) where T : struct
    {
    T?[] nullableArray = new T?[array.Length];
    for(int i = 0; i < array.Length; i++)
    nullableArray[i] = array[i];
    return nullableArray;
    }

    public override void OnAuthenticated(IServiceBase authService, ServiceStack.Auth.IAuthSession session, ServiceStack.Auth.IAuthTokens tokens, System.Collections.Generic.Dictionary<string, string> authInfo)
    {
    // Determine UserId from the Username that is in the session
    var userId = Db.Users.Where(u => u.Username == session.UserName).Select(u => u.Id).First();

    // Determine the Group Memberships of the User using the UserId
    var groups = Db.GroupMembers.Where(g => g.UserId == userId).Select(g => g.GroupId).ToArray();

    IsAdministrator = groups.Contains(1); // Set IsAdministrator (where 1 is the Id of the Administrator Group)

    Groups = ConvertArray<int>(groups);
    base.OnAuthenticated(authService, this, tokens, authInfo);
    }
    }

    我希望你觉得这个例子很有用。如果有任何不清楚的地方,请告诉我。

    流畅验证:

    Also, can this be integrated with FluentValidation and return appropriate HTTP responses?



    您不应该尝试在验证处理程序中执行此操作,因为 这不是验证 .检查您是否有权限是一个验证过程。如果您需要根据数据源中的特定值检查某些内容,则不再执行验证。 See this other answer of mine这也涵盖了这一点。

    关于design-patterns - 使用 ServiceStack 在 "per object per user"基础上控制权限的最佳设计模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22375880/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com