8 ms·
Distributed Authorization
- simonw 3y agoThis is a product launch. Full title: "Authorization is still a nightmare for engineers: Launching Distributed Authorization"
- prophesi 3y agoAnd now it's just "Distributed Authorization"; further distancing itself from the product it's actually about.
- simonw 3y agoThere's an interesting section here about one of my favourite challenges in authorization: how to efficiently return a list of things that the current user has permission to access, without running a "can_access()" permission check on every single one of them (which is bad if you have thousands of items and you want to paginate them). Their solution is to let you configure rules that get turned into SQL fragments that you can run against your own database: https://www.osohq.com/docs/guides/integrate/filter-lists#list-filtering-with-decentralized-data https://www.osohq.com/docs/guides/integrate/filter-lists#lis... - example Rails app here: https://github.com/osohq/rails_list_filtering_sample_app https://github.com/osohq/rails_list_filtering_sample_app A team I worked with in the past came to the same conclusion - turning authorization rules into WHERE clauses is a very efficient way to solve this problem, if you can figure out a clean way to do it.
- cqqxo4zV46cp 3y agoThere’s a Django app called Bridgekeeper that goes a fair way toward doing this (of course, via the Django ORM). It’s got some pretty major design flaws of its own, and unfortunately hasn’t gotten much love in quite some time. However I still find the concept / intent to be quite telling. It certainly feels like it’s in the right ballpark.
- canadiantim 3y agohttps://github.com/daisylb/bridgekeeper https://github.com/daisylb/bridgekeeper
- moribvndvs 3y agoI’m not entirely happy with the solution, but one big advantage of a JS authorization library called CASL is the same ability to turn rules into queries. I also like that I can create field-level permissions and even feed it a target object and get a reduced representation of that object which will include only fields the user is authorized to modify or read. Unfortunately, there are a few issues not worth getting into here other than to say I feel that the industry has a very frustratingly uneven set of solutions for authorization, no doubt in part due to it being complicated.
- samjs 3y ago(Oso CTO here). Out of curiosity what do you not like about CASL? It always seemed to have a similar goal in mind which I loved, but I suspect it hit similar challenges we had when replying on ORM integrations.
- moribvndvs 3y agoOne big annoyance is including attributes beyond the target (which CASL calls the subject). There may be a plethora of environmental factors I want to evaluate in my rule. The two obvious options are: 1) build the rules programmatically, based on what you observe in the request context. This works fine until you want users to be able to create and assign custom policies and load them from a database. 2) put placeholders in the rule’s conditions, and swap them with the current contextual values when rehydrating the rules for a given request. Fine, except this obviates caching except for the raw rule from a database, and rehydrating dozens or hundreds of rules for every request starts to add up in terms of overhead. I wish rule conditions could reference a “context” parameter (name not important) so I could create a condition like {userId: context.user.id} and at runtime I could pass the current context when I call can. That way I can rehydrate the rule once. I realize this creates all sorts of complications with serializing a rule to be stored in a database or sent over the wire, but that’s where some special placeholders could be understood natively by an Ability and hooked into a passed in context. However, that still creates an issue if I want to create a condition that is purely based on context ( e.g. {context.user.isTrialUser: false} ) The other thing I have been struggling with is that can with a subject name only (rather than an instance) will match a rule with no conditions along with rules that have matching conditions. I understand the author’s rationalization, but can potentially create unexpected results particularly when you offer a system that lets users build their own policies. The last issue is CASL is nodejs only and I may need to support multiple platforms. I’ve looked at Casbin because of its multiplatform support and customizable model, but I’ve found it extraordinarily hard to use beyond simple RBAC or claims-based authorization, and it still doesn’t offer solutions like conditions-to-query filters or field-level authorizations.
- wkirby 3y agoSame conclusion we came to, and the basis of our in-house permission gem for RoR. The most efficient declaration of permissions is to express them as a WHERE statement, and then the implementation of can_whatever() is just inclusion in the collection returned by the WHERE. Permissions have three moving parts, who wants to do it, what do they want to do, and on what object. Any good permission system has to be able to efficiently answer any permutation of those variables. Given this person and this object, what can they do? Given this object and this action, who can do it? Given this person and this action, which objects can they act upon? We’ve found most permissioning systems end up with a pick-2 approach, and the most common one to be abused is given a person and an action, give me the collection. This leads to implementing permissions twice, once in code, and once as a query.
- gsarjeant 3y agoHi, wkirby! I'm the post author, I do DevRel at Oso. > Permissions have three moving parts, who wants to do it, what do they want to do, and on what object. Any good permission system has to be able to efficiently answer any permutation of those variables. Given this person and this object, what can they do? Given this object and this action, who can do it? Given this person and this action, which objects can they act upon? > We’ve found most permissioning systems end up with a pick-2 approach, and the most common one to be abused is given a person and an action, give me the collection. This leads to implementing permissions twice, once in code, and once as a query. I love the way you put this! I'm always looking for good ways to talk about authorization without falling back on jargon and I've never come up with a way to talk about the difference between authorizing an action on a single resource and returning a list of authorized resources that I've been happy with. Would you mind if I adapted this in future writing?
- wkirby 2y agoBy all means! I enjoyed your article here, and I will keep an eye on Oso on the future. Authorization has become a hobby horse of mine, and I always appreciate people who are thinking about the complexity required to meet real-world needs.
- samjs 3y agoHey simon! Oso CTO here. Definitely one of my favourite problems too! Some additional context for those who don't think about this all the time: in many cases, the solution is as simple as "write some SQL where clauses to do the filtering I care about". e.g. I suspect the vast majority of people have logic like `where tenant_id = ?` or similar and they pass in a tenant ID on every query. Where things get challenging is when you want to decouple the logic (e.g. have an abstraction in your code, or centralize logic in a service). Because then you're in the world of what's the decoupled API that allows me to filter my database. The easiest way to do that is just generate return a big list of IDs the user can see, and put `id in (... list of ids)` on the query. But that involves (a) syncing the data to the central service and (b) that list can get pretty long. And so that's why you would even need to think about turning rules into WHERE clauses in the first place :)
- topspin 3y ago> efficiently return a list of things that the current user has permission to access I've done this with out-of-the-box Keycloak Authorization Services. There is an entire standards based framework for authorization piggy-backed on OAUTH called UMA2. Keycloak provides an implementation of this. It's young and the documentation is a bit thin, and the learning curve is cliff shaped, but it does what is says on the tin. My case involved authorizing verbs on a set of resources. The backend generates a permission ticket with an arbitrary list of resources and scopes and obtains (from Keycloak) an otherwise ordinary OAUTH access token containing a UMA2 RPT (Requesting Party Token) claim. That is cycled through the "token introspection" endpoint of Keycloak which returns a clean, simple JSON response with the subset of resources and scopes that are authorized. Net result is two requests for any arbitrary subset of resources and scopes. Nothing is stored or managed by the backend system: all the authorization stuff is in Keycloak. You can submit an open ended request that just dumps everything the user is authorized for. It's simple enough that, foregoing signature checks that are otherwise performed, I prototype and test this stuff using shell scripts, curl and jq. Since it's all piggy-backed on the existing OAUTH system there is no additional infrastructure.
- slt2021 2y agojust wanted to make sure I understand correctly, upon authentication you just bake everything a user has access to (all policies) into a claims part of the JWT? how it would look like for example if a user has access to 10000 objects: are all of them baked into a token as claims?
- cryptonector 2y agoAuthorization can be relational, so JOIN to authz tables and and add WHERE conjunctions and it's enough -- if you have a database application anyways.
- AndrewHampton 2y ago> A team I worked with in the past came to the same conclusion - turning authorization rules into WHERE clauses is a very efficient way to solve this problem, if you can figure out a clean way to do it. For rails specifically, https://github.com/polleverywhere/moat https://github.com/polleverywhere/moat was built with this in mind. It's heavily inspired by Pundit, but let's you write policies at the `ActiveRecord::Relation` level. So `policy_filter(Article).find_by!(id: params[:id])` would run something like `select * from articles where id = ? and id in (select id from articles where owner_id = ?);`.
- mackross 2y agoNice library
- pluto_modadic 2y agoTailscale's ACLs (attach users to groups, attach objects to groups, cross link the two in policies) does a bit of indirection to reduce that volume. e.g.: many objects <-> object tag <-> policy <-> user group attachments <-> user
- slt2021 2y agoif all the data is local why even need cloud authz solution? you can run SQL locally using sqlite or something for authorization decisions and list filtering (give me everything I have access to)
- valenterry 3y ago> Suppose you decide to add teams to gitcloud, so different teams in an organization can have different permissions. Will you be able to find all the functions and queries you need to modify in order to make teams work properly? Do you want to have to do that? > This is why we introduced Polar, our declarative DSL for authorization. Polar allows you to separate your authorization logic from your application logic and express it in a language that is purpose built for authorization. A simple Polar policy looks something like this: First, I appreciate that it is the Polar "programming language" and not yet another kind of stupid YAML DSL. However: you seem to target developers. Why do you force me to leave my IDE and use your "rules editor"? Can I not write all those things in my IDE, with all the support it brings, and integrate this into my CICD flow? (yes, there is the .polar file, but why force me to jump through hoops?) Then, why did you create a new DSL and not a merely a (de-)serializable datastructure (which will indeed look like a dsl)? One, that is powerful enough to represent the capabilities you need. Then, I could in fact use any language (library) of my choice and create the rules from this language, which just has to create the datastructure. Or, backwards: why do you think authorization is so special that it deserves a custom language? Is it more special than performance-testing, logging, auditing, debugging, metrics and so on? Apart from that, I really like the `yes, if` idea! Would be nice to hear a bit more about that (unfortunately, the article pretty much ends there). Such as: how to deal with actions that change things and can (or must) potentially be run before the authorization is completed and such.
- tux1968 3y agoAny such IDE needs a way to communicate with the services that are going to enforce the permissions. The DSL is a good way to do this. It allows inspection, diffing, and access by other tools, instead of being locked away in some hidden IDE binary structures. Edit: But no IDEs will yet have any graphical tool to assist in rule development and checking. So the fact that one is provided, is better than it not existing at all. But you can always edit the DSL manually, if need be. They explicitly state that their rules-editor can sometimes be limited: "The Rules Workbench, a visual rules editor that you can use to model most of these patterns"
- 3y ago
- doh 3y agoQuick note for the osohq team: The "Read the docs" button leads to 404
- gneray 3y agoDoh, fixing now. Thank you!
- withinboredom 3y agoI tend to build the "ownership" model whenever I can. It works extremely well and has a few simple rules: 1. a user can own an entity/row/unit/whatever. They have full control over this unit. 2. a user can share ownership with another user/role. 3. a user can share various "rights" over any units they own -- CRUD, for example -- for any user/role. 4. a user can only interact with any unit they have a right to. This can be implemented through a simple db table (or inline in the data itself) and doesn't depend on much. Once you build the middleware, you don't even need to think about the authorization layer.
- sroussey 3y agoI would love to see a reference implementation. I know some people inline these rights into data inside a crdt. I really would love to see a reference implementation of that!
- withinboredom 3y agoHere you go, <120 loc: https://github.com/bottledcode/durable-php/blob/v2/cli/auth/user.go https://github.com/bottledcode/durable-php/blob/v2/cli/auth/...
- samjs 3y agoThat's a really clean implementation. And the shares are used to resolve authorization here [1], right? Two things that we're solving for at Oso is: making it easier for multiple teams to collaborate on permissions (which we solve by having a declarative language), and solving the list filtering problem (as talked about in the post). If you don't need either of those two things and are happy with a DIY approach, what you've shared would work great IMO. If you packaged that up as a standalone solution, I could see a lot of people getting value from it! There are not enough people sharing authz implementations out there, a blog post on this shares approach would be super cool. [1] https://github.com/bottledcode/durable-php/blob/3ad509fcdbb3feeebd4b2993b1fd959159ff2179/cli/auth/resource.go#L230-L270 https://github.com/bottledcode/durable-php/blob/3ad509fcdbb3...
- samjs 3y agoHey all! I'm Sam, cofounder + CTO at Oso. Thank you all for the great discussions going on here. If folks are interested in either learning more about the product or working on these kinds of problems, you can email me directly at sam [at] osohq.com.
- augunrik 3y agoKinda sounds like OPA (Open Policy Agent) [1], but a different implementation. [1] https://www.openpolicyagent.org/docs/latest/ https://www.openpolicyagent.org/docs/latest/
- ebolyen 2y agoSlightly tangential, but is there any hope of seeing Polar return as a (maintained) open source system? I absolutely love the concept of using a logic language for authorization, and I think Polar's aesthetic qualities make it significantly more approachable for most people (over Prolog/Datalog). But even without the authorization problem, Polar is just... really nice looking. It would be awesome to be able to use it as its own language outright.
- gneray 2y agoYes. This is happening. Stay tuned.
- ogazitt 2y agoCongrats on the launch! [Disclosure: I'm one of the co-founders of Aserto, the creators of Topaz]. The problem of data filtering is indeed a huge part of building an effective authorization system. Partial evaluation is one way of doing it, although with systems like OPA [0] it requires a lot of heavy lifting (parsing the returned AST and converting it into a WHERE clause). Looking forward to seeing how turnkey that can be with Oso. With that said, there are applications where you really want the data close to the authorization engine. With a ReBAC model, you can easily find the objects that a user has access to, or the users that have access to an object, by walking the relationship graph. That's the approach we've taken with Topaz [1]. Funny timing - a few days ago we published a blog post on that very topic! [2] [0] https://openpolicyagent.org https://openpolicyagent.org [1] https://topaz.sh https://topaz.sh [2] https://www.aserto.com/blog/how-rebac-helps-solve-data-filtering https://www.aserto.com/blog/how-rebac-helps-solve-data-filte...
- renszarv 2y agoIs it self hosted? I can't see any docker image