How to authenticate exist-db users in RESTXQ

703 views Asked by At

(complete rephrase - since no answer): I am developing an exist-db application with user authentication and RESTXQ. My users log in via the login:set-user function from the login module. Here a snippet from the controller:

import module namespace login="http://exist-db.org/xquery/login" at "resource:org/exist/xquery/modules/persistentlogin/login.xql";

declare variable $local:login_domain := "org.exist-db.superApp";
declare variable $local:user := $local:login_domain || '.user';

let $logout := request:get-parameter("logout", ())
let $set-user := login:set-user($local:login_domain, (), false())

this works perfectly fine. If I call e.g. xmldb:get-current-user() anywhere within an app:function() I get the currently logged in username.

I also have a RESTXQ module with a couple of functions to be dynamically called via URL by AJAX requests. Some of these actions (deleting xml-elements in the data, adding new collections...) are rather critical. I don't want guests (or users with the wrong rights) to be able to just call these RESTXQ URLs to manipulate the data - so RESTXQ must be secured and it should doublecheck: is the currently logged in user allowed to modify specific resources/collections?

If I call e.g. xmldb:get-current-user() in any RESTXQ function, I always get "guest", all the security manager (sm) functions also indicate that within RESTXQ there is no knowledge about the current user login. Only if I do something like xmldb:login("/db","username","password") in each RESTXQ function, RESTXQ seems to know someone is logged in, but usernames and passwords should not constantly be passed back and forth via URL - yet without passing this data in, RESTXQ functions don't seem to know about it (right?).

So how do I make sure, RESTXQ lets only authenticated users change data (without users having to authenticate multiple times and without having to pass the authentication data to RESTXQ on each request)?.

example use-case: I want a RESTXQ function that gets a search-string ($text) and a collection-path ($path) as input, now it checks for all xml files in $path: If the currently logged-in-user (must be authenticated!) has writing-access to the file, delete all nodes that contain $text and return some JSON response to the user...

I have multiple such functions, (move, delete and add data, add new collections... ), they work nicely except for the bold part: I do not get this If in. Any ideas?

PS: this topic on the eXist mailing list asks the same question. Here @adamretter sugests to restrict the RESTXQ module file itself, so users get prompted to (re-)authenticate when a function is called - even if they already used the persistent log-in. This is what I don't like:

  1. I don't want my users to have to log in multiple times (that's the idea of getting things as dynamic as possible - e.g. by using RESTXQ)
  2. the prompted login (for executing RESTXQ) and the previous login (persitent) via the login-module can differ now!
  3. even if the user logs out within the the application, RESTXQ is still granted access because that prompted authentication knows nothing about the logout. If a new user logs (not in the prompt, but via the login-module) she still has access to RESTXQ, because this is not re-prompted (and I don't know how to "logout" this prompted, seemingly parallel second login).
  4. I can restrict the whole RESTXQ module this way, but I still don't know how to ask within a RESTXQ function: "what access rights does current user have on resource A, B, C...
2

There are 2 answers

1
dariok On

Hoping that this will help others:

When making an AJAX-request, add a header:

[...]
headers: {'Authorization': 'Basic ' + btoa(username + ':' + password)},
[...]

This will log the user in with the given credentials (if correct). In your RESTXQ function you can then access the user's details via sm:id():

declare namespace sm = "http://exist-db.org/xquery/securitymanager";
[...]
let $username := xs:string(sm:id()//sm:real//sm:username)

You should use the info under sm:real as a setuid or setgid may mask the real values.

Note that the login information are not stored so you need to send them with every AJAX request you make.

0
adamretter On

The RESTXQ API is stateless. So you need to authenticate on each request. This intentionally the case and is done by design so that each RESTXQ call can work in isolation efficiently.

This isn't unusual as other APIs such as Amazon's S3 also require such authentication to be provided on each request.

How your users authenticate to RESTXQ and how you prompt them for the request credentials (and possibly reuse those) is outside the scope of the RESTXQ API, and instead is considered to be a client issue that you need to solve in your own application.