Java – efficient, database-aware instance-level authorization? - java

In a JPA app I have a scenario in which the app is to
list all accounts the given user is authorized to withdraw from
I have the Account entity and a many-to-many table that lists what authorizations each user has on each account – to implement the above scenario, the app currently just inner-joins the two tables – which is quite quick.
Now, I was planning to add an explicit authorization layer (based on apache shiro / spring security / other) to insulate authorization-related logic from the rest of the code, but...
There are some 10k Accounts in the database and the "average" user is granted "deposit" on all of them, "view" on one half of them and "withraw" on just a few.
Does any security framework allow to implement this scenario efficiently?
Ie: is any of them able to "decorate" a JPA query of the type "select a from Account a" (or the equivalent SQL) and thus get the list of accounts without loading all user grants from the database, and by all means, without having to retrieve all accounts?)

Have a look at Apache Shiro.
It allows you to pull in the User authorization once and cache it for the duration of the session. In addition, if all users can VIEW all ACCOUNTS then you wouldn't need to explicitly define this which would significantly reduce the overhead.
If your solution requires realtime access handlers Shiro has a way to reset the Permissions dynamically during runtime too.
Shiro allows you to implement a typical RBAC and define permissions like this:
domain:action:instance
So in your case permissions might look like this for a user:
account:deposit:* // deposit all accounts
account:view:1111
account:view:2222
account:view:3333 // view on these accounts
account:withdraw:5555
account:withdraw:6666 // withdraw on these accounts
In code you can then do something like this:
if (SecurityUtils.getSubject().isPermitted("account:withdraw:"+account.getAccountNumber() ) {
// handle withdraw
}
Shiro also has annotation driven permissions for additional abstraction.
EDIT
The Shiro permissions is the end result, not where you start. I used a set of tables representing mappings of the user-to-role and role-to-permission along with other mappings to instance. After AuthN its usually a simple set of queries indexed by the User PK to build up the data structures needed to render the permissions.

I have a hope that this is one of the possibilities to implement your requirement with Spring-Security.
Write custom org.springframework.security.acls.Permission like
ViewAccount,DepositToAccount,WithDrawFromAccount
Write custom
org.springframework.security.access.PermissionEvaluator Override
hasPermission(Authentication userAuthentication,Object
accountObject,Object oneOfThePermission) to check if the user has
the defined permission on the accountObject
Get reference to JPA
EntityManager in your custom evaluator and cross check/verify in DB
with user_id,permission_id,account_id
If the user is 'root' you can
staight away return true for hasPermission without verifying with
DB.
Annotate your service calls with
#PreAuthorize("isAuthenticated() and hasPermission(#accountArgument,
'respectivePermission')")
Refer link for custom implementations of Permission & PermissionEvaluator

If you are using EclipseLink there are a few features for this,
one is the #AdditionalCriteria annotation that allow a filter to be applied to all queries for a class,
http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_additionalcriteria.htm#additionalcriteria
another is EclipseLink's support for Oracle VPD (row level security in the database),
http://wiki.eclipse.org/EclipseLink/Examples/JPA/Auditing
and finally EclipseLink supports SessionEvents that can allow filter to be appended to any query execution,
http://www.eclipse.org/eclipselink/api/2.4/org/eclipse/persistence/sessions/SessionEventAdapter.html#preExecuteQuery%28org.eclipse.persistence.sessions.SessionEvent%29

Related

Restrict URL access control by id in jhipster

I have a problem that each user can retrieve other users data from URL.
For instance, I have a rest api like this:
#GetMapping("/getFindByPersonId/{perId}")
#Timed
public List<ComboVahedAmoozeshi> getFindBySkhsIdCombo(#PathVariable Long perId){
return comboVahedAmoozeshiRepository.getFindBySkhsIdCombo(perId);
}
After authorization, each user can change id and get other users data like the image below:
Is there any suggestion to restrict each user to don`t have access to call the method? Or Jhipster have any options to use UUId to hide id?
Thanks from #atomferede for the right answer. I have to add jhi_user_id in other entities and used #postfilter annotation to limit user's access to data.
Although, maybe it`s a good idea to have this option in jhipster generator to enhance the security level and faster implementation.

Create users in Oracle, MySQL databases using Springboot - Spring Data JPA

