Enforcing Column-Based Access

We want to manage user access to different data depending on a database relationship. In the recipe below, we will manage supplier access to their products. A supplier can't see other supplier's products.

To implement column-based access, we will use supplier's email from a JSON Web Token, and the queryRewrite extension point to manage data access.

We have Products and Suppliers cubes with a hasOne relationship from products to suppliers:

cube(`Products`, {
  sql: `SELECT * FROM public.products`,

  joins: {
    Suppliers: {
      relationship: `belongsTo`,
      sql: `${CUBE}.supplier_id = ${Suppliers.id}`,
    },
  },

  dimensions: {
    name: {
      sql: `name`,
      type: `string`,
    },
  },
});
cube(`Suppliers`, {
  sql: `SELECT * FROM public.suppliers`,

  dimensions: {
    id: {
      primaryKey: true,
      sql: `id`,
      type: `string`,
    },

    email: {
      sql: `email`,
      type: `string`,
    },
  },
});

Let's add the supplier email filter if a query includes any dimensions or measures from the Products cube:

module.exports = {
  queryRewrite: (query, { securityContext }) => {
    const cubeNames = [
      ...(query.dimensions || []),
      ...(query.measures || []),
    ].map((e) => e.split('.')[0]);

    if (cubeNames.includes('Products')) {
      if (!securityContext.email) {
        throw new Error('No email found in Security Context!');
      }

      query.filters.push({
        member: `Suppliers.email`,
        operator: 'equals',
        values: [securityContext.email],
      });
    }

    return query;
  },
};

To get the supplier's products, we will send two identical requests with different emails inside JWTs.

{
  "iat": 1000000000,
  "exp": 5000000000,
  "email": "purus.accumsan@Proin.org"
}
{
  "iat": 1000000000,
  "exp": 5000000000,
  "email": "gravida.sit.amet@risus.net"
}

We have received different data depending on the supplier's email.

// purus.accumsan@Proin.org
[
  {
    'Products.name': 'Awesome Soft Salad',
  },
  {
    'Products.name': 'Rustic Granite Gloves',
  },
];
// gravida.sit.amet@risus.net
[
  {
    'Products.name': 'Incredible Granite Cheese',
  },
];

Please feel free to check out the full source code or run it with the docker-compose up command. You'll see the result, including queried data, in the console.

Did you find this page useful?