Hasura object permission based authorization - hasura

I am trying to set a "Row Select" permissions on Hasura. I have a (simplified for brevity) Data Model like below
User
id: UserID
App
id: AppID
App Permissions
user_id: User ID
app_id: App ID
permissions: [ ENUM: Admin, View, Owner ]
Feed
app_id: AppID
feed_data: Some Feed Data
Now, I wish to query all Feed for an authenticated user. The query can be of the form
GET all apps, for which the authenticated user has view permissions
query MyQuery {
feed(limit: 10) {
app_id
feed_data
}
}
GET apps with app_id in the query filter for which the authenticated user has view permissions
query MyQuery {
feed(limit: 10, where: {app_id: {_in: [1, 2]}}) {
app_id
feed_data
}
}
Since feed table does not have user_id information directly in it, I can not use X-Hasura-User-Id attribute directly against feed table. I also tried to use _exists relation against the app_permission table, but I am unable to put app_id filter in the permission clause.
{
"_exists": {
"_where": {
"user_id": {
"_eq": "X-Hasura-User-Id"
}
},
"_table": {
"schema": "public",
"name": "app_permission"
}
}
}
I am not really sure how to proceed with such data modelling with Hasura. Any help is appreciated. Thanks.

Since you dont have a direct relationship, I think you can query via appPermissions Table instead of directly querying feeds table.
When you create a feeds table with appId as foreign key relationship, Hasura lets you track this relationship as shown below
This way you can make nested graphQL queries to appPerms table as shown below
query GetUserFeeds {
test_appPerms {
id
userId
feeds(limit: 10) {
app_id
id
feed_data
}
}
}

Another thing I'd like to suggest is that you could try is by using a session variable like x-hasura-app-id along side a x-hasura-role and build your permissions around that.
https://hasura.io/docs/latest/graphql/core/auth/authorization/roles-variables.html

Related

How to create a friends relationship in Hasura

