I'm playing with Firebase Realtime Database and after a while I start wondering if there are best practice to structure the database for privacy.
I mean, I see best practice for performance like database fan-out
Map updatedUser = new HashMap();
newPost.put("name", "Shannon");
newPost.put("username": "shannonrules");
Firebase ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
Map fanoutObject = new HashMap();
fanoutObject.put("/users/1", updatedUser);
fanoutObject.put("/usersWhoAreCool/1", updatedUser);
fanoutObject.put("/usersToGiveFreeStuffTo/1", updatedUser);
ref.updateChildren(fanoutObject); // atomic updating goodness
But I did found nothing about privacy polices.
I know there are Database ACL that I can use to, for example, restrict access to users not authenticate or users there are not the "owner" of a particular node... but for those nodes that are readable someone could be, if he would, access the entire children of those nodes.
Suggestions?
EDIT: Database rules are not descendant so if I let users read a node they alway can read all nodes below:
{
"rules": {
"foo": {
// allows read to /foo/*
".read": "data.child('baz').val() === true",
"bar": {
/* ignored, since read was allowed already */
".read": false
}
}
}
}
You can secure your database using Firebase Realtime Database Rules.
Firebase Realtime Database Rules determine who has read and write access to your database, how your data is structured, and what indexes exist. These rules live on the Firebase servers and are enforced automatically at all times. Every read and write request will only be completed if your rules allow it. By default, your rules are set to allow only authenticated users full read and write access to your database. This is to protect your database from abuse until you have time to customize your rules or set up authentication.
All your requirements can be met using security rules.
If you need ACL style security, take a look at Custom Auth Claims - using Cloud Functions you can add your own properties to a user's JWT auth token, e.g. to say which groups they belong to or which products they have purchased. Then your security rules can look at those properties upon the user and decide if they can access a particular node.
Related
I have the following data structure in Firebase Firestore to represent a many to many relationship between clients and users:
Clients
clientId1 {
users (object): {
userId1: true
userId2: true
}
}
clientId2 {
users (object): {
userId1: true
}
}
I query it on Android using the following query:
db.collection("clients").whereEqualTo("users."+uid, true);
For userId2, the query should only return clientId1.
If I set the rule to (allow read: if true;) when I execute the query above I get the correct clients returned.
I would also like to set up a database rule to prevent userId2 from seeing clientId2.
I tried this rule but I get no results returned:
match /clients/{clientId} {
//Allow read if the user exists in the user collection for this client
allow read: if users[request.auth.uid] == true;
}
I also tried:
match /clients/{clientId} {
//Allow read if the user exists in the user collection for this client
allow read: if resource.data.users[request.auth.uid] == true;
}
But neither of the above rules returns any clients.
How do I write the rule?
I am going to answer my own question as I was just doing something silly.
My data structure is fine and the correct syntax for my rule is this one:
match /clients/{clientId} {
//Allow read if the user exists in the user collection for this client
allow read: if resource.data.users[request.auth.uid] == true;
}
Given this:
Cloud Firestore evaluates a query against its potential result set
instead of the actual field values for all of your documents. If a
query could potentially return documents that the client does not have
permission to read, the entire request fails.
This Android query does correctly implement the right filter for the rule:
db.collection("clients").whereEqualTo("users."+uid, true);
I am yet to implement my adapter properly. I wanted to see if I could get the correct data structure / rules / query working first. I was calling it from another listener that was listening on the entire client collection (which fails the rule) and therefore this query was not being called. Earlier when I set the rule to (allow read: if true;) the initial listener was executing my query and returning the correct results. This lead me to believe my rule was incorrect, when it wasn't.
As per the official documentation regarding Firestore Security Rules:
When writing queries to retrieve documents, keep in mind that security rules are not filters—queries are all or nothing. To save you time and resources, Cloud Firestore evaluates a query against its potential result set instead of the actual field values for all of your documents. If a query could potentially return documents that the client does not have permission to read, the entire request fails.
So you cannot filter the documents that exist in your database using security rules.
In the firestore security settings you can set conditions for writing/reading data.
Currently I have this piece:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth != null && request.time <
resource.data.timeCreated + duration.value(1, 'h');
allow write: if request.auth != null;
}
}
}
Now I want to limit the writing; a user should only be able to send data every 5 minutes. How can I achieve this?
There is not a native way to do this.
You cannot for non-signed in users in any way that is not trivially bypassed. You could use Cloud Functions to achieve this for signed in users though.
Each user has a profile with next time they can write, along with the id of the document to write next.
Use Rules on writes to check that the id doesn't already exist and it's >= the allowed time
Use Cloud Functions on write to update the user profile document with a new allowed time and unique id for the next write
I configured a JPA store and see users and roles getting added correctly to the db when I call the related picketlink (2.7.1) API's
My questions is this: how does one get a list of all users that have a given role?
I tried doing this using the following RelationshipQuery
RelationshipQuery<Grant> rq = relationshipManager.createRelationshipQuery(Grant.class);
rq.setParameter(Grant.ROLE, role);
List<Grant> grants = rq.getResultList()
But the resulting grant list contains a single assignment grant, that refers to the last user in the database that has that role.
I checked the example queries in the documentation and tests but found nothing that does what I want. I know the project is no longer active but am hoping to find a solution to this.
Found out that role data wasn't imported correctly from the old db. Once I fixed that the above code worked as expected.
I would like to know if someone have tried to implement a spring acl feature, but instead of using a Database, use a simple properties file. for example :
myobject.input.field1=ACL_READ
myobject.input.field2=ACL_READ,ACL_WRITE
and in the jsp :
<sec:accesscontrollist hasPermission="ACL_READ" domainObject="myobject.input.field1">
This will be shown if the user has either of the permissions
represented by the values "1" or "2" on the given object.
<input type="text" name="field1" />
</sec:accesscontrollist>
c.f. http://static.springsource.org/spring-security/site/docs/3.1.x/reference/taglibs.html#d0e6991
Firstly, your approach
myobject.input.field1=ACL_READ
myobject.input.field2=ACL_READ,ACL_WRITE
does not contain any information about SIDs (Security IDs) that are granted specified permissions. SID is any subject that can be granted permission. For instance, user of your application or group of users.
Who is granted that ACL_READ permission for myobject.input.field1 object? I guess you forgot about it.
Secondly, holding such a proprties file would be simply implementing your own database system. You'd better use existing software for that.
If you don't want to use full-blown RDBMS like PostgreSQL or Oracle, you can use file based relational databases.
Existing and tested software:
SQLite
HSQLDB
I'm new to Liferay development in general, so feel free to point out if I'm going about stuff totally the wrong way.
I'm trying to get a DynamicQuery object of all users within a certain group (I'll use this object to further filter another query I'll do against the message board). The User interface seems to have a roleIds property that I might be able to use, since I already know the roleId I'm interested in. But I can't find the proper way to query if roleIds contains a certain value.
Any ideas on what I want to do?
PS: I would have the exact SQL query I could ask directly, but I'd rather use Liferay's own connection pool, without needing to do some weird ext project thingy.
You don't need a DynamicQuery. These are the methods you are looking for in the classes that Dirk points out:
long[] UserServiceUtil.getRoleUserIds(long roleId)
or
long[] UserLocalServiceUtil.getRoleUserIds(long roleId)
List<User> UserLocalServiceUtil.getRoleUsers(long roleId)
Remember that the methods in the classes XXXLocalServiceUtil are not checking the permissions of the current user.
EDIT: If you are looking for all users with a given role within a given community:
long companyId= _X_; //Perhaps CompanyThreadLocal.getCompanyId() if you don't have it anywhere else?
Role role=RoleLocalServiceUtil.getRole(companyId, "Example Role");
Group group=GroupLocalServiceUtil.getGroup(companyId, "Example Community");
List<UserGroupRole> userGroupRoles = UserGroupRoleLocalServiceUtil.
getUserGroupRolesByGroupAndRole(groupId, role.getRoleId());
for(UserGroupRole userGroupRole:userGroupRoles){
User oneUser=userGroupRole.getUser();
}
The easiest way to access liferays own objects is by using the XXXServiceUtil classes (e.g. RoleServiceUtil.getUserRoles(userId)). Thus you rarely have to deal with any SQL directly. Either the RoleServiceUtil or UserServiceUtil might have what you need.
The roles of an Organizations are stored in the table UserGroupRole, so if you want to get the owner of an Organization you must use the following code:
boolean isOrgOwner =
UserGroupRoleLocalServiceUtil.hasUserGroupRole(
usr.getUserId(),
this.currentOrganization.getGroupId(),
RoleConstants.ORGANIZATION_OWNER);
If you want to retrieve all the Organization Owners of an organization:
List<User> administrators = new LinkedList<>();
List<UserGroupRole> allOrganizationAdministrators =
UserGroupRoleLocalServiceUtil.getUserGroupRolesByGroupAndRole(
this.currentOrganization.getGroupId(), roleId);
for (UserGroupRole userGroupRoleTemp : allOrganizationAdministrators) {
administrators.add(userGroupRoleTemp.getUser());
}
Cheers!