Joining entities with #ManyToMany relationship - jpa

I have these entities:
User
Role
Permission
A user has many roles and a role has many permissions.
What is the best way to retrieve a set of permission a user has?
I need a method to check if a User has a particular Permission.
This is what I have so far:
public boolean hasPermission(String permissionString) {
if (!authenticated) return false;
for (Role role : user.getRoles()) {
for (Permission permission : role.getPermissions()) {
if (permission.getName().equals(permissionString)) {
return true;
}
}
}
return false;
}
A second, but related question -- where should I put the code that checks if a user has a particular permission?
In the User entity?
In the UserBean EJB?
In the Authentication JSF Managed Bean?

It depends on your mappings, the number of objects in the list, if the lists have already been fetched, your database connections, the database tuning etc.
You would have to try with production data to determine what ways are best.
For instance, If your collections have been prefetched with a join query, then traversing them in Java is trivial. If they haven't, each access in the for loop would cause a query to populate the objects. If it is the last one all the time, it means your java code causes you to traverse your object graph in the worst way possible and it would have been better to fetch it upfront. So you would be losing any benefit of lazy access, and would be better of hitting the database once to query for the permission linked to this user with the permissionString name: "Select p from u User join u.roles r join r.permissions p where p.name = :permissionName".
Only testing on production data will give you the best answer for your situation, and numerous other decisions in the application and mappings change the outcome.

Related

Is there a way to setup a field-level authorisation on FaunaDB + GraphQL?

I'm having troubles finding a way to hide user emails from everyone, except the owner (user has access to only his email). Is there a way to hide a certain document field, for a certain roles?
Here is an example I found that creates a role with dynamic access to the whole User collection:
CreateRole({
name: "tier1_role",
membership: {
resource: Collection("User"),
predicate: Query(
Lambda("userRef",
// User attribute based rule:
// It grants access only if the User has TIER1 role.
// If so, further rules specified in the privileges
// section are applied next.
Equals(Select(["data", "role"], Get(Var("userRef"))), "TIER1")
)
)
},
privileges: [
{
// Note: 'allUsers' Index is used to retrieve the
// documents from the File collection. Therefore,
// read access to the Index is required here as well.
resource: Index("allUsers"),
actions: { read: true }
}
]
})
I tried to change it a bit, but I wasn't able to set up field-level access.
Let's say I'd set up FaunaDB with GraphQL schema below.
enum UserRole {
TIER1
}
type User {
email: String! #unique
username: String! #unique
role: UserRole!
}
type Query {
allUsers: [User!]
}
type Mutation {
addUsers(new_users: [UserInput]): [User]
#resolver(name: "add_users", paginated: false)
}
How do create a FaunaDB role in such a way that all of the users (except the current one) in resulting array from allUsers query, will not have email field?
I could break User collection into two: one is public, the other is accessible to a document owner, but this sounds wrong.
I'm new to the noSQL concept, so maybe I'm looking at this problem from the wrong perspective?
it's a request that came up a few times. You probably want to do this straight in FaunaDB's ABAC role system but although it provides row-level security, hiding a specific field is currently not provided yet. The feedback has been logged though, we will look into it.
The current way to do this is to split out Users from Accounts and fetch Users instead of Accounts. It would be useful to have something like hidden fields though in the future.
If you think of it, in this case, it does make sense to split authentication information from User information. You never know that you might offer another way to authentication in the future. I still recall from the Phoenix Framework book that they do it there was well and considered it a good practice.
You could also make a thin wrapper using Apollo in a serverless function and filter out these fields when you pass through the results. There is a guide that explains how to build such a thin Apollo middleware that just delegates to FaunaDB https://www.gatlin.io/blog/post/social-login-with-faunadb-and-auth0

Zend Navigation Multiple ACL roles

I am trying to create an ACL where users may have different roles in different departments.
The user is given a role in the form of role::guest or role::user depending if they are logged in. This is their userRole. (There is also a role::superuser that has access to all departments).
I have also added departmental roles to the ACL in the form of department::role (Eg. bookings::user). This is their departmentRole.
The users departmental roles are stored in the Zend_Auth identity.
The access control part works by extending Zend_Acl and over-riding the isAllowed function.
This successfully allows or denys each user.
public function isAllowed($role = null, $resource = null, $privilege = null)
{
$identity = Zend_Auth::getInstance()->getIdentity();
$userRole = $identity->role;
$departmentRoles = $identity->departmentRoles;
if (parent::isAllowed($userRole, $resource, $privilege))
{
return parent::isAllowed($userRole, $resource, $privilege);
}
else {
foreach ($departmentRoles as $departmentRole)
{
if(parent::isAllowed($departmentRole, $resource, $privilege))
{
return true;
}
}
}
return false;
}
The problem I am having is that Zend_Navigation requires an instance of the Acl and a single user role. My view script which builds the navigation menu uses $this->navigation()->accept($page) which only validates against the single user role.
How can I have multiple Acl roles for each user and have Zend_Navigation display menu items that they have access to?
If there is a better / different / correct approach to this please share.
Thanks
EDIT:
The fact that this approach meant over riding a core function in isAllowed() got me thinking this can't be the correct way to do this.
Now, in my ACL model I fetch all users, departments and associations and loop through creating an array for each user made up of their various roles within their relevant departments. I then create one role for each user and inherit the roles in the array previously created.
This is working well up to now and also means I can also add the users as resources and allow the relevant admin and department managers rights to amend their details etc.
It also means that I can pass a single role to Zend_Navigation and the menu structure should be relevant to their department roles.
IMHO having multiple ACL roles for single user looks like anti-pattern. Zend_Navigation rules are binded to (multiple) resources for single role which makes perferct sense.
What are your constraints that forbids you to allow resources for your (department) roles?
You can always use inheritance for your ACL roles.
If you prefer having multiple roles for single user, you might need to have separate ACL rules.
Zend_View_Helper_Navigation_HelperAbstract::setDefaultAcl($acl);
Zend_View_Helper_Navigation_HelperAbstract::setDefaultRole($role);