I have 2 tables: users and friends.
friends has a user_id foreign key which obviously maps to the users id key.
friends also has a friend_id key which maps to the users id key as well.
I am using JWT Auth. I have successfully set the permission on the users table so that when the user queries for users the user pulls only that specific user's column. I have also created an array relationship for friends on the users table so user rows can have friends via the relationship.
However, when querying something like:
{
user {
username
friends {
id
username
}
}
}
This returns null for friends, because the user role does not have access to friends (due to the session variable X-Hasura-User-Id used to determine user by session. How do I reconcile having the session variable work but also be able to query other friends?
Put this permission on friends, and then users can see all friend rows where the friend_id is their user.id:
{
"friend_id" :{
"_eq" : "X-Hasura-User-Id"
}
}
Permissions for relationships are inherited from the relationship table. So you just need to ensure that the user can regularly access the related table rows by themselves, and if you can do that it will apply to the relationships as well.
Unrelated, it looks like these tables have the same columns. You might want to use a self-referential relationship from users->users as friends if they're identical. If not ignore this =)
Edit:
Try using an _exists permission:
(You can probably simplify this using the relationships direct access to friends and user but I don't know your exact table and relationship structure)
"If there exists in table friends, a row where the 'friend_id' is 'X-Hasura-User-Id' and the 'user_id' is equal to this row's user ID"
{
"_or": [
{
"_exists": {
"_table": { "schema": "public", "name": "friends" },
"_where": {
"_and": [
{ "friend_id": { "_eq": "X-Hasura-User-Id" } },
{ "user_id": { "_ceq": "user_id" } }
]
}
}
},
{ "id": { "_eq": "X-Hasura-User-Id" } }
]
}

Firebase REST API query with different keys

So this is the structure of my Firebase DB right now, I am using the Firebase REST API:
"company": {
company1_id {
id: company_id,
userId: userid,
name: name
//someotherstuff
}
company2_id {
id: company_id,
userId: userid,
name: name,
//someotherstuff
}
}
Soo, right now I am getting the companies belonging to one user by calling :
"firebasedbname.firebaseio.com/company.json?orderBy="userId"&equalTo=userId"
This works perfectly fine and gets the corresponding data, but now I want it to order the companies alphabetically by name, and then i try this:
"firebasedbname.firebaseio.com/company.json?orderBy="name"&equalTo=userId"
But this time, it returns no data! Even though i have added .indexOn: "name" to the company node.Any help will be aprreciated.
As explained in the doc, if you want to filter data you need to first "specify how you want your data to be filtered using the orderBy parameter", and then you need to "combine orderBy with any of the other five parameters: limitToFirst, limitToLast, startAt, endAt, and equalTo".
So if you added "an .indexOn: "name" to the company node", it means that you intend to query as follows:
https://xxxx.firebaseio.com/company.json?orderBy="name"&equalTo="companyName"
You cannot order by (company) name and filter on userId.
If you want to get all the companies corresponding to a specific user and order them by the company name, you will need to use ?orderBy="userId"&equalTo=userId" and do the sorting in the client/application calling the REST API.

How can I get other users' profiles details in meteor

I have got problem accessing the user profile details of the users other then the current user.
The goal is to display a little footer under a each of the posts in the kind of blog entries list . Footer should consist of the post and author details (like date, username etc.).
Blog entry is identified by authors' _id but the point is that I can not access
Meteor.users.find({_id : authorId});
Resulting cursor seems to be the same as Meteor.user (not 'users') and consists of one only document, and is valid for the current user ID only. For others, like authors ID, I can only get an empty collection.
The question is, if is there any way, other then next Meteor.users subscription to get authors profile (like username profile.nick etc) ???
Update: You can Publish Composite package if you want to get blog entry and user details in a single subscription. See the following sample code and edit as per your collection schemas,
Meteor.publishComposite('blogEntries', function (blogEntryIds) {
return [{
find: function() {
return BlogEntries.find({ courseId: { $in: blogEntryIds }});
// you can also do -> return BlogEntries.find();
// or -> return BlogEntries.find({ courseId: blogEntryId });
},
children: [{
find: function(blogEntry) {
return Meteor.users.find({
id: blogEntry.authorId
}, {
fields: {
"profile": 1,
"emails": 1
}
});
}
}}
}]
});
End of update
You need to publish Meteor.users from the server to be able to use it on client. accounts package will publish current user, that's why you are only seeing current user's information.
In a file in server folder or in Meteor.isServer if block do something like this
//authorIds = ["authorId1", "authorId2];
Meteor.publish('authors', function (authorIds) {
return Meteor.users.find({ _id : { $in: authorIds }});
});
or
Meteor.publish('author', function (authorId) {
return Meteor.users.find({ _id : authorId });
});
Then on client side subscribe to this publication, in template's onCreated function, with something like this
Meteor.subscribe('author', authorId); //or Meteor.subscribe('author', authorIds);
or
template.subscribe('author', authorId); //or template.subscribe('author', authorIds);
If you want to show only username (or a few other fields), you can save them in post document along with authorId. For example:
post:{
...
authorId: someValue,
authorName: someValue
}
You can use them in your templates as a field of a post.
If you have too many fields which you do not want to embed in post document, (so you want to keep only authorId), you can use publish-composite when you make your posts publication. (See example 1)
You do not need to publish all your users and their profiles.

how to retrieve data ordered by key inside unspecified key with firebase

I have a snapshot for my reference in firebase like this:
"friendlist" : {
"user1" : {
"user3" : 1
},
"user2" : {
"user1" : 0
}
"user3" : {
"user1" : 1
}
}
The explanation for the reference:
Every user has an unique id, i'm using user's id for their friendlist unique id. In example above i have 3 users and every user have his own friendlist. Inside their friendlist, there's other user's id that already be friend with him. If the value is 1, the user already be friend. But when the value is 0, the user is requesting to be friend.
My problem is:
How to get all user's friendlist's id which have "user1" with value 0 inside their friendlist? Can i do that in just one query?
I think i need to iterate through all friendlist and orderbykey for every friendlist and looking for "user1". Or there's any good approach to do that?
Any answer would be appreciated, thanks!
It would help if you next time tell a bit more about what you've already tried. Or at the very least specify what language/environment you're targeting.
But in JavaScript, you can get those users with:
var ref = new Firebase('https://yours.firebaseio.com/friendlist');
var query = ref.orderByChild('user1').equalTo(0);
query.once('value', function(usersSnapshot) {
usersSnapshot.forEach(function(userSnapshot) {
console.log(userSnapshot.key());
});
});
With the sample data you specified, this will print:
user2
You should add (and will get a warning about) an index for efficiently performing this query:
{
"rules": {
"friendlist": {
".indexOn": ['user1']
}
}
}
Without this index, the Firebase client will just download all data to the client and do the filtering client-side. With the index, the query will be performed server-side.
A better data model
You'll likely want to search for any friend, which turns the index into:
".indexOn": ['user1', 'user2', 'user3']
But with this structure, you'll need to add an index whenever you add a user. Firebase SDKs don't have an API to add indexes, which is typically a good indication that your data structure is not fitting your needs.
When using a NoSQL database, your data structure should meet the needs of the application you're building. Since you are looking to query the friends of user1, you should store the data in that format too:
"friendlist" : {
"user1" : {
"user3" : 1
},
"user2" : {
"user1" : 0
}
"user3" : {
"user1" : 1
}
},
"friendsOf": {
"user1": {
"user2": 0,
"user3": 1
},
"user3": {
"user1": 1
}
}
As you can see, we now store two lists:
* friendList is your original list
* friendsOf is the inverse of your original list
When you need to know who friended user 1, you can now read that data with:
ref.child('friendsOf').child('user1').on('value'...
Note that we no longer need a query for this, which makes the operation a lot more scalable on the database side.
Atomic updates
With this new data model, you need to write data in two places when adding a friend relation. You can do this with two set()/update() operations. But in recent Firebase SDKs, you can also perform both writes in a single update like this:
function setRelationship(user1, user2, value) {
var updates = {};
updates['friendList/'+user1+'/'+user2] = value;
updates['friendsOf/'+user2+'/'+user1] = value;
ref.update(updates);
}
setRelationship('user3', 'user4', 1);
The above will send a single command to the Firebase server to write the relationship to both friendList and friendsOf nodes.

Angularfire generate new unique ID for non-email object

I'm making a web app using angularfire. I have a url for users at 'url.firebaseio.com/users'. I want to make another url to store chat-room type things at 'url.firebaseio.com/rooms'. When I create a new user using the $createUser() method, it stores the user's information under a unique ID created by $createUser() in the '/users' url. I want to do this with the new '/rooms' url, but I can't find a way to generate unique IDs in the same way without $createUser(). I can't use $createUser() because it requires an email address argument, and I just want to take in a name for the room and a password, all in an object under the unique ID for the room.
I can't think of any code to provide, so here's what a user looks like:
users: {
uniqueUserId: {
email: email#email.com,
name: name
}
}
And here's what I'd like a 'room' to look like:
rooms: {
uniqueRoomId: {
roomName: something
}
}
Is there a built-in way to do this? If not would it be best to generate IDs on my own?

Resources