Hiding a column from some users: a masked salary
Row-level security decides which rows someone sees. A mask decides one column: everyone signed in sees the staff directory, and a salary is seen by the person it is paid to and by their manager.
authz
type employee = app.employees
self : user = user_id
manager : user = manager_id
can see_salary = self or manager
rules app.employees view app.employees_visible
select : signed_in
mask salary : see_salaryview app.employees_visiblemakes a view with the rowsselectallows.mask salary : see_salary: in that viewsalaryis NULL unless the rule holds for the row.- Applying the policy takes the app role's
SELECTonsalaryin the table itself away, so the view is the only way to read it. The other columns of the table stay readable. - Applying refuses to go on if the column would still be readable through another role, and
authz.lint()reports a laterGRANTthat makes it readable again.
Tested
Ann manages Bo; Cy is a colleague:
authz
test "everyone sees the directory; a salary, only the person and their manager"
as user $cy sees 1 {SELECT FROM app.employees_visible WHERE id = $e AND title = 'Engineer' AND salary IS NULL}
as user $bo sees 1 {SELECT FROM app.employees_visible WHERE id = $e AND salary = 5000}
as user $ann sees 1 {SELECT FROM app.employees_visible WHERE id = $e AND salary = 5000}
as user $cy refused {SELECT salary FROM app.employees WHERE id = $e}
as user $cy sees 1 {SELECT title FROM app.employees WHERE id = $e}Try it
Open it in the playground: the tables, the policy and the tests, in your browser, with nothing to install. Or take the files of docs/cookbook/hiding-a-column/ (schema.sql, policy.authz, tests.authz) and run rowstile dev. CI applies and tests them on every change (core/tests/cookbook.sh), and every line shown here is in those files.
More: masked columns, the other recipes.