Spring Security w/ Nested Accounts - java

I'm having difficulties with authorizing nested accounts in Spring Security. I'm new to Spring Security. I spent around a week to reach a working solution, but it's an ugly one and I'd love to refactor it...
I'm using Spring 4.2.4 and Spring Security 4.0.3.
I have user accounts that look like this:
Office User A
Office User B
Field User C
--> Field Sub-User a
--> Field Sub-User b
Field User D
--> Field Sub-User c
So, an office user has only one account, but a field user has at least one sub-account always, with the potential for two or more sub-accounts. Each sub-account can have different authorities. Like one sub-account can view only, and another sub-account can view plus create.
The user will sign-in with an account (A, B, C, or D). If A or B signs in, there is no issue. However, if user C signs in, they need to pick (a or b) and we need information for their choice. If user D signs in, we need information for user (c). I'm using Spring Security and struggling with getting the information for User a/b/c above. I came to a solution, but it's not ideal, and I'd like to know a more proper way.
My solution:
In the #AuthenticationPrincipal implementation of UserDetails, add below code:
private Collection<GrantedAuthority> authorities;
private String uniqueId1;
private String uniqueId2;
public void setAuthorities(Collection<GrantedAuthority> authorities) {
this.authorities = authorities;
}
public Collection<GrantedAuthority> getAuthorities() {
if (this.authorities != null) {
return this.authorities; //CHILD
}
return super.getAuthorities(); // (SUPER)
}
public void setUniqueId1(String uniqueId1) {
this.uniqueId1 = uniqueId1;
}
public String getUniqueId1() {
if (this.uniqueId1!= null) {
return this.uniqueId1; //CHILD
}
return super.getUniqueId1(); // (SUPER)
}
public void setUniqueId2(String uniqueId2) {
this.uniqueId2 = uniqueId2;
}
public String getUniqueId2() {
if (this.uniqueId2!= null) {
return this.uniqueId2; //CHILD
}
return super.getUniqueId2(); // (SUPER)
}
Then, in my method to get logged-in user information, accept a string of the selected user and do like such:
public #ResponseBody FullUserDetails getLoggedInUserInfo(
#AuthenticationPrincipal MyImplementationOfUserDetails user,
String selectedUsername)
MyImplementationOfUserDetails user2 = getUserInfo(selectedUsername);
user.setAuthorities(user2.getAuthorities());
user.setUniqueId1(user2.getUniqueId1());
user.setUniqueId2(user2.getUniqueId2());
In this way, I am able to 'change' the authorities and unique id properties which would otherwise be unchangeable because they are private in the parent classes and normally only accessible through a constructor. I tried making a new constructor in the UserDetails implementation, but when I create a new #AuthenticationPrincipal, it doesn't override the one in the session. I think a new constructor would be the most proper way to go. How do I override the #AuthenticationPrincipal in the session with a new UserDetails object? Or is there a better approach entirely, which I'm not thinking of? I just want the new authorities and unique ID information for the sub-user that was selected to be put into the #AuthenticationPrincipal. The next time I get the #AuthenticationPrincipal, it will then have that information.
The consequences of not getting the new information and putting it in the #AuthenticationPrincipal is that the webpage behavior will be wrong. Mostly because the authority levels will not be correct for the user that was selected.
I was trying to call:
SecurityContextHolder.getContext().setAuthentication(newAuthenticationObjectHere);
But I wasn't seeing the changes after calling setAuthentication()...
The only way I was able to see the changes was by directly calling setters in the MyImplementationOfUserDetails object retrieved from the #AuthenticationPrinciple.

Sorry, very confused, but the main idea is almost understandable.
I think you go in cycles with an idea to calculate all complex relations between accounts and after that to give the final account required permission and provide access to some resource.
What if to change the logic of providing access to resources without manipulating the account relations? And the very important chain - to use remote/distributed Authorization server like Spring OAuth2 one? With it, you can have authorities for any account AND scopes (!). Depending on organization conditions an account can have different scopes. And your final resources (usually they are controllers methods) have pre-authentication by scope. According to this idea, you can separate the logic of providing access based on specific of accounts, implementing all logic in DB with accounts.
Maybe it's the wrong idea for your project, for me it works - I change scopes for customers depending their conditions, but not manipulate with their data and relations in runtime.

