Package: datasette-permissions-sql
datasette-permissions-sqlDatasette plugin for configuring permission checks using SQL queriesInstallationInstall this plugin in the same environment as Datasette.$ pip install datasette-permissions-sqlUsageFirst, read up on how Datasette'sauthentication and permissions systemworks.This plugin lets you define rules containing SQL queries that are executed to see if the currently authenticated actor has permission to perform certain actions.Consider a canned query which authenticated users should only be able to execute if a row in theuserstable says that they are a member of staff.Thatuserstable in themydatabase.dbdatabase could look like this:idusernameis_staff1cleopaws02simon1Authenticated users have anactorthat looks like this:{"id":2,"username":"simon"}To configure the canned query to only be executable by staff users, add the following to yourmetadata.json:{"plugins":{"datasette-permissions-sql":[{"action":"view-query","resource":["mydatabase","promote_to_staff"],"sql":"SELECT * FROM users WHERE is_staff = 1 AND id = :actor_id"}]},"databases":{"mydatabase":{"queries":{"promote_to_staff":{"sql":"UPDATE users SET is is_staff=1 WHERE id=:id","write":true}}}}}The"datasette-permissions-sql"key is a list of rules. Each of those rules has the following shape:{"action":"name-of-action","resource":["resource identifier to run this on"],"sql":"SQL query to execute","database":"mydatabase"}Both"action"and"resource"are optional. If present, the SQL query will only be executed on permission checks that match the action and, if present, the resource indicators."database"is also optional: it specifies the named database that the query should be executed against. If it is not present the first connected database will be used.The Datasette documentation includes alist of built-in permissionsthat you might want to use here.The SQL queryIf the SQL query returns any rows the action will be allowed. If it returns no rows, the plugin hook will returnFalseand deny access to that action.The SQL query is called with a number of named parameters. You can use any of these as part of the query.The list of parameters is as follows:action- the action, e.g."view-database"resource_1- the first component of the resource, if one was passedresource_2- the second component of the resource, if availableactor_*- a parameter for every key on the actor. Usuallyactor_idis present.If any rows are returned, the permission check passes. If no rows are returned the check fails.Another example table, this time granting explicit access to individual tables. Consider a table calledtable_accessthat looks like this:user_iddatabasetable1mydbdogs2mydbdogs1mydbcatsThe following SQL query would grant access to thedogsttable in themydb.dbdatabase to users 1 and 2 - but would forbid access for user 2 to thecatstable:SELECT*FROMtable_accessWHEREuser_id=:actor_idAND"database"=:resource_1AND"table"=:resource_2In ametadata.yamlconfiguration file that would look like this:databases:mydb:allow_sql:{}plugins:datasette-permissions-sql:-action:view-tablesql:|-SELECT*FROMtable_accessWHEREuser_id = :actor_idAND "database" = :resource_1AND "table" = :resource_2We're usingallow_sql: {}here to disable arbitrary SQL queries. This prevents users from runningselect * from catsdirectly to work around the permissions limits.Fallback modeThe default behaviour of this plugin is to take full control of specified permissions. The SQL query will directly control if the user is allowed or denied access to the permission.This means that the default policy for each permission (which in Datasette core is "allow" forview-databaseand friends) will be ignored. It also means that any otherpermission_allowedplugins will not get their turn once this plugin has executed.You can change this on a per-rule basis using"fallback": true:{"action":"view-table","resource":["mydatabase","mytable"],"sql":"select * from admins where user_id = :actor_id","fallback":true}When running in fallback mode, a query result returning no rows will cause the plugin hook to returnNone- which means "I have no opinion on this permission, fall back to other plugins or the default".In this mode you can still returnFalse(for "deny access") by returning a single row with a single value of-1. For example:{"action":"view-table","resource":["mydatabase","mytable"],"sql":"select -1 from banned where user_id = :actor_id","fallback":true}