How can I allow access to all queries for a role in Hasura?

Viewed 232

My Hasura instance is configured to accept JWT tokens and use them for authorization. It has 40 tables.

I want a role of an 'all-access' flavor. This role can issue any query - no filtering, no denied operations. They will not be an admin.

How can I tell Hasura that this role should have access to every query? It seems that I need to to manually (or via script) grant access to every table.

The problems I see:

  1. Manually will take a long time and be error prone
  2. It will be difficult to confirm that permissions are complete and accurate (auditing)
  3. If we add a new table, it will default to inaccessible. This is not desired
  4. The script will be complicated. It will likely require schema introspection, aggregate handling, etc. It will be unclear when the script should be run

Not the same, but I would hope for something like this excerpt from firestore security rules:

// Allow read/write access on all documents to any user signed in to the application
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.auth != null;
    }
  }
}
1 Answers

Until a better solution presents itself, here's my node script. Using Hasura 1.3.3. It should grant all permissions to all tables to a superuser role. Assumes no security enabled on hasura yet, so you may need to pass the admin secret key as a header to authenticate if security is enabled.

/**
 * Export metadata from hasura
 * Loop over all tables
 * Grant all actions to these tables to all users
 */

const fetch = require('node-fetch');

const hasuraUrl = 'http://localhost:18080/v1/query';

async function main() {

  const metadata = await getMetadata()
    .catch( e => console.error(`Unable to fetch schema. Cannot grant permissions.`, e) ); 

  metadata.tables.map( table => {

    // Grant all actions to a superuser-type role
    const fetches = grantAllActions( table );

    return fetches;
  });
}

async function getMetadata() {
  const body = {
    type: 'export_metadata',
    args: {}
  }

  return fetch( hasuraUrl, {
    method: 'post',
    headers: { 
      'Content-Type': 'application/json'
    },
    body: JSON.stringify( body )
  })
  .then( res => res.json() );
}

async function grantAllActions( tableDef ) {
  // Check: {}  means a "TrueExp". It will always pass the check, regardless of row contents
  const grantInsert = {
    type: 'create_insert_permission',
    args: {
      table: {
        name: tableDef.table.name,
        schema: 'myschema'
      },
      role: 'my-superuser',
      permission: {
        check: {},
        filter: {},
        columns: '*'
      }
    }
  }
  // Use the above as a template and modify as needed to satisfy the required keys on different grants
  const grantUpdate = JSON.parse( JSON.stringify( grantInsert ));
  const grantDelete = JSON.parse( JSON.stringify( grantInsert ));
  const grantSelect = JSON.parse( JSON.stringify( grantInsert ));

  grantUpdate.type = 'create_update_permission';
  
  grantDelete.type = 'create_delete_permission';
  delete grantDelete.args.permission.columns;
  
  grantSelect.type = 'create_select_permission';
  grantSelect.args.permission.allow_aggregations = true;

  const $fetches = [grantSelect, grantUpdate, grantDelete, grantInsert].map( grantObject => {

    return fetch( hasuraUrl, {
      method: 'post',
      headers: {
        'Content-Type': 'application/json',
        'X-Hasura-Role': 'admin'
      },
      body: JSON.stringify( grantObject )
    })
      .then( res => console.log( res ))      
      .catch( e => console.warn( `Error during superuser grant. table: ${tableDef.table.name}`, e ) );
    
  });

  return Promise.all($fetches);
}

main();

Related