Access Control Based on Client IP Address
MarkLogic Server supports deployments in which a user is automatically given access to the application based on the client IP address.
Consider a scenario in which a user is automatically logged in if he is accessing the application locally (as local-user
) or from an approved subnet (as site-user
). Otherwise, the user is asked to login explicitly. The steps below describe how to configure MarkLogic Server to achieve this access control.
Using the Admin Interface, configure the app server to use a custom login page:
Go to the Configuration tab for the HTTP or WebDAV app server for which you want to create a custom login page.
Scroll down to the authentication field and select
application-level
.For this example, choose
nobody
as the default user. Thenobody
user is automatically created when MarkLogic Server is installed. It is created with the following roles:rest-reader
,rest-extension-user
,app-user
,harmonized-reader
and is given a password, which is randomly generated.
Define
try-ip-login()
:Create a file named
login-routine.xqy
and place the file in theModules
directory within the MarkLogic Server program directory. You create an amp fortry-ip-login()
inlogin-routine.xqy
in the next code sample. For security reasons, all amped functions must be located in the specifiedModules
directory or in theModules
database for the app server.Add the following code to
login-routine.xqy
:xquery version "1.0-ml" module namespace widget ="http://widget.com"; declare function widget:try-ip-login( ) as xs:boolean { let $ip := xdmp:get-request-client-address() return if(fn:compare($ip,"127.0.0.1") eq 0) then (:local host:) xdmp:login("localuser",()) else if(fn:starts-with($ip,"<approved-subnet>")) then xdmp:login("site-user",()) else fn:false() };
If the user is accessing the application from an approved IP address,
try-ip-login()
logs in the user with usernamelocal-user
orsite-user
as appropriate and returnstrue
. Otherwise,try-ip-login()
returnsfalse
.Note
In the code snippet above, the empty sequence () is supplied in place of the actual passwords for
local-user
andsite-user
. The pre-definedxdmp-login
execute privilege grants the right to callxdmp:login()
without the actual password. This makes it possible to create deployments in which users can be automatically logged in without storing user passwords outside the system.Add the following code snippet to the beginning of the default page displayed by the application, for example,
default.xqy
.xquery version "1.0-ml"; import module namespace widget = "http://widget.com" at "/login-routine.xqy"; let $login := widget:try-ip-login() return if($login) then <html> <body> The protected page goes here. You are {xdmp:get-current-user()} </body> </html> else xdmp:redirect-response("login.xqy")
Finally, to ensure that the code snippet above is called with the requisite
xdmp-login
privilege, configure an amp fortry-ip-login()
:Using the Admin Interface, create a role called
login-role
.Assign the pre-defined
xdmp-login
execute privilege tologin-role
. Thexdmp-login
privilege gives a user of thelogin-role
the right to callxdmp:login()
for any user without supplying the password.Create an amp for
try-ip-login
as shown below:
An amp temporarily assigns additional role(s) to a user only for the execution of the specified function. The amp above gives any user who is executing
try-ip-login()
thelogin-role
temporarily for the execution of the function.In this example,
default.xqy
is executed asnobody
, the default user for the application. Whentry-ip-login()
is called, thenobody
user is temporarily amped to thelogin-role
. Thenobody
user is temporarily assigned thexdmp-login
execute privilege by virtue of thelogin-role
. This enablesnobody
to callxdmp:login()
intry-ip-login()
for any user without the corresponding password. Once the login process is completed, the user can access the application with the permissions and privileges oflocal-user
orsite-user
as appropriate.The remainder of the example assumes that
local-user
andsite-user
can access all the pages and functions within the application.Create a role called
application-user-role
.Create an execute privilege called
application-privilege
. Add this privilege to theapplication-user-role
.Add the
application-user-role
tolocal-user
andsite-user
.Add this snippet of code before the code that displays each of the subsequent pages in the application:
try { xdmp:security-assert("application-privilege","execute") ... } catch($e) { xdmp:redirect-response("login.xqy") }
or
if(not(xdmp:has-privilege("application-privilege","execute"))) then ( xdmp:redirect-response("login.xqy") ) else ()
This ensures that only the user who has the
application-privilege
by virtue of his role can access these protected pages.