haproxy
haproxy copied to clipboard
Support of array in json_query / jwt_payload_query
Your Feature Request
Currently if I want to assign e.g. the roles of a jwt token to a variable I can do a query like:
http-request set-var(txn.roles) http_auth_bearer,jwt_payload_query('$.resource_access.account.roles[0]')
Which returns the first role. If I select
http-request set-var(txn.roles) http_auth_bearer,jwt_payload_query('$.resource_access.account.roles[*]')
instead I would expect to get a string of an json array of all roles, but the variable only contains the first role. Since the number of roles can vary with each call, it is difficult to define a static number of elements and concatenate to a string.
With a json array string I could do a check with e.g,
http-request deny deny_status 401 content-type 'text/html' string 'Invalid roles' unless { var(txn.roles) -m sub 'offline_access' }
if a specific role is present.
What are you trying to do?
I need to evaluate a jwt bearer token and check if the token contains a specific set of roles
Output of haproxy -vv
NA
we use the msjon lib ( https://github.com/cesanta/mjson) for json parsing. there is a funcion mjson_next which is able to return JSON Arrays but this is not yet implemented https://github.com/haproxy/haproxy/blob/master/src/sample.c#L4163 . Patches are welcome for that feature request
As mentioned by @git001, the jwt converter uses the already existing json_query one which cannot return an array or an object directly yet (see sample_conv_json_query).
It should be possible to simply dump the full array or object returned by mjson_find by simply relying on the tokptr and toklen set by the parser.
int mjson_find(const char *s, int len, const char *path, const char **tokptr, int *toklen);
But bear in mind that in your case you would end up with a manageable output since your array contains only strings but some other users might end up having too big of an array which would not even fit in a buffer.
Regardless, that does not seem that hard to do and you could even try to work on it yourself if you feel like it.
Hello, thanks for the details ;-) I already had a look and understand the issue. I will bring it up in the next internal meeting, if I can get a C developer to work on this. You are correct, my solution would only work for an array of strings and only for a fixed maximum size. However without being able to check the roles, the jwt feature is only of limited value to us. I will also start an internal discussion, if we could do something based on scope (which is already a concatenated string in the token) as a workaround for now...
You can temporarily solve it with lua :) Last year I was in the same place and in the end I used lua-cjson
Hi all, I made a small change in my fork: https://github.com/jenspopp/haproxy/blob/master/src/sample.c#L4162C2-L4162C2
I think handling this with the mjson_next as proposed by @git001 is a bit of overkill for this use case. This would be good, if you really have a json path like '$.resource_access.account.roles[*]'. But by removing the [*] I get the complete array and I don't need to traverse through the single elements.
Basically I only copy the complete json array into a string and return this value. I built and tested it and it works for my use case. This is basically what was proposed by @rlebreton
The issue with the length is valid, and perhaps we could introduce a maximum like 1024 chars? Or in a next phase with an additional parameter in the config?
Right now there is also the clumsy code to remove linebreaks. I think it would be better to work with regex, but I am not sure due to the pcre support switches... (something like replace "\s*,\s*" with "," )
Another way would to simply use strncpy and not touch the whitspace and linebreak characters...
Any help and comments are welcome...
@jenspopp how do you handle then the return value in haproxy config?
How about to use $.resource_access.account.roles['offline_access'] and add a second argument for sample_conv_json_query
My current working config is
...
http-request set-var(txn.roles) http_auth_bearer,jwt_payload_query('$.resource_access.account.roles')
...
http-request deny deny_status 401 content-type 'text/html' string 'Invalid resource roles' unless { var(txn.roles) -m sub '"manage-account-links"' }
The variable txn.roles contains: ["manage-account","manage-account-links","view-profile"]
I think that's a good start point, from my point of view.
Please take a look into https://github.com/haproxy/haproxy/blob/master/CONTRIBUTING and send a patch to mailing-list .
Please also mention in the Patch if it should be backported to any version, from my point of view could it be backported to at least 2.6 because it's not very invasive.
+1
When can this be expected to be fixed?
@aman-github-ns A patch was sent to the mailing list, as soon as the exchanges regarding this patch are done, the patch should be merged. It should be a matter of days. It would then be included in the next 2.9 version.
@rlebreton Hi, I uploaded the latest patch. Is it also possible to backport to 2.8 branch?
Hello, is there any step I missed? Should I create a merge request?
@jenspopp Not at all, it is currently being reviewed by @wlallemand but we have some ongoing talks about whether we should take the patch as-is or go all the way and properly add the support of objects and "bigger" arrays in the json_query converter. Sorry for the delay but don't worry, your patch is still in the pipe we did not forget about your dev.