How to deal with complex permissions in Hasura - postgresql

Basics - I need to return data from columns based on some variables from a different table(I either return column or null if access is not allowed)
I have already done what I need via a custom function in postgres, but the problem is that in Hasura functions share the permission with the table/view it implements SETOF on.
So I have to allow the access to the table itself and as the result permissions in my function are kind of meaningless, because anyone will be able to access the data simply by querying the original table directly.
My current line of thinking is that the only way to do what I need is to create a remote schema and remove access to the original table.
But maybe there is a way to not expose some of the tables as a graphql query? If I could do something like this - I'd just hide my table and expose only a function.

The remote schema seems like it would work.
Another option would be the allow-queries option.
It's possible to limit queries. It's a bit tricky it seems, you need an exact copy of every query that should be allowed (with the fields in the exactly correct order), but if you do that, then only your explicitly whitelisted queries will be accepted. More info in the docs.
I'm not familiar enough with postgres permissions to offer any better ideas...

Related

Read-Only Postgraphile-CLI API

I am currently implementing a public API for a Open Data Platform with Postgraphile creating the needed API for me. The API should be completly public, with no authentification whatsoever and because of that the API should only implement read-only queries. Has anyone found a possibility to use Postgraphile-CLI to only create read-only functionality?
So far I have sucessfully setup a Postgraphile-CLI API for my postgres databases, with a user that only has the "GRANT SELECT" for the schemas in Postgres. However, this doesn't seem to work for my use case, since I still can use the mutation in graphql and insert or delete data from my schemas.
Since I don't know too much about postgres database administration, I therefor wonder If it is possible to just not provide mutations with Postgraphile-CLI.
Kind regards
Grigorios
EDIT0: I have found the mistake with my Postgres database rights. That may solve that read-only problem, but If anybody knows an answer to the initial question, I would be curious to know anyway.
You have a number of options:
Use permissions, as you suggest, along with the --no-ignore-rbac option - you will have to ensure your database permissions are and remain correct (no default grants to the public role, for example) for this to work
Use PostGraphile's --disable-default-mutations (-M) option; this will stop the CRUD mutations being generated but won't prevent custom mutation functions from being exposed, if you have any
Skip the MutationPlugin via --skip-plugins graphile-build:MutationPlugin - this will prevent the Mutation type from being added to the schema in the first place, so no mutations can be added.
For a real belt-and-braces approach, why not all three?
postgraphile \
--no-ignore-rbac \
--disable-default-mutations \
--skip-plugins graphile-build:MutationPlugin

How to implement complex permission based data access in Postgres with Postgraphile or alternatives

For a new project, we're currently designing a database and an API to access this. We've already established we'll be using PostgresQL for the database, and want to access it via a GraphQL API.
To ease with maintainability, we looked at several intermediaries between client/API/database, mainly Prisma, PostGraphile and Hasura. PostGraphile stood out, because of ease of use and the focus of handling stuff "in database" as opposed to in your backend code. However, we ran into issues when figuring out how to implement this.
Allow me to expand on what we designed thus far:
Provisional database design:
users table
groups table
roles table:
u_g_r table: A user can be part of multiple groups, and can have multiple roles in each group. This table represents foreign keys for users, groups and roles, as many-to-many relations can exist in virtually all combinations.
Data Permissions:
We want users to grant others access to their personal data in several steps, preferably for each group. For example:
level 3: Yourself and only absolutely necesary people, such as account manager
level 2: Only people in group X, Y, etc
level 1: Everybody
It would be awesome if it was possible to set this for various types of data, for example grant level 2 for your phone number, but only level 1 for your physical address.
So, these levels (1, 2, 3) would accompany data in the database, like phone_number and phone_number_access_level for example. Then, in the u_g_r junction table, each combination of user/group/role would have an allowed level attached to it, which must be higher than the required level for the relevant data. Thus, if your role allowed access to data on level 2, you would be able to view data on level 1 and 2, but not level 3.
Postgres allows both column- and row level security, to let users access certain data. The PostGraphile wiki goes into some detail (here and here) how you would make this work with JWT claims instead of PostGres roles.
Our problem arrives when we want to implement the above features. It seems we want a kind of 'field level security' that does not exist, but I can't imagine others not having had the same issues.
What would you advive us to do? Please let me know if there are options we've missed, or whether there are other options that are better for us!
Implementing this outside the database, in backend code might might be the easiest way in and of itself, but it greatly impacts maintainability for us, as the main luxury of things like PostGraphile for us is removing the need to write GraphQL schema's and resolvers ourselves.
It seems that you want all users to see all table rows, but only certain columns.
You probably cannot use column permissions, because these can only allow or deny access to the column as a whole and do not respect who “owns” a certain table row.
So perhaps views can do what you want, for example:
CREATE VIEW users_view
WITH (security_barrier = true, check_option = local) AS
SELECT /* accessible to everyone */
username,
/* accessible only to certain groups */
CASE WHEN pg_has_role('x', 'USAGE') OR pg_has_role('y', 'USAGE')
THEN level2_col
ELSE NULL
END AS level2_col,
/* accessible only to admins and owner */
CASE WHEN username = current_user OR pg_has_role('admin', 'USAGE')
THEN level3_col
ELSE NULL
END AS level3_col
FROM users;
security_barrier makes sure that nobody can use functoins with side effects to subvert security, and check_option ascertains that nobody can INSERT a row that is not visible to themselves.
You can allow DML operations on the views if you define INSTEAD OF triggers.
Based on the answer of Laurenz Albe, I created an immense view for all kinds of columns. It worked, certainly, and even with several thousands of entries of mock data it was still relatively quick.
When I got back to it last week, a cleaner solution (arguably) dawned on me. Instead of using custom views like this, I'm now using separate tables with the sensitive data, link them with foreign keys and enable Row Level Security on these rows.
I haven't done any benchmarks, but it should be faster as this data isn't always requested anyways. It at least saves complicated views with a lot of boilerplate!

