parse Spring Security method #PreAuthorize - spring-security

I have annotated some service methods in my App with #PreAuthorize annotations. These annotations use hasAuthority, hasRole etc and they may include other valid constructs in future.
For ex:
#PreAuthorize("hasAuthority('Customer.Get')")
public Customer getCustomer(String name);
I want to collect all the authorities used across all my PreAuthorize annotations such as the Customer.Get etc, reflectively.
PreAuthorize pre = (PreAuthorize) m.getAnnotation(PreAuthorize.class);
String expr = pre.value();
I was looking for something of the sorts:
List<HasAuthority> h = SpringXYZ.getHasAuthorities(expr);
and get the "Customer.Get" string out of the h.get(0) ....
Is this possible? Since I need to do this retrieval during the initialization of my app, there is no authentication object available at that time.
Any suggestions are appreciated.

An SPeL expression like this hasAuthority('Customer.Get') will be evaluated at run time. At load time all that framework must know is that #PreAuthorize was applied (and corresponding parameters as strings). All work will be done at run time by SPeL parser / runtime environment. So I think there is no helper class that can help you.
If you want avoid parsing then try to use #Secured annotations:
#Secured("ROLE_Customer.Get")
public Customer getCustomer(String name);
ROLE_ prefix may be removed (some additional conf):
#Secured("Customer.Get")
public Customer getCustomer(String name);
or JSR-250 #RolesAllowed:
#RolesAllowed("Customer.Get")
public Customer getCustomer(String name);
It will be less flexible from security point of view but much simplier for pasing.

Related

Including Domain Object Security #PostFilter in Spring Data repositories Pageable endpoints

In my project I use Spring-Data, Spring-Data-Rest and Spring-Security.
What I need to accomplish is to implement domain object security (ACL) over these repositories. Specificaly #PostFilter over Pageable.findAll() method.
Method level security is easily implemented as outlined here.
There is also a section in docs about using security expression with #Query here.
But although I can use hasPermission(..) method inside #Query too, there is no way to include the object (SQL row) in this method - to be specific do this:
#Query("select u from #{#entityName} u where 1 = ?#{security.hasPermission(u, 'read') ? 1 : 0}")
Now I understand that this is way different than modifying the query pre-execution like this:
#Query("select m from Message m where m.to.id = ?#{ principal?.id }")
I also found the following jira issue:
https://jira.spring.io/browse/DATACMNS-293
Which I suspect that once it gets resolved there will be a solution to this, but it doesn't seem like it's going to be anytime soon.
I still need to implement this functionality and for that I would like to get your input and pointers on possible solutions.
Right now I am thinking about creating my custom annotation that will mimmick the #PostFilter one and use the same syntax but will get invoked manually inside my own BaseRepositoryImplementation. There I will get the repository interface from type and Repositories#getRepositoryInformationFor(type)#getRepositoryInterface(), find the annotation on respective method and manually invoke the security check.
Do you maybe have a different solution, or some notes about my proposed solution?
Also do you happen to know if there is any timetable on the mentioned jira issue?
One lightweight way is to do it is using the hasPermission() method and implementing your own "Permission Evaluator" at the Controller level, if that's an option for you.
#PreAuthorize("hasPermission(#employee, 'edit')")
public void editEmployee(Employee employee) {
...
}
#Component
public class PermissionEvaluatorImpl implements PermissionEvaluator {
#Override
public boolean hasPermission(Authentication auth,
Object targetDomainObject, Object permission) {
// return true if "auth" has "permission" permission for the user.
// Current-user can be obtained from auth.
}
...
}
This is described in more detail here: http://www.naturalprogrammer.com/spring-domain-object-security-logged-in-user/

How to only fetch data owned by an authenticated user in GraphRepository