Related

Rest Resources Separation

I've been trying to start a REST api with Spring Boot and I'm a bit strugling with the separation of my resources and which endpoint should be in which file.
Let's say we have an api enpoint to deal with a user and achievements from this user:
/user/{id} GET - to fetch user by id
/achievement/{id} GET - to fetch by achievement
Which are both in their separates resources file:
UserResource
#RestController
public class UserResource {
public UserResource() {...}
#GetMapping("/users/{id}")
public UserDTO getUser(String id) {
log.debug("REST request to get User : {}", login);
return userService.getUserWithAuthoritiesById(id).map(AdminUserDTO::new));
}
And AchievementResource
#RestController
public class AchievementResource {
public AchievementResource(...) {...}
#GetMapping("/achievements/{id}")
public ResponseEntity<Achievement> getAchievement(#PathVariable Long id) {
return achievementRepository.findById(id);
}
}
So far so good, pretty simple. My problem comes when I must get all achievements from a User. Naming covention says I should have an endpoint such as:
/user/{id}/achievements GET
But where should this endpoint be? I feel like both Resources could be good since for the UserResource, the root of the endpoint is the user, but the AchievementResource could be logical too since we are returning achievements.
Easy answer: you have the wrong problem
But where should this endpoint be?
The definition of the resource should be in your machine readable api definition. You produce the class files you need by feeding your definition into a code generator for your choice of language. The generator will put the classes it creates in files somewhere, and you leave them in this default arrangement until some point in the future when you have a compelling reason to arrange them differently (at which point, you fork the code generator and make your preferred design the default).
That said, when designing by hand there's nothing particularly special about "REST endpoints". The guidelines for where resource classes belong is no different from any other classes in Java....
That said, I find that the literature around file layout heuristics rather disappointing. There doesn't seem to be a lot of material discussing the trade offs of different designs, or contexts in which one choice might be more compelling than another.
For your specific situation, I would advise putting the new resource into a file of its own. The argument here being that your UserResource has User dependencies, and your AchievementsResource has achievements dependencies, but your new thing has both, and as a matter of (hand waves) principle, we should avoid bringing unneeded achievements dependencies into the namespace of the UserResource (and vice versa).
In other words, if we find ourselves adding imports to an existing file to implement a new thing, that's a hint that the new thing may be better placed somewhere else.
Using separate files also has nice mechanical advantages - it reduces merge collisions, each file will have its own source control history (meaning that the history of Users isn't cluttered with a bunch of commits that are exclusively about new thing). See Adam Tornhill's work over at CodeScene, for example.
As you separated the controllers, it is not wrong, you should classify the methods by their general entity, "if I need to recover the user's achievements", it is related to both, however, where does she get this data from? of the Achievements knowing that each achievement must have a relationship in the database with the user, you can very well look it up in the achievement controller with a List returnAchievementsByUser (Integer Id) method.
It depends on your point of view and the business behind the scene. You can use just one endpoint in many cases; if "users" are the main resources who have achievements, then "/users/{user-id}" and {users/{user-id}/achievements/{achievement-id} get the user by Id and special achievement of the user
#RestController
#RequestMapping("users")
public class UsersRestController{
#GetMapping("/{user-id}")
public UserDTO getUser(#PathVariable("user-id") String id) {
code...
}
#GetMapping("/{user-id}/achievements/{achievement-id}")
public AchievementDTO getAchievement(#PathVariable("user-id") String userId,
#PathVariable("achievement-id") String achievementId) {
code...
}
}
And if locating "achievements" on top of "users" in their entity hierarchy has meaning to you and your business, then /achievements/{achievement-id}/users/{user-id} can be a rest presentation:
#RestController
#RequestMapping("achievements")
public class AchievementsRestController{
#GetMapping("/{achievement-id}")
public UserDTO getAchievement(#PathVariable("achievements-id") String id) {
code
}
#GetMapping("/{achievements-id}/users/{user-id}")
public AchievementDTO getAchievement(#PathVariable("user-id") String userId,
#PathVariable("achievement-id") String achievementId) {
code
}
}
finally ,whenever they are not in an entity hierarchy, you can pass userId to
"/achievements/{achievements-id}" (or achievement-id to "/users/{user-id}") as a RequestParam.

How to get Query/Mutation operation name

I'm new to Spring boot + GraphQL.
I need to get the Query/Mutation operation name inside my controller class.
Purpose : Need to grand permission to some users to specific mutation/Query operations.
Here the user type will be passed as a request header and will be validated and check whether the user is allowed to access that operation.
#PostMapping
public ResponseEntity<Object> callGraphQLService(#RequestBody String query, #RequestHeader("user") String userName) {
ExecutionResult result = graphService.getGraphQL().execute(ExecutionInput.newExecutionInput()
.query(query)
.context(userName)
.build());
return new ResponseEntity<>(result, HttpStatus.OK);
}
Suggest any efficient mechanism to perform authorization for specific Query/Mutation
I think you're thinking of authorization in REST terms here, and it doesn't map well to GraphQL. Instead of a single decision at the top level based on the operation name (or based on the URL in REST), you need a more granular approach. You need to know who's allowed to see/do what at the field level, as the client is allowed make ad-hoc selections.
There's multiple ways to do this, but since you mentioned Spring, you can simply use Spring Security at the service level. If each protected field is backed by a service method (and it should be), you can protect those methods using Spring Security as usual.
Even better, you should also provide a custom GraphqlFieldVisibility implementation, so that unauthorized clients can't even know about the the existence of fields they're not allowed to see in the schema. You can use e.g. Spring's SpelExpressionParser to make decisions on what parts of the schema are visible dynamically, for each user, based on Spring Security rules.
If Spring Security is not an option, you can implement a custom Instrumentation (e.g. by extending SimpleInstrumentation). There you can implement the callbacks like beginExecuteOperation, that will give you access to the parsed query (enough if you really just want to do REST-style top-level auth only), or begin(Deferred)Field (which gives you access to the FieldDefinition) or beginFieldFetch/instrumentDataFetcher (which gives you access to the entire DataFetchingEnvironment) to perform auth per-field.
If you go this way, you can keep the auth information (e.g. the required roles) in the field definition itself as directives. And keep the currently logged in user in the shared context. This way you always have everything you need to do authentication at each level.
In all cases, it's advisable to provide GraphqlFieldVisibility to completely hide the existence of the protected fields contextually.
Here's an abstract example showing the major points using the Instrumentation approach (as you need nothing special for the Spring Security approach, just use Spring Security as usual):
//Checks if the current user has the needed roles for each field
public class AuthInstrumentation extends SimpleInstrumentation {
#Override
public DataFetcher<?> instrumentDataFetcher(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters) {
GraphQLFieldDefinition fieldDefinition = parameters.getEnvironment().getFieldDefinition();
//Each protected field is expected to have a directive called "auth" with an argument called "rolesRequired" that is a list of strings representing the roles
Optional<GraphQLArgument> rolesRequired = DirectivesUtil.directiveWithArg(fieldDefinition.getDirectives(), "auth", "rolesRequired");
if (rolesRequired.isPresent()) {
List<String> roles = (List<String>) rolesRequired.get().getValue();
User currentUser = parameters.getEnvironment().getContext(); //get the user from context
if (!currentUser.getRoles().containsAll(roles)) {
//Replace the normal resolution logic with the one that always returns null (or throws an exception) when the user doesn't have access
return env -> null;
}
}
return super.instrumentDataFetcher(dataFetcher, parameters);
}
}
You don't have to store the required roles in the directives, it's just a convenient place. You can get the same info from an external source if it's appropriate.
Then register this instrumentation:
GraphQL graphQL = GraphQL.newGraphQL(schema)
.instrumentation(new AuthInstrumentation())
.build();
And when executing a query, put the current user into the context:
//Get the current user's roles however you normally do
User user = loadUser(userName);
ExecutionInput input = ExecutionInput.newExecutionInput()
.query(operation)
.context(user) //put the user into context so the instrumentation can get it
.build()
This way you have everything neatly separated (no auth logic in resolvers, no external context needed) and contextual per field, even without using Spring Security.
Let's go further and make a custom GraphqlFieldVisibility:
public class RoleBasedVisibility implements GraphqlFieldVisibility {
private final User currentUser;
public RoleBasedVisibility(User currentUser) {
this.currentUser = currentUser;
}
#Override
public List<GraphQLFieldDefinition> getFieldDefinitions(GraphQLFieldsContainer fieldsContainer) {
return fieldsContainer.getFieldDefinitions().stream()
.filter(field -> isFieldAllowed(field, currentUser))
.collect(Collectors.toList());
}
#Override
public GraphQLFieldDefinition getFieldDefinition(GraphQLFieldsContainer fieldsContainer, String fieldName) {
GraphQLFieldDefinition fieldDefinition = fieldsContainer.getFieldDefinition(fieldName);
return fieldDefinition == null || !isFieldAllowed(fieldDefinition, currentUser) ? null : fieldDefinition;
}
private boolean isFieldAllowed(GraphQLDirectiveContainer field, User user) {
//Same as above, extract this into a common function
Optional<GraphQLArgument> rolesRequired = DirectivesUtil.directiveWithArg(field.getDirectives(), "auth", "rolesRequired");
List<String> roles = (List<String>) rolesRequired.get().getValue();
return currentUser.getRoles().containsAll(roles);
}
}
As you see, visibility depends on the user, which this time you can not get from the context, so you have to instantiate it per request. This means you need to transform the schema and instantiate GraphQL per request as well. The rest is the same.
GraphQLSchema schema = baseSchema.transform(sch ->
sch.codeRegistry(baseSchema.getCodeRegistry().transform(code ->
code.fieldVisibility(new RoleBasedVisibility(currentUser)))));
GraphQL graphQL = GraphQL.newGraphQL(schema)
.instrumentation(new AuthInstrumentation())
.build();
With that, you have a full security setup. Unauthorized users won't even know a field exists if they're not allowed to. If they're allowed to see it in general, but they can only fetch it conditionally, the AuthInstrumentation covers it.

How to avoid anemic data model? Can repositories be injected into entities?

I have an immutable User entity:
public class User {
final LocalDate lastPasswordChangeDate;
// final id, name, email, etc.
}
I need to add a method that will return information if the user's password must be changed i.d. it has not been changed for more than the passwordValidIntervalInDays system setting.
The current approach:
public class UserPasswordService {
private SettingsRepository settingsRepository;
#Inject
public UserPasswordService(SettingsRepository settingsRepository) {
this.settingsRepository = settingsRepository;
}
public boolean passwordMustBeChanged(User user) {
return user.lastPasswordChangeDate.plusDays(
settingsRepository.get().passwordValidIntervalInDays
).isBefore(LocalDate.now());
}
}
The question is how to make the above code more object oriented and avoid the anemic domain model antipattern? Should the passwordMustBeChanged method be moved to User if so how to access SettingsRepository, should it be injected into User's constructor, or should a Settings instance be provided to the ctor, or should the passwordMustBeChanged method require a Settings instance to be provided?
The code of Settings and SettingsRepository is not important, but for completness, here it is:
public class Settings {
int passwordValidIntervalInDays;
public Settings(int passwordValidIntervalInDays) {
this.passwordValidIntervalInDays = passwordValidIntervalInDays;
}
}
public class SettingsRepository {
public Settings get() {
// load the settings from the persistent storage
return new Settings(10);
}
}
For a system-wide password expiration policy your approach is not that bad, as long as your UserPasswordService is a domain service, not an application service. Embedding the password expiration policy within User would be a violation of the SRP IMHO, which is not much better.
You could also consider something like (where the factory was initialized with the correct settings):
PasswordExpirationPolicy policy = passwordExpirationPolicyFactory().createDefault();
boolean mustChangePassword = user.passwordMustBeChanged(policy);
//class User
public boolean passwordMustBeChanged(PasswordExpirationPolicy policy) {
return policy.hasExpired(currentDate, this.lastPasswordChangeDate);
}
If eventually the policy can be specified for individual users then you can simply store policy objects on User.
You could also make use of the ISP with you current design and implement a PasswordExpirationPolicy interface on your UserPasswordService service. That will give you the flexibility of refactoring into real policy objects later on without having to change how the User interacts with the policy.
If you had a Password value object you may also make things slightly more cohesive, by having something like (the password creation date would be embedded in the password VO):
//class User
public boolean passwordMustBeChanged(PasswordExpirationPolicy policy) {
return this.password.hasExpired(policy);
}
just to throw out another possible solution would be to implement a long-running process that could do the expiration check and send a command to a PasswordExpiredHandler that could mark the user with having an expired password.
I have stumbled upon a document that provides an answer to my question:
A common problem in applying DDD is when an entity requires access to data in a repository or other gateway in order to carry out a business operation. One solution is to inject repository dependencies directly into the entity, however this is often frowned upon. One reason for this is because it requires the plain-old-(C#, Java, etc…) objects implementing entities to be part of an application dependency graph. Another reason is that is makes reasoning about the behavior of entities more difficult since the Single-Responsibility Principle is violated. A better solution is to have an application service retrieve the information required by an entity, effectively setting up the execution environment, and provide it to the entity.
http://gorodinski.com/blog/2012/04/14/services-in-domain-driven-design-ddd/

context dependent validation for BeanValidation

I am a big fan of JSR 303 BV but in my current project i am having a lot of "context dependent validation" and i do not find any reasonable approach to implement them with BV.
Basically validation rules can depend on
logged in user (it is stored in http request)
a user we act upon - the id of the user is path param of REST URL like /foo/bar/user/1/sth
both of these
A little example :
class Alphabet{
#Valid
private Alpha a;
#Valid
private Beta b;
#Valid
private Gamma g;
}
And validation rule:
If the user whose id will provided in URL has role "admin" that none of "a","b","g" properties can be null. If it has role "user" only "a" must be not null and other props must be null.
So basically these kind of rules can be easily implemented as class level-constraint of Alphabet class but custom validator would need somehow access id of the user which is provided in URL. Is there any smart way this could be achieved or i have to force all REST clients to pass user id multiple times: in URL and in payload so the latter will be only used by BV.
The more complex case is when validation rule depends on logged in user, so custom validators will have to somehow access authenticated principal - from ThreadLocal or HttpServletRequest.
You could make use of validation groups:
//Define validation groups
interface AdminChecks {}
interface UserChecks {}
//Assign the constraints to the two groups
class Alphabet{
#NotNull(groups={AdminChecks.class, UserChecks.class})
private Alpha a;
#NotNull(groups=AdminChecks.class)
#Null(groups=UserChecksChecks.class)
private Beta b;
#NotNull(groups=AdminChecks.class)
#Null(groups=UserChecksChecks.class)
private Gamma g;
}
Depending on the role of the current user, you then specify either AdminChecks or UserChecks when invoking the validator, e.g. like this for the admin checks:
Validator validator = ...;
Set<ConstraintViolation<Alphabet>> violations = validator.validate(
new Alphabet(),
AdminChecks.class
);

Spring MVC 3 - Binding parameters to backing object

I am trying to setup an edit form for my User object. I have automatic binding from the form to the user object working fine.
I'm wondering how do I prevent malicious users from binding to fields of my User object that I don't want them to?
For example, my user object has userName, and it also has points. When they click edit user, I want them to be able to change the userName, but not the points. A malicious user could just send an extra points=429429 as a hidden field in the form by editing the HTML, and this would automatically be bound to the backing object by Spring.
I would suggest separating your front-end code from the logic for what will be saved in the database. The form backing object is just meant to be a simple object that captures want the user has done in the view... it shouldn't be used to save directly to the database. I would have a Service layer handle the decision on whether or not to update certain fields... the controller should just receive the input and pass it along. This way, the service can decide what fields should be updated.
public void updateUser(long userId, User updatedUser) {
User currentUser = dao.getCurrentUserById(userId);
currentUser.userName = updatedUser.username;
//...... update anyother fields....
dao.SaveUser(currentUser);
}
or you could define the method in a way that the caller knows what will be updated:
public void updateUser(long userId, String updatedUsername);
I would also argue that this is a lot easier to unit test if this logic is in the Service Layer.
Hope this helps
Add this to your controller:
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setAllowedFields("field1", "field2");
}

Categories