JPA - Join Two tables

I have two tables, namely
USER_ROLE {user_id, Role} PK {user_id, role}
ROLE_PERMISSION {role, permission} PK {role, permission}
A User can have multiple Roles.
A Role can be mapped to multiple
Permissions.
I have a entity - USER that maintains information about the User. This info is fetched via LDAP (not DB) on first login. Now, for my authorization aspects, I need to also fetch dtls on User's permissions from above mentioned tables.
So I would imagine adding attributes to my existing USER entity
USER {
user_id,
first_name,
last_name,
etc
// Authorization
List<String> roles;
List<String> permissions;
}
Can someone pls help how I can use JPA to populate the roles and permissions Lists? Looked over internet, can't figure it out. thanks
I would create a USER table in your database and map it to a User object with the role and permissions. The User object then would include additional LDAP data.
Without a USER table you have nothing to map to.
Otherwise just query for the database using native SQL queries and populate your LDAP user object yourself.

Using of bulk upate via Query.executeUpdate() vs. object's set methods for multiple entities

Sometime, in he case when we have a list of entity IDs, I have to update a set of fields of a collection of related of passed IDs entities and I am wandering in which cases is better to use the standard way via 1-st: loading of all entities and 2-nd: calling related set() methods :
List<Long> ids = ....;
String par = "example";
List<User> users = USER_DAO.getUsers(ids);
for(User user : users) {
user.setField(par);
}
or the other way is via executing of a bulk update operations like :
Query query = em.createQuery("UPDATE User user SET user.field =:1? WHERE user.id IN : 2?");
int rowCount = query.executeUpdate();
Do you have some knowledge and/or investigations of that issue ?
Or the cases, when first way is better than the second way or vs. ?
All recommendations are welcome,
Thanks,
SImeon
The second way will perform better as there will be less sql's executed on the database. In your first case two sql's will be executed for each user(one for the select and one for the update). In your first case you will also take a performance hit while hibernate maps the sql to the object.

Entity Framework code first aspnet_Users mapping / joins

I was wondering with Entity Framework 4.1 code first how do you guys handle queries that involve an existing aspnet_Users table?
Basically I have a requirement for a query that involves the aspnet_Users so that I can return the username:
SELECT t.Prop1, u.Username
FROM Table1 t
INNER JOIN aspnet_User u ON t.UserId = u.UserId
Where t.Prop2 = true
Ideally in linq I would like:
from t in context.Table1
join u in context.aspnet_Users on t.UserId equals u.UserId
where t.Prop2 = true
But I'm not sure how to get aspnet_Users mapping to a class User? how do I make aspnet_Users part of my dbset ?
Any help would be appreciated, thanks in advance
Don't map aspnet_Users table or any other table related to aspnet. These tables have their own data access and their own logic for accessing. Mapping these tables will introduce code duplication, possible problems and breaks separation of concerns. If you need users for queries, create view with only needed information like id, user name, email and map the view. The point is that view will be read only, it will contain only allowed data and your application will not accidentally modify these data without using ASP.NET API.
First read Ladislav's answer. If you still want to go ahead : to do what you want would involve mapping the users and roles and members tables into the codefirst domain - which means writing a membership provider in code-first.
Luckily there is a project for that http://codefirstmembership.codeplex.com/ although its not a perfect implementation. The original is VB, look in the Discussion tab for my work on getting it running in c# MVC.
I'm working with the author on a better implementation that protects the membership data (password, last logged on date, all of the non-allowed data) but allow you to map and extend the user table. But its not ready yet!
You don't really need to use Entity Framework to access aspnet_membership provider accounts. You really just need to create an instance of the membership object, pass in a unique user identifier and a Boolean value indicating whether to update the LastActivityDate value for the user and the method returns a MembershipUser object populated with current values from the data source for the specified user.
You can then access the username by using the property of "Username".
Example:
private MembershipUser user =
Membership.GetUser(7578ec40-9e91-4458-b3d6-0a69dee82c6e, True);
Response.Write(user.UserName);
In case you have additional questions about MembershipProvider, you can read up on it on the MSDN website under the title of "Managing Users by Using Membership".
Hope this helps you some with your requirement.