I'm trying to create a REST API using Spring Boot (Version 1.4.0.M2) with Spring Data Neo4j, Spring Data Rest and Spring Security. The Domain is consisting of three types of Entities:
The user entity storing the username/password and relationships to all user owned entities:
#NodeEntity
public class User extends Entity {
#Relationship(type = "OWNS")
private Set<Activity> activities;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;
private String username;
}
Content entities created by/owned by a user:
#NodeEntity
public class Activity extends Entity {
private String name;
#Relationship(type = "ON", direction = Relationship.INCOMING)
private Set<Period> periods;
#Relationship(type = "HAS", direction = Relationship.INCOMING)
private User user;
}
Data entities storing date and time informations accessible to all users
#NodeEntity
public class Period extends Entity {
#Relationship(type = "HAS")
private Set<Activity> activities;
#Relationship(type = "HAS_PERIOD", direction = Relationship.INCOMING)
private Day day;
private PeriodNames name;
}
I'm trying to keep everything as simple as possible for now so I only use Repositories extending GraphRepository and a Spring Security configuration that uses Basic Authentication and the User Object/Repository in the UserDetailsService. This is working as expected, I am able to create a user, create some objects and so on.
The problem is, I want a user only to access their own entities. Currently, everybody can access everything. As I understood it from my research, I have three ways to achieve this:
Use Spring Security Annotations on the repository methods like this:
#PostAuthorize("returnObject.user.username == principal.username")
#Override
Activity findOne(Long id);
#PostFilter("filterObject.user.username == principal.username")
#Override
Iterable<Activity> findAll();
Annotate the methods with #Query and use a custom query to get the data.
Create custom controllers and services that do the actual data querying, similar to this: sdn4-university
.
Now my question:
What would be the best way to implement the desired functionality using my available tools?
For me it seems to be the preferred way to use #2, the custom query. This way I can only fetch the data that I actually need. I would have to try to find a way to create a query that enables paging for Page<Activity> findAll(Pageable pageable) but I hope this is possible. I wasn't able to use principal.username in the custom query though. It seems as if spring-data-neo4j doesn't have support for SpEL right now. Is this correct or is there another way to access the currently authenticated user in a query?
Way #1, using Spring Security Annotations works for me (see the code above) but I could not figure out how to filter Page<Activity> findAll(Pageable pageable) because it returns a Page object and not an entity or collection. Also I'm not sure if this way is efficient as the database always has to query for all entities and not only the ones owned by a specific user. This seems like a waste of resources. Is this incorrect?
Or should I just go with #3 and implement custom controllers and services? Is there another way that I didn't read about?
I'm very grateful for any input on this topic!
Thanks,
Daniel
Since no one has answered yet, let me try...
1) I agree with your assessment that using Spring #PostAuthorize Security is not the way to go here. For filtering data it seems not to be the perfect way to do it here. As you mentioned, it would either load all the data and then filter it, creating a heavy load or probably wreck the paging mechanism:
Imagine you have a million results, loading them all would be heavy. And if you filter them later, you might end up with let's say 1.000 valid results. But I strongly doubt that the paging mechanism will be able to cope with that, more likely that in the end you will seem to have many empty pages. So if you loaded the, let's say, first 20 results, you might end up with an empty result, because they were all filtered out.
Perhaps for some stuff you could use #PreAuthorize to prevent a query from happening, if you only want to get a single result, like in findOne. This could lead to a 403, if not allowed, which would, imho, be ok. But for filtering collections, Spring security doesn't seem a good idea.
3) That's always a possibility, but I wouldn't go there without trying for alternatives. Spring Data Rest is intended to make our code cleaner and coding easier, so we should not throw it away without being 100% sure that we cannot get it to do what we need.
2) Thomas Darimont wrote in this blog posting that there is a way to use principal (and other stuff) in #Query annotations. Let me sumarize to have the answer here...
The basic idea is to create a new EvaluationContextExcentionSupport:
class SecurityEvaluationContextExtension extends EvaluationContextExtensionSupport {
#Override
public String getExtensionId() {
return "security";
}
#Override
public SecurityExpressionRoot getRootObject() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return new SecurityExpressionRoot(authentication) {};
}
}
...and...
#Configuration
#EnableJpaRepositories
class SecurityConfiguration {
#Bean
EvaluationContextExtension securityExtension() {
return new SecurityEvaluationContextExtension();
}
}
...which now allows a #Query like this...
#Query("select o from BusinessObject o where o.owner.emailAddress like "+
"?#{hasRole('ROLE_ADMIN') ? '%' : principal.emailAddress}")
To me, that seems to be the most clean solution, since your #Query now uses the principal without you having to write all the controllers yourself.
Ok, i think I have found a solution. I guess it's not very pretty but it works for now. I used #PostAuthorize("returnObject.user.username == principal.username") or similar for repository methods that work with single entities and created a default implementation for Page<Activity> findAll(Pageable pageable) that just gets the username by calling SecurityContextHolder.getContext().getAuthentication().getName() and calls a custom query method that gets the correct data:
#RestResource(exported = false)
#Query("MATCH (u:User)-[:HAS]->(a:Activity) WHERE u.username={ username } RETURN a ORDER BY CASE WHEN NOT { sortingProperty} IS NULL THEN a[{ sortingProperty }] ELSE null END SKIP { skip } LIMIT { limit }")
List<Activity> findAllForUsernamePagedAndSorted(#Param("username") String username, #Param("sortingProperty") String sortingProperty, #Param("skip") int skip, #Param("limit") int limit);

AutoFac Injection into attribute

So I have a need for injecting a number of different services into an authorization attribute I'm using. For simplicity I will leave this to show the configuration manager.
public class FeatureAuthorizeAttribute : AuthorizeAttribute
{
public IConfigurationManager ConfigurationManager;
private readonly string _feature;
public FeatureAuthorizeAttribute(string feature)
{
_feature = feature;
var test = ConfigurationManager.GetCdnPath();
}
}
Which would be used as follows
[FeatureAuthorize("Admin")]
I have tried to use constructor injection
public FeatureAuthorizeAttribute(string feature, IConfigurationManager configurationManager)
{
ConfigurationManager = configurationManager;
_feature = feature
}
However this just causes an error when I attempt
[FeatureAuthorize("Admin", IConfigurationManager)]
Which seems like the wrong way to go about it in the first place. I'm assuming that I need to register my custom authorization attribute with the container to get it to start picking up
Instead of trying to use Dependency Injection with attributes (which you can't do in any sane, useful way), create Passive Attributes.
Specifically, in this case, assuming that this is an ASP.NET MVC scenario, you can't derive from AuthorizeAttribute. Instead, you should make your Authorization service look for your custom attribute, and implement IAuthorizationFilter. Then add the filter to your application's configuration.
More details can be found in this answer: https://stackoverflow.com/a/7194467/126014.

HTTP Method Security

I'm working on building a simple HATEOAS REST service with Spring Boot.
I have a MongoDB repository and resource where I would like to allow GET, but disallow all else. (POST, UPDATE DELETE, etc.)
The general idea is to allow a "USER" to do as it wishes with the resoure and allow "PUBLIC" read-only access.
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends MongoRepository<Person, String>
{
#Secured("ROLE_USER")
public void delete(Person person);
#Secured("ROLE_USER")
public Person save(Person person);
#Secured("ROLE_USER, ROLE_PUBLIC")
public Person findOne(String id);
}
I don't feel I'm approaching this at the right angle. What's the preferred way of doing this?
I'm guessing you're using spring-data-rest and skipping controllers entirely.
First thing you're having a problem with is that #Secured takes an array of roles...so i think your check on findOne is looking for a role ROLE_USER, ROLE_PUBLIC not either of those roles. Changing it to:
#Secured({"ROLE_USER", "ROLE_PUBLIC"})
might solve your problems.
After that you have a few options to consider
Option 1: Switch to ROLE_ANONYMOUS instead of ROLE_PUBLIC which is the default role anonymous users are assigned in Spring Web w/ spring security.
Option 2: Make sure anonymous users have the ROLE_PUBLIC (which they won't by default, they have ROLE_ANONYMOUS by default). There's a lot of ways to do this, see http://docs.spring.io/spring-security/site/docs/3.0.x/reference/anonymous.html
Option 3: Switch to #PreAuthorize annotations for securing methods and use the EL expression like:
#PreAuthorize("hasRole('ROLE_USER') or isAnonymous()")
see http://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html note you have to enable pre-post annotation processing.
Final note, ROLE_USER is not a default role...it's DEFAULT_USER for most stuff.
this is a very open ended question and there's a lot of solutions, you'd have to give more context as to your desires to narrow it down.

How do I use JAXB types as Grails domain classes?

My system consists of a back-end part, written in Java, exposing a set of web services which are defined contract-first using WSDL and XSD. Among the data transported over the web services are a large set of product types (they are different kinds of bank accounts, to be precise). Our front-end is a Grails web application. It uses and exposes the data and operations hosted by the back-end. The front-end has no database on its own for security reasons; all data is stored on the back-end. Pretty standard architecture.
Now, the set of products is large, growing, and volatile. Each product type has to be CRUD-ed on the web application user interface. It would be lovely if I could tell Grails to use the XSD specifications of the product types as domain types, and generate views and controllers for them.
I have not found a solution for this puzzle yet, even after extensive experiments and lots of web browsing. I have about a year's worth of professional experience with Grails. Any help or ideas would be appreciated.
Some details:
A product type is a simple POJO data carrier. Some simplified examples:
package jaxbgenerated;
public class Product1 {
protected Account from;
protected Account to;
protected String name;
// + getters and setters
}
public class Product2 {
protected List<Account> accounts;
protected String name;
// + getters and setters
}
public class Account {
protected String id1;
protected String id2;
// + getters and setters
}
Note that "Account" is not a product type but it is a JAXB-generated type. Products can contain such in addition to properties of simple data types like String, int and Date but they never contain other product types.
The end result I am aiming for is a Grails-generated form where a user can edit a Product1 instance with nested forms for editing its constituent Accounts. And likewise for Product2.
My idea is to first manually code a Grails domain class for each JAXB-generated type:
//in grails-app/domain:
 
import utilities.Copier
 
class Product1 extends jaxbgenerated.Product1 {
Product1(jaxbgenerated.Product1 jaxb) {
Copier.copy(jaxb, this)
    }
static constraints = {
}
}
There is a bit of boilerplate code here but nothing more than I can live with. The Copier.copy() function is (I think) needed to convert a jaxbegenerated.Product instance fetched from the back-end to a Product1 instance which can be used in Grails. It recursively looks for jaxbgenerated properties in the jaxb source, and copies their values to the corresponding Grails domain type. The constructor is invoked from a layer that fetches data from the back-end.
I can use the constraints block to manually add semantic constraints where needed, e.g. that the "from" and "to" accounts are not the same.
Next, I generate controllers and views for all the Grails domain classes thus constructed, and run the application. And get a stack of exceptions:
Caused by MappingException: Could not determine type for: Account, at table:
product1_type, for columns: [org.hibernate.mapping.Column(from)]
I think the trouble here is that Product1's "from" and "to" properties are not of type Account but of type jaxbgenerated.Account.
I have tried different approaches but to no avail. Some notes:
As I said, all data storage happens on my back-end, so I do not need
the GORM/Hibernate aspect of Grails. Therefore I tried adding
"static mapWith = "none" to the domain classes but that did not
help.
I tried explicitly telling Hibernate the type of the Accounts
in Product1 by adding "static mapping = { from type: Account }" but
that did not work either.
Any help or ideas would be appreciated.
/Jan Reher, Systematic A/S, Denmark
I think you have do write a dababase plugin similar in concept to this simpledb

Resources