Skip to main content

Securing MarkLogic Server

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.

  1. Using the Admin Interface, configure the app server to use a custom login page:

    1. Go to the Configuration tab for the HTTP or WebDAV app server for which you want to create a custom login page.

    2. Scroll down to the authentication field and select application-level.

    3. For this example, choose nobody as the default user. The nobody 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.

  2. Define try-ip-login():

    1. Create a file named login-routine.xqy and place the file in the Modules directory within the MarkLogic Server program directory. You create an amp for try-ip-login() in login-routine.xqy in the next code sample. For security reasons, all amped functions must be located in the specified Modules directory or in the Modules database for the app server.

    2. 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 username local-user or site-user as appropriate and returns true. Otherwise, try-ip-login() returns false.

    Note

    In the code snippet above, the empty sequence () is supplied in place of the actual passwords for local-user and site-user. The pre-defined xdmp-login execute privilege grants the right to call xdmp: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.

  3. 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")
  4. Finally, to ensure that the code snippet above is called with the requisite xdmp-login privilege, configure an amp for try-ip-login():

    1. Using the Admin Interface, create a role called login-role.

    2. Assign the pre-defined xdmp-login execute privilege to login-role. The xdmp-login privilege gives a user of the login-role the right to call xdmp:login() for any user without supplying the password.

    3. Create an amp for try-ip-login as shown below:

    Screenshot illustrating amp fields filled in for try-ip-login

    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() the login-role temporarily for the execution of the function.

    In this example, default.xqy is executed as nobody, the default user for the application. When try-ip-login() is called, the nobody user is temporarily amped to the login-role. The nobody user is temporarily assigned the xdmp-login execute privilege by virtue of the login-role. This enables nobody to call xdmp:login() in try-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 of local-user or site-user as appropriate.

  5. The remainder of the example assumes that local-user and site-user can access all the pages and functions within the application.

    1. Create a role called application-user-role.

    2. Create an execute privilege called application-privilege. Add this privilege to the application-user-role.

    3. Add the application-user-role to local-user and site-user.

    4. 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.