Securing read operations

To secure a read operation, you'll want to add a read rule.

module.exports = {
  "default": {
    "projects": {
      "read": () => {
        return true;
      }
    }
  }
};

Whether you're reading a collection of documents or an individual document, each document will be passed through the read rule, allowing you to return true or false. This allows you to granularly filter documents out of a collection.

Verify that the user is logged in

You may only want to allow logged in users to read certain documents from the database. Context information is passed to the read rule which can be inspected to determine login state.

module.exports = {
  "default": {
    "projects": {
      "read": (req) => {
        const { isLoggedIn } = req.client.auth;
        return !!isLoggedIn;
      }
    }
  }
};

Verify ownership of a document

Once you verify a user is logged in, you may also only want to allow them to read documents that belong to them. In addition to seeing the logged in user's information, you can see a snapshot of the document that is being read.

module.exports = {
  "default": {
    "projects": {
      "read": (req, res) => {
        const { isLoggedIn, data } = req.client.auth;
        
        return (
          !!isLoggedIn &&
          data.id === res.data.owner
        );
      }
    }
  }
};

Alter the data returned to the client

You may want to allow anyone to read a document, but only allow certain users to see privileged information on the document. For example, everyone can view a user's profile, but only that user can read their email address on the document.

module.exports = {
  "default": {
    "users": {
      "read": (req, res) => {
        const { isLoggedIn, data } = req.client.auth;
        
        if (!isLoggedIn) {
          return false;
        }
        
        if (data.id !== res.data.owner) {
          delete res.data.email; // redact `email` property
        }
        
        return true;
      }
    }
  }
};

Access other data to validate an operation

In some cases, you may need to access other data in the database in order to validate an operation. Within the rules.js file, you can use the Adamite SDK to query data just like you would on your client.

const adamite = require("@adamite/sdk").default;

module.exports = {
  "default": {
    "projects": {
      "read": async (req, res) => {
        const { isLoggedIn, data } = req.client.auth;
        
        if (!isLoggedIn) {
          throw new Error("Not logged in.");
        }
        
        const matchingOrganizations =
          await adamite()
            .database()
            .collection("organizations")
            .where("members", "array-includes", data.id)
            .get();
        
        if (matchingOrganizations.docs.length === 0) {
          throw new Error("User does not belong to any organizations.");
        }
        
        const matchingOrganizationIds = matchingOrganizations.docs.map((o) => {
          return o.id;
        });
        
        const projectOrganizationId = res.data.organization;
                
        if (!matchingOrganizationIds.includes(projectOrganizationId)) {
          throw new Error("User is not a member of project's organization.");
        }
        
        return true;
      }
    }
  }
};

Last updated