Row level security using prisma and postgres

I am using prisma and yoga graphql servers with a postgres DB.
I want to implement authorization for my graphql queries. I saw solutions like graphql-shield that solve column level security nicely - meaning I can define a permission and according to it block or allow a specific table or column of data (on in graphql terms, block a whole entity or a specific field).
The part I am stuck on is row level security - filtering rows by the data they contain - say I want to allow a logged in user to view only the data that is related to him, so depending on the value in a user_id column I would allow or block access to that row (the logged in user is one example, but there are other usecases in this genre).
This type of security requires running a query to check which rows the current user has access to and I can't find a way (that is not horrible) to implement this with prisma.
If I was working without prisma, I would implement this in the level of each resolver but since I am forwarding my queries to prisma I do not control the internal resolvers on a nested query.
But I do want to work with prisma, so one idea we had was handling this in the DB level using postgres policy. This could work as follows:
Every query we run will be surrounded with “begin transaction” and “commit transaction”
Before the query I want to run “set local context.user_id to 5"
Then I want to run the query (and the policy will filter results according to the current_setting(‘context.user_id’))
For this to work I would need prisma to allow me to either add pre/post queries to each query that runs or let me set a context for the db.
But these options are not available in prisma.
Any ideas?
You can use prisma-client instead of prisma-binding.
With prisma-binding, you define the top level resolver, then delegates to prisma for all the nesting.
On the other hand, prisma-client only returns scalar values of a type, and you need to define the resolvers for the relations. Which means you have complete control on what you return, even for nested queries. (See the documentation for an example)
I would suggest you use prisma-client to apply your security filters on the fields.
With the approach you're looking to take, I'd definitely recommend a look at Graphile. It approaches row-level security essentially the same way that you're thinking of. Unfortunately, it seems like Prisma doesn't help you move away from writing traditional REST-style controller methods in this regard.

SQL Server - Return rows based on user role

We are developing an Access application with a SQL Server backend. We have a table that has records that belong to division A, B or C. The users also belong to role A, B or C. We want each user to see only their corresponding division records, as well as only certain columns.
I've thought of two ways, one making different queries for each role and then, based on the user's role, change the source object of the form. However I don't know if it is possible to retrieve it from SQL SERVER with VBA (all VBA documentation I've found so far is quite lacking).
The other solution I thought was to implement this on the server, however I don't know how a T-SQL query or view could fetch only the information needed based on the user's role
Any ideas?
PS: I can't use functions or stored procedures. For some reason the SQL Server we have been provided has them disabled and IT Ops won't enable them (Don't know the logic behind that).
Okay, it's been a while since I posted this but I'll post the solution I came up with in the end. VBA is not quite necessary in this case. It can be done perfectly with views.
To retrieve the users roles, (inner) join the table database_role_members twice with the database_principals one. Join by Id (from database_principals) on both fields. With this, you get a list of all roles and their corresponding users. To get the roles of the user querying the database simply add a where clause that checks that the user name corresponds with the function USER_NAME.
Then, don't give permission to those roles to access the table we want to restrict access to. Instead, make a view that fetches info from that table and add a where clause that looks up the value from a column against the query that retrieves the user roles.
With this you can make a link in access to the view and will allow you to see only the records that correspond to the user roles.
While this approach is easy, it doesn't allow for more complicated row level security. For a more powerful approach it might be useful to check the following link.
https://msdn.microsoft.com/en-us/library/dn765131.aspx
You could create the same tables with different schemas and assign user rights to different schemas. For example, instead of using dbo.Users you could have Accounting.Users and Warehouse.Users. Assign users in an accounting group to the Accouting schema. Or as suggested above those could be views within a schema that select data from underlying tables.

What kind of int storage is this?

We have an Firebird database for a (very crappy) application, and the app's front end, but nothing in between (i.e. no source code).
There is a field in the database that is stored as -2086008209 but in the front-end represents as 63997.
Examples:
Database Front-End
758038959 44093
1532056691 61409
28401112 65866
-712038758 40712
936488434 43872
-688079579 48567
1796491935 39437
1178382500 30006
1419373703 66069
1996421588 48454
890825339 46313
-820234748 45206
What kind of storage is this? The aim for us here is to access the application's back-end data and bypass the front-end GUI alltogether, so I need to know how to decode this field in order to get appropriate values from it. It is stored as a int in FireBird (I don't know if FireBird has signed/unsigned ints, but this is showing as signed when we select it).
This is the definition of the field:
It is not, as far as I can tell, de-normalised. The generator GEN_CONTACTS_ID has 66241 against it, which at a glance looks accurate.
I work on with an application that stores bitmaps in integers (just don't ask), if you express them in that form do you something useful or consistant
My impression is that the problem is in the front end. If what is stored in the DB is -2086008209, then what is stored in the DB is -2086008209. To understand better how the application is manipulating the data, try storing other numbers in the DB and see how they are displayed.
Did you come to this realization through logging SQL? If you havent, you may serve yourself well by using the Firebird Trace API to get that SQL: http://www.firebirdfaq.org/faq95/. An easier tool to parse the Trace API is this commercial product: http://www.upscene.com/products.fbtm.index.php.
I've used these tools and other techniques (triggers etc,.) to find what an application is using/changing in the Database.
Of course, if the SQL statement is select * from table, then these tools would not help much.