Implementing custom user permissions in GraphQL and Hasura - permissions

I am creating a webapp that allows users to communicate in several different rooms and am hoping that I can use GraphQL and Hasura for this project. There will be admins which can create/delete rooms (wow-room, lol-Room, pubg-room), create/delete users, and create/delete permissions (admin, wow, lol, pubg) along with assigning roles to users. Users will be able to see any the rooms which they have the permisions to.
The problem is, I want to be sure that only the admins can create/delete these rooms/users/permisions and that only the correct users can see these rooms. Is there a way I can get Hasura to check the permissions of the given user and return the appropriate data? I believe that I need to write a custom resolver but am not sure how that is done or if it is the correct solution.

While Hasura can handle dynamic roles (you can use the API to create new roles and new permission rules on the fly) with Hasura, I think in this case that's not required.
The problem is, I want to be sure that only the admins can
create/delete these rooms/users/permisions and that only the correct
users can see these rooms. Is there a way I can get Hasura to check
the permissions of the given user and return the appropriate data?
Hasura allows you to set a permission rule that traverse relationships which makes this possible. Check out the article-collaborators example in the Hasura docs.
Assuming that your models are rooms, users and room_users and you have the relationships set up so that room.users returns the list of users for a particular room, the permission for a user role on the rooms table can be expressed as follows:
Allow SELECT on a row in rooms if
rooms.users.id: _eq: x-hasura-user-id
This translates to: if a room's users list contains atleast one user_id that is equal to x-hasura-user-id then grant access to that room.
I've set this up on a heroku app: https://multiple-roles-hasura.herokuapp.com/console/api-explorer
Try the following queries out in GraphiQL:
Set the headers to:
x-hasura-role: user
x-hasura-user-id: 1
Run the following query:
query {
rooms {
id
name
}
}
You'll see that the response only contains the rooms that user1 has access to
Switch x-hasura-user-id through different values 1, 2, 3, 4, 5, 6 and you'll see different results for the same query. Basically the right rooms that the only the current user has access to.
Checkout the models and the permissions for the models on:
User & Room mappings: https://multiple-roles-hasura.herokuapp.com/console/data/schema/public/tables/room_users/browse
Room permissions: https://multiple-roles-hasura.herokuapp.com/console/data/schema/public/tables/rooms/permissions

Related

More efficient way to check if a user is in a group that itself belongs to another group?

We have groups that represent where the user is providing coverage, each user belongs to one group. Rather than put this in a database table that needs to be maintained I am trying to use the Microsoft Graph API. It is working well, except it now takes 10-12 seconds to load the page where this is done. When I pull this out, it's down to 3 seconds which is because of another API, so I believe it to be the culprit.
So let's say user Jane belongs to "NorthEast" group, which belongs to "Regions" group. So right now I am getting a list of the groups that belong to "Regions" then I am iterating through each user's groups (the big slow down is here) to see if any of them match a group that belongs to "Regions". If one does, then that is their assigned region.
I see MemberOf as a returned item in the list of groups that belong to "Region" but it is always NULL.
List of groups that belong to "Regions":
var members = await graphClient
.Groups["RegionGroupID"]
.Members
.Request()
.GetAsync();
List of a user's groups:
var memberOf = await graphClient
.Users[UserId]
.MemberOf
.Request()
.GetAsync();
I'm not very clear about your business logic, but as you said that you need to get all the "Regions" groups first, and then get all the groups of a specific user, then you may need to use "double for loop" to check if this user has a group which is one of the "Regions" groups.
And you said that the big slow down is here so I think you may try to add the "Region" group value to each user so that you can get the value directly. Let's see user property in graph api. How do you think that you set the "Region" group value to officeLocation or streetAddress? You may go to azure portal to edit them for each user and you can also get this property by graph api.

LDAP filter to only allow users that have a group membership

Is there a filter in LDAP that would let me filter out users that don't have a groupMembership? I can find information on filtering by group, but I'd like to say "if a user is not in any groups, then don't include them".
That would be something like: (&(objectClass=person)(!(groupMembership=*))). That just asks for all users where groupMembership is not present. Assuming groupMembership is a backlinked attribute on the user.
Not all directories store a static attribute on the user for each group they are a member of.
eDirectory for example stores Member on the Group, listing the users who are members. It stores GroupMembership on the user, listing all the groups the user is a member of.
Active Directory on the other hand stores Member on the Group but does not statically store the group membership data on the User. Rather it defines a dynamic attribute on the user, called MemberOf, that is evaluated, each time you query for the attribute.
That is, when you ask AD for the value of that attribute, it does an LDAP query in the background that is functionally something like (&(objectClass=Group)(Member=cn=MyUser,ou=MyOU,dc=domain,dc-local))
Thus the attribute is MemberOf, but I am not certain that a query for (!(memberOf=*)) would work, since that implies doing that implicit query on every object, which could be painful.