I am very new to Springboot and Spring Data JPA and working on a use case where I am required to create users in different databases.
The application will receive 2 inputs from a queue - username and database name.
Using this I have to provision the given user in the given database.
I am unable to understand the project architecture.
Since the query I need to run will be of the format - create user ABC identified by password;
How should the project look like in terms of model class, repositories etc? Since I do not have an actual table against which the query will be run, do I need a model class since there will be no column mappings happening as such.
TLDR - Help in architecturing Springboot-Spring Data JPA application configured with multiple data sources to run queries of the format : create user identified by password
I have been using this GitHub repo for reference - https://github.com/jahe/spring-boot-multiple-datasources/blob/master/src/main/java/com/foobar
I'll be making some assumptions here:
your database of choice is Oracle, based on provided syntax: create user ABC identified by password
you want to create and list users
your databases are well-known and defined in JNDI
I can't just provide code unfortunately as setting it up would take me some work, but I can give you the gist of it.
Method 1: using JPA
first, create a User entity and a corresponding UserRepository. Bind the entity to the all_users table. The primary key will probably be either the USERNAME or the USER_ID column... but it doesn't really matter as you won't be doing any insert into that table.
to create and a user, add a dedicated method to your own UserRepository specifying the user creation query within a #NativeQuery annotation. It should work out-of-the-box.
to list users you shouldn't need to do anything, as your entity at this point is already bound to the correct table. Just call the appropriate (and already existing) method in your repository.
The above in theory covers the creation and listing of users in a given database using JPA.
If you have a limited number of databases (and therefore a limited number of well-known JNDI datasources) at this point you can proceed as shown in the GitHub example you referenced, by providing different #Configuration classes for each different DataSource, each with the related (identical) repository living in a separate package.
You will of course have to add some logic that will allow you to appropriately select the JpaRepository to use for the operations.
This will lead to some code duplication and works well only if the needs remain very simple over time. That is: it works if all your "microservice" will ever have to do is this create/list (and maybe delete) of users and the number of datasources remains small over time, as each new datasource will require you to add new classes, recompile and redeploy the microservice.
Alternatively, try with the approach proposed here:
https://www.endpoint.com/blog/2016/11/16/connect-multiple-jpa-repositories-using
Personally however I would throw JPA out of the window completely as it's anything but easy to dynamically configure arbitrary DataSource objects and reconfigure the repositories to work each time against a different DataSource and the above solution will force you to constant maintenance over such a simple application.
What I would do would be sticking with NamedParameterJdbcTemplate initialising it by using JndiTemplate. Example:
void createUser(String username, String password, String database) {
DataSource ds = (new JndiTemplate()).lookup(database);
NamedParameterJdbcTemplate npjt = new NamedParameterJdbcTemplate();
Map<String, Object> params = new HashMap<>();
params.put("USERNAME", username);
params.put("PASSWORD", password);
npjt.execute('create user :USERNAME identified by :PASSWORD', params);
}
List<Map<String, Object>> listUsers() {
DataSource ds = (new JndiTemplate()).lookup(database);
NamedParameterJdbcTemplate npjt = new NamedParameterJdbcTemplate();
return npjt.queryForList("select * from all_users", new HashMap<>());
}
Provided that your container has the JNDI datasources already defined, the above code should cover both the creation of a user and the listing of users. No need to define entities or repositories or anything else. You don't even have to define your datasources in a spring #Configuration. The above code (which you will have to test) is really all you need so you could wire it in a #Controller and be done with it.
If you don't use JNDI it's no problem either: you can use HikariCP to define your datasources, providing the additional arguments as parameters.
This solution will work no matter how many different datasources you have and won't need redeployment unless you really have to work on its features. Plus, it doesn't need the developer to know JPA and it doesn't need to spread the configuration all over the place.

Change MySQL Schema at runtime

I have a multi-tenant application. It's a school management system.
The application is configured to use Schema-Per-Tenant approach. But this is limited to only one schema access per request. I want to use three different schemas per request.
Each user has two schemas and an application wide common schema. Totally 3. I want to enable my app to switch between all three schemas under one rest call.
For example, if the request is to add a new Student, the student must be added to SCHEMA_A, an auto-generated user account for the student must be added to the COMMON_SCHEMA and the studentId must be added to the class in SCHEMA_B.
At present I use
entityManager.createNativeQuery("USE SCHEMA_A").executeUpdate();
add(student);
entityManager.createNativeQuery("USE COMMON_SCHEMA").executeUpdate();
add(generateUserForStudent(student));
entityManager.createNativeQuery("USE SCHEMA_B").executeUpdate();
addStudentToClass(student);
it works.
But I wonder if it is a valid approach.
I also am not sure if it will cause conflicts when multiple users are logged in at the same time.
Can someone guide me on this?

Better way to secure REST resources in Spring?

