Access Control and Data Validation for App Endpoints

      +
      Access Control and Data Validation is vital to the security of your App Endpoint.

      You can access the Access Control and Data Validation function for an App Endpoint through the App Endpoint’s configuration screen:

      1. From the App Services screen, you can select the App Endpoints tab.

        Select app endpoint
        Figure 1. Select App Endpoint

        The next screen will allow you to configure the endpoint.

      2. Select the Security  Access and Validation option.

        Updating the Access Control and Data Validation function
        Figure 2. Updating the Access Control and Data Validation Function

        The provided Javascript function executes every time a new revision/update is made to a document.

        The Capella UI (shown in Figure 2) will check that the JavaScript function is valid when the APPLY button is pressed.
        You can restore the function to its original default by clicking RESTORE TO DEFAULT.

      The Access Control and Data Validation Function

      The default function performs no validation; it simply assigns the document to the channels specified in its 'channels' attribute.

      function (doc, oldDoc, meta) {
          channel(doc.channels);
      }

      The function arguments are:

      Name Description

      doc

      This object references the content of the document that is being saved. It matches the JSON saved by the Couchbase Lite application and replicated to the App Service.

      The document’s _id property contains the document ID The document’s _rev property is the new revision ID. If the document is being deleted, it will have a _deleted property with the value true.

      oldDoc (optional)

      If the document has been saved before, this object references the revision being replaced; otherwise it is null.
      Note: In the case of a document with conflicts, the current provisional winning revision is passed in oldDoc.

      meta (optional)

      This argument references the user defined XATTR that you can use to hold access grant data.

      The referenced object can include items such as channels or roles. So, instead of embedding channel information directly within the document body, users can specify the user-defined XATTR associated with the document.

      Writing a Custom Access Control and Data Validation Function

      Consider your access control and document distribution requirements. For example:

      • The document types it will process

      • The users it will serve

      • Which users need to access which document types

      • What constraints are to be placed on creating, updating and/or deleting documents.

      Access Control Function Example

      This example demonstrates a number of possible use-cases that may be useful to you. Start by creating your function as usual:

      function (doc, oldDoc, meta) {
          // ...
      }

      The following example defines channel settings with the content of an XATTR:

      function (doc, oldDoc, meta) {
      
        if (meta.xattrs.channelXattr === undefined)
          {
            console.log("no user_xattr_key defined")
            channel(null)
          } else {
            channel(meta.xattrs.channelXattr)
      
          }

      The meta parameter exposes the user defined user_xattr_key if it is defined, and uses the content of the XATTR to define the channels setting for the document.

      Handling Deletion

      In this example, we require the user to:

      • have the Editor role

      • be one of the original writers of the document.

          if (doc._deleted) {
              requireRole("role:editor");
              requireUser(oldDoc.writers);
      
              // Skip other validation because a deletion has no other properties:
              return;
          }

      Handling Required Properties

      In this example, we:

      • require the properties: 'title', 'creator', 'channels', 'writers'

      • expect the 'channels' and 'writers' properties to be lists, and require the 'writers' list to be non-empty.

          if (!doc.title ||
              !doc.creator ||
              !doc.channels ||
              !doc.writers)
          {
              throw({forbidden: "Missing required properties"});
          }
          else if (doc.writers.length == 0) {
              throw({forbidden: "No writers"});
          }

      Handling Creation

      If oldDoc is not passed to the function, then a new document is being created. In this example, we:

      • require the user to have the 'editor' role

      • require the user to match the original 'creator' of the document.

          if (! oldDoc) {
              requireRole("role:editor");
              requireUser(doc.creator)
          }

      Handling Modification

      If oldDoc is passed to the function, we know that document is being modified. In this example:

      • Only users in the existing doc’s writers list can change a document

      • The 'creator' property is immutable.

          if (oldDoc) {
              requireUser(oldDoc.writers);
      
              if (doc.creator != oldDoc.creator) {
                      throw({forbidden: "Can't change creator"});
              }
          }

      Assigning the Document to Channels

      In this example, we assign the document to the channels in the list:

          channel(meta.xattrs.[xattrName]);