This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # aleph (4)
- # beginners (34)
- # boot (37)
- # cljs-dev (5)
- # cljsrn (8)
- # clojure (31)
- # clojure-austin (3)
- # clojure-greece (4)
- # clojure-spec (11)
- # clojure-uk (1)
- # clojurebridge (2)
- # clojurescript (13)
- # cloverage (22)
- # cursive (9)
- # datascript (2)
- # defnpodcast (2)
- # devcards (12)
- # emacs (1)
- # hoplon (67)
- # off-topic (5)
- # om (3)
- # onyx (36)
- # planck (3)
- # quil (2)
- # testing (1)
- # untangled (4)
- # yada (168)
I'm wondering about security and how you would protect
:get access to a resource for certain users
you've authenticated a request (let's say with a cookie) but then you have to check whether that particular user has access to that particular resource
hi @ericnormand - I'm in GMT+0100 and was sleeping, been a hectic week with our #XT16 conf!
See here for docs: https://juxt.pro/yada/manual/index.html#authorization
yada, like many other web libraries/frameworks, separates authentication and authorization into separate steps. Each step is dealt with by a separate interceptor (yada's approach to modularity), and these are in
Unlike many other web libraries/frameworks, authentication only determines the veracity/credability of the claims made in the request - for instance, with Basic auth, is it the correct password? for JWT token auth, is the signature cryptographically valid?
Unlike Liberator (and others) the authentication step does not typically return an error like 401 if there is no password/token. It can however, invite the user-agent to provide authentication credentials in the WWW-Authenticate response header.
So the authentication step is only about authentication. Liberator's
authorized? decision point also did authentication, and it was rather confusing to explain to people what was going on, and that a 401 Not Authorized actually meant Not Authenticated.
yada is based on the substantially cleaned up and disambiguated HTTP RFCs starting with 7230 onwards, where authentication (checking the veracity of the claims) and authorization (determining whether the user can access the resource) and entirely separate.
In yada, it is the authorization step that produces 200, 401 and 403, depending on what's in the request. You can use yada's only built-in role-based authorizer, or plugin your own. Authorization is done via the
yada.authorization/validate multi-method which dispatches on the value of the authorization
Critically, authentication is done before a resources properties (meta-data) are loaded (perhaps involving a database trip). If you're only concerned about checking the veracity/truthfulness of a request, you don't need to know anything about the resource involved.
Then the resource's properties are loaded (which could involve loading certain attributes about the resource, enabling attribute-based access control ABAC schemes) - https://en.wikipedia.org/wiki/Attribute-Based_Access_Control -
Then the authorization step is called, which makes the final determination whether a user is allowed to access the resource, based on the established reliable claims in the request, and possibly the properties of the resource.
Finally, to clear up confusion about when yada emits 200, 401 or 403, my preferred analogy is the passport. A countries passport office does the authentication step (you provide it with evidence of your identity, and it creates the passport). When you arrive at the border between 2 countries there are various outcomes. You might not need a passport to cross the border (200), you might have brought your passport with you anyway but not needed it (200), you might have needed it but forgot to bring it with you (401), you might have brought it with you but it doesn't have the required visa stamp (403). The 403 is useful because it says 'there's no point in coming back here with the same passport, you're still not entering this country', whereas the 401 means 'try bringing your passport with you next time?'
The upside of this interpretation is that it entirely aligns with the HTTP specs from a terminology perspective. Authentication means authentication, Authorization means authorization.
However, be warned, there are subtle differences between yada's approach and others' e.g. https://leastprivilege.com/2014/10/02/401-vs-403/
(Of course, I think I'm correct and that others are wrong, wrt. what the HTTP authors intended, but that's a matter of opinion!)
Oh and also, yada's built-in authorization scheme does allow for method-level role-based authorization, if that's what you're looking for.
Sorry for these lengthy explanations in slack, I will collate and produce a blog article from them
but to check if user
abc is authorized to GET document at
/doc/1234, I need to look up in the database in the access control table if
yes, one of the properties would be the 'owner' in this case. You add a
:properties entry with a value that is a function that takes the request context as the parameter. The docs are a bit too brief but they're here: https://juxt.pro/yada/manual/index.html#properties
your properties function is called after authentication but before authorization
you can do anything you like in a the properties call back, but the idea is that you might visit a database or something that established the properties of the resource. Depending on what you're doing, you might elect to load the whole resource in this step so you can provide the response without revisiting the database
also, like any other callback, you can return a future/promise/channel from this function.
in this case, the owner is a property of the resource, so you need ABAC rather than RBAC here.
yada can't really know what you mean by 'owner', so of course you have to provide your own authorization function
you can find many of the properties in the docs, in the source (especially yada.schema), but a ref guide is on its way
warning: most yada users end up cloning the yada repo and getting a basic familiarity with the code-base - that helps because the code-base makes a good reference as to what interceptors use what properties.
that might sound a very unsatisfactory excuse for poor documentation, but in practice the code for each interceptor is pretty small (around a dozen lines or so) and yada.resource shows which the list of the interceptors that make up the processing chain
for POST you can return a java.net.URI and yada will figure out that's the Location and set the status to 201
well, great - the docs are in /docs and in asciidoc (which I feel gives more features for rich tech docs)
> 10.2.2 201 Created > The request has been fulfilled and resulted in a new resource being created. The newly created resource can be referenced by the URI(s) returned in the entity of the response, with the most specific URI for the resource given by a Location header field. The response SHOULD include an entity containing a list of resource characteristics and location(s) from which the user or user agent can choose the one most appropriate. The entity format is specified by the media type given in the Content-Type header field. The origin server MUST create the resource before returning the 201 status code. If the action cannot be carried out immediately, the server SHOULD respond with 202 (Accepted) response instead.
you can also have more control over the response by returning
(:response ctx), perhaps modified. yada knows what
(:response ctx) is, and knows the record so doesn't confuse it with a map.
if you return a map from a yada response function, mostly yada thinks you're trying to return pure data, not a Ring response
The general idea is that yada follows the spec. If there's any debate between us about what yada should do in a given situation, the spec. always wins!
but it seemed like adding a clojure map as the :body was not converting correctly to json
oh - well, the reason is that if you are returning
(:response ctx) yada gets out of your way and you're in full control
but if you just return a map, and the content negotiation has establised, say,
application/json, then yada will kick in and convert the map to json for you
I see, if you want to return a body from the POST response (as well as your Location header), you have to do that yourself
Quite high up on the todo list is reactive negotiation, which is where you get 300 response codes and lists of representations
when I've added that to yada, it might be a good idea to allow you to return a list of URIs from a POST and yada can then generate the body
my understanding from the spec. snippet you quoted is that the body of a POST is arbitary (set by the user), it is rather a 'list of resource characteristics and locations'
so perhaps yada should allow a POST response function to return a list of maps, or something.
yes, it's funny, few people are familiar with it, there's lots of things in HTTP that aren't used. My design position is that it's all going to get implemented eventually, regardless of who uses it. Maybe folks don't use a feature because it isn't widely implemented rather than it not being useful, I don't know
I think thought that you're not really meant to return a body from POST (or rather, the actual body is prescribed by the spec.)
normally you POST and return a Location header and a 201 and then the user-agent can follow the Location header to show the resource.
I usually elect to return 201 because single page apps can follow links like this - if you want old-school behaviour where you generate redirects, that's possible too -you might only do so if you know you returning text/html. If you're returning application/json I don't think the redirect thing is useful
the ultimate goal behind yada is to make it really simple and easy for beginners to write web APIs that actually work, are standards compliant, and don't require all this manual soldering that people do all the time.
there's a bit of a learning curve with yada too because it's quite different conceptually from the usual 'write a function that takes a request and returns a response' nature of other web libs
btw. I've been watching Zach's Strange Loop talk today: https://www.youtube.com/watch?v=_1rh_s1WmRA
@ericnormand thanks for that bidi PR last night btw., I merged it to master and will release in due course
yes, the swagger part is made a lot easier once you have your services full specified in data, because it's really just a data->data transformation
@malcolmsparks i saw that talk shared in #aleph, it’s interesting because i’m working on something similar with yada (i personally call ‘subservices’)
basically the client part of yada, with monitoring, but also circuit breakers, bulkheads, etc
but yeah, it seems to be a missing component for microservices in the clojure ecosystem
basically ensuring thread starvation never happens by ensuring there’s exactly 1 thread per component
it’s a bit technical, so instead of explaining it myself, i’ll refer you to: https://johnragan.wordpress.com/2009/12/08/release-it-stability-patterns-and-best-practices/
tl;dr: bulkheads prevent deadlocks in distributed systems where service A is waiting for service B that is waiting for service A again
this is very close to what I've been thinking about regarding yada supporting 503s and 408s, throttling (which manifold already has a function for), rate-limiting (controllable programmatically), etc.
> I am 110 pages into Michael Nygard’s “Release It!”. I ignored this book earlier because I pictured some namby, pamby book on release best practices, but this book is absolutely fantastic and fascinating to read!
well, either way, i’ll probably release it as opensource in the near future, as a kind of middleware between yada and aleph’s http client
thanks - Zach's talk has already reminded me I need to add support for timeouts and 408s. I'm really keen on rate-limiting because something I read on HN the other day about an Apple hack where they didn't rate-limit the 'find my phone' feature
I really love hammock-driven development now, I don't feel guilty spending some time thinking about a design
but it would be nice to integrate it with something that could do rate-limiting
and I'd love to put something into yada that collected metrics about how long each interceptor was taking to give a comprehensive view of the status of services
I think what I mean is that, as a yada user, I'd like to be able to say (in my resource declaration) that the resource should only allow so many requests per second -I'd like to declare that on a per-resource basis.
So if I was returning a static file, I don't really care, but if I'm looking up something in a database, or doing some authorization requiring a network call, I'd like to signal a limit
ahh right, i guess that’s valid — you should implement it using manifold’s stream rate limiting, probably
but it would also be nice to do this dynamically, e.g. backpressure if I'm detecting that a call to a remote service is taking longer than usual
and I think the web is good enough an architecture for companies to use internally