I have a RESTful service that exposes resources like /user/{id}
Now, the user can provide the credentials, get the token and access the resource. However, once authenticated, the user can access the resources for any id.
Meaning, user1 can access the URIs like /user/1 as well as user/2 and so on. I ended up using a Principal in the controller methods and started checking the id of the Principal with the id the user is trying to access.
Further, the user has multiple resources associated with it. Say, user1 owns res1 and res2, user2 owns res3 and res4. These can be accessed via /user/1/res/2. I need a way where I can prevent /user/1/res/3 as res3 is owned by user1 and not user2.
But I believe that this problem is very common and I am not really convinced with my solution.
Is there a better way to deal with this problem?
Thanks
You should not be exposing resourse /user/{id} at all if you all user can do is access only their own ID.
If I understand correctly, just exposing /user is enough, find ID of user from Principal or session etc and return result.
If you really want to do it, you can have custom implementation of #PreAuthorize. Got this code from a blog.
#PreAuthorize("isUsersRes(#id)")
#RequestMapping(method = RequestMethod.GET, value = "/users/{id}")
public UsersfindById(#PathVariable long id) {
return Users.findOne(id);
}
public class CustomMethodSecurityExpressionRoot
extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public CustomMethodSecurityExpressionRoot(Authentication authentication) {
super(authentication);
}
And implemenation of isUsersRes
public class CustomMethodSecurityExpressionRoot
extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public boolean isMember(Long OrganizationId) {
//user logic
}
Check complete blog here
This is a common problem with varied solutions. Also its not a problem related to REST alone. We have had this ever since apps exist. Employee can see his salary slip, leave records, etc but not another employee's.
One solution I like the most is 'security in depth'. This idea comes from how I have seen this work in banking systems for decades. This needs to get supported in the DB layer first.
You would need a table design like this example (or whatever your app's entity hierarchical structure is):
Organisation
-Dept
--user
And all non-master tables need to have a relation to one of these entities. Example:
Payslip -> user
Leave record -> user
Manager -> dept
HR Manager -> org
etc...
You would need another table to map out the basic access levels (This can get complex if we need to implement different sub access levels)
user1:dept2:org1
user2:dept2:org1
(I have seen some implementations that send this table's info as part of an encrypted access token that is used on every access request if the access has to be sessionless.)
You have not mentioned a framework/language but most languages have a database layer. For example if the DB layer is hibernate-java. There are interceptors (https://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Interceptor.html#onPrepareStatement(java.lang.String)) that can be used to modify the query thats being executed.
Every query to DB will go with additional where-clause for these relationship keys.
We can get clever with Spring AOP, REST interceptors and a lot of other techniques on top of this basic architecture to enforce this security.
Idea will be that DB layer does not return data thats not accessible to the logged in user principal irrespective of what queries higher layer code uses.
if this is in place, a REST GET call for
/payslip/user1/Jan-2017
will end up with a 404 and not a 403.
Expecting this to be solved by a framework or a superficial set of interceptors is both risky and not future proof. We end up continuously tweaking the interceptors as the url patterns evolve.
Addition to show table examples:
ACL table
user, uid, dept, org
--------------------
jhon, 1 , 1 , 1
mary, 2 , 2 , 1
will, 3 , 2 , 1
Payslip table
--------------
month, net, deductions,..., uid
-------------------------------------
Jan , 200, 15.5 ,..., 3
Feb , 200, 15.5 ,..., 3
Project table
-------------
pname, pstart, pbudget, dept
------------------------------------
mark1, 12/21 , 20000 , 2
markx, 12/31 , 40000 , 2
What you want is user roles and permissions + cross user control. To find out user roles and permissions refer this
Also additionally you may want to cross check their user ID to the resource ID. Since you cannot let user1's resource ID 1 to view by user2, you will need to add userID as part of the resource id ex:- /user/user_id_1.
Otherwise we don't have a logical way to separate which resources are applicable to which users.

Is it possible to retrieve custom property from AD using ADVAPI

I am doing NTLM/Kerberos SSO using com.sun.jna.platform.win32.Sspi, com.sun.jna.platform.win32.Secur32 and com.sun.jna.platform.win32.AdvapiUtils to actually authenticate the user against the AD and retrieve its username. This works perfectly for both domain and non-domain users. As a next step I would like to retrieve a custom user property from the AD, e.g. email. Unfortunately, the com.sun.jna.platform.win32.Advapi32Util.Account class has only fqn, sid and groups properties. Is it possible using the native interfaces listed above to retrieve other properties as well?
I can't use LDAP because of a number of constraints, so I am looking for a work around.

Categories