XACML Policy with Multiple Resources with Multiple Rules and Multiple Actions

In a multiple decision profile scenario I want to create a policy for a particular Tenant and for the root resources like Customer. Here my scenario is like I have a Tenant T1 and Tenant T1 is allowed to access Root resource Customer. Customer is the Top level resource and it will contain sub child resources like: Sub-Resources: name, email. In my scenario how can i create a policy so that i can enforce multiple rules for each sub resources like:
Rule-1:
Admin Permit access to resource-
{name: create,read,update,delete},
{email: create,read,update,delete}
Rule-2:
Employee Permit access to resource-
{name: read,update},
{email: read}
Please share the policy structure and the Request format for the same.
In the request format i want to pass only the Tenant Id and the Root level resource Customer .
In this scenario, what you would want to do is pass in the field id you are interested in.
The request would be: "Can Alice view the name field of customer record #123"?
You could express this as a multiple decision request e.g.:
"Can Alice view the name, email, and job title fields of customer record #123"?
Either way your policy would be field-centric. It would protect a given field or set of fields. You could actually define a set of non-sensitive fields and a set of sensitive fields. You could also even write the policy in terms of field metadata. Instead of saying "a user can view field 'email'", you could write "a user can view a field if the user's clearance > field's sensitivity".
Alternatively, you could also use Reverse Query - that's specific to Axiomatics' APIs though. Reverse Query lets you do the following type of requests / responses:
Q: list the fields Alice can view
A: name, email

Accessing role ID through module

Im quite stuck here. In my cwebuser I've already defined my roles. My logins to my modules are restricted by roles, which is great! But my problem is restricting the modules to specific users within the roles. In webuser isShop is defined as a certain user id (user_role_id) in database to see if the user is user or shop. The issue is shop module can be seen by all roles who are isShop. My question is is there a way to authorize so that shop module gets user's id and shop id?
Something that mimics yii::app()->user->user_id;
like yii::app()->getmodule(shop)->shop_id;
Or must this be defined in model through criteria by shop_id? Doesn't sound right though, doing it this way.
I think if you are using the following function in model, you can apply the SHOP relations in here
public function defaultScope() {
if(isset(yii::app()->user->user_id)) return array('condition'=>'');
// here you can apply your conditions with the relation feilds
}

How to build a data model for an access control list (ACL)

It's fairly obvious how to model a database table that would act as an access control list (ACL) when you're just dealing with discrete users who have some level of access to a discrete resource. Something like this:
TABLE acl (
user_id INT,
resource_id INT,
access_type INT
)
... where access_type is a number representing something like:
0 (or lack of record for user_id and resource_id) means no access
1 means read-only
2 means full control
However it starts getting trickier when you've got scenarios like users can be a member of one or more groups and groups can contain other groups. Then a resource could be a folder that contains other resources.
Other than the obviously poor approach of doing a whole bunch of recursive queries at runtime to determine the level of access a user should have to a resource, how do these scenarios tend to get handled? Are there commonly-accepted designs for modelling an ACL like this?
Are you using a DB with support for connect by, or something similar?
In oracle, I've implemented the following.
Table Group //Just the parent groups
{
groupCode varchar
groupDesc
}
Table groupMap //associates groups with other groups
{
parentGroup
childGroup
}
table userGroup //can assign user to more than one group
{
userId
groupCode
}
then use connect by to get all child groups for user
SELECT rm.CHILDGroup as roleCode
FROM groupMap rm
CONNECT BY PRIOR rm.CHILDGroup = rm.PARENTGroup
START WITH rm.CHILDGroup in
(SELECT ur.groupCode
FROM userGroup ur
WHERE ur.userId = &userId);
This query will get all the groups that were assigned to the user in userGroup and all the child groups assigned to the groups that the user belongs to.
Spring ACL is a solid implementation of ACL with inheritance for java. It is open source so I would check it out if it is what you are looking for.