In the existing Grails application, I have 2 user domains, say UserAdmin and UserBasic. Both these domains have few common fields and also some distinct fields(respective to domain) and currently the login/logout is maintained using sessions for both types of users separately. I want to integrate spring-security in the existing application.
What would be the best approach to do it ? Considering that both domains also have different field. Can we inherit both the domains in a single domain and use it as Spring Security user class ? Please suggest.
Spring Security Core uses one of the implementations of UserDetails interface as a projection of authenticated user. Grails provides e.g. GrailsUser class:
https://github.com/grails-plugins/grails-spring-security-core/blob/master/src/java/grails/plugin/springsecurity/userdetails/GrailsUser.java
Keep in mind, that this class is not a "domain" class in terms of Grails application layout - it does not get persisted in the database, it's just a projection of the user that is bounded to the current session.
If you have 2 different domain classes that represents users in your application, you can try to provide your own implementation of UserDetailsService, e.g.
class CustomUserDetailsService implements UserDetailsService {
#Override
UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
// 1. Check if expected user is type of UserBasic
// 2. If not, check if expected user is type of UserAdmin
// 3. If nothing found, throw an exception
// 4. Otherwise create new GrailsUser instance using UserBasic or UserAdmin data
// 5. Return created GrailsUser instance
return null
}
}
Then you have to inject your implementation by adding or modifying an entry in grails-app/conf/spring/resources.groovy e.g.
// Place your Spring DSL code here
beans = {
// other beans goes here
// ...
userDetailsService(CustomUserDetailsService)
}
This is just a concept that you can start from.
Related
In spring security, or RBAC, the Authority is described as a string, such as "download-file" means user can download file. If I need to limit user maximum daily download times and assign different values to different user, means the Authority contains dynamic values, how can I do this in spring security?
As you are alluding to there is a difference between authorities (i.e. roles) and permissions. Authorities tend to broadly apply for an application and have no state while permissions tend to be on specific objects and contain state.
This seems more like a domain problem than a permissions problem. Putting the logic into security feels a bit like having a form that must contain a valid email and checking the email format in security. I'd consider moving the logic outside of the security code.
If you really want to do this with Spring Security, I'd use a custom Bean that performs the check:
#Component
public class Download {
public boolean isAlowedForUser(Authentication authentication) {
// ...
return result;
}
public boolean isAllowedForCurrentUser() {
return isAllowedForUser(SecurityContextHolder.getContext().getAuthentiation());
}
}
Then you can autowire the Bean into your code and check the permission by invoking the code. If you prefer, you can also integrate into Spring Security's method security to perform the checks. To enable it you need to specify #EnableGlobalMethodSecurity(prePostEnabled = true) at the top of one of your configuration classes. Then you can use something like this on a Spring managed Bean:
#PreAuthorize("#download.isAllowedForCurrentUser()")
public void downloadFile(String fileName) {
Please refer this link
Spring Boot : Custom Role - Permission Authorization using SpEL
You can add new permission , like "DOWNLOAD_FILE" and authenticate if the current user has that permission using -
#PreAuthorize("hasPermission('DOWNLOAD_FILE')")
You can also limit access for Roles as well
#PreAuthorize("hasRole('ADMIN') and hasPermission('DOWNLOAD_FILE')")
I want to use a custom claim on an open id connect id_token to indicate the groups that the user belongs to.
I have this working with Okta and Spring Security 5. With Okta there was a simple way to set-up a custom claim and associate it with a user's groups.
Now I want to do the same with Cognito.
How do I use Spring Securities group/role authorities in conjunction with Cognito?
I have open id connect login working with Spring Security 5, Webflux & Cognito but now I want to add some role based, group membership coarse-grained entitlements.
I have added a custom attribute to my user-pool but can't see how to populate it.
thanks
Just want to add an implementation example here.
I use my custom JwtAuthenticationConverter to extract cognito:groups and bind them to Spring Security Authorities.
Stack is the same as yours (Webflux, Spring 5 [boot]). Here is the converter class:
#Component
public class GrantedAuthoritiesExtractor extends JwtAuthenticationConverter {
#Override
protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
val authorities = (Collection<String>) jwt.getClaims().get("cognito:groups");
if (authorities == null) {
return emptyList();
}
return authorities.stream()
.map(SimpleGrantedAuthority::new)
.collect(toList());
}
}
And here is the related part in my SecurityConfiguration:
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http,
GrantedAuthoritiesExtractor extractor) {
http
// ...
// Other configurations
// ...
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(new ReactiveJwtAuthenticationConverterAdapter(extractor));
return http.build();
}
Also one point that should be mentioned is that #EnableReactiveMethodSecurity annotation should be placed above SecurityConfiguration class to enable functionality of #PreAuthorize annotations across the application.
I have created another user pool and confirm that adding a custom attribute to the Cognito user pool with the name of 'groups', that is mutable, has resulted in the id_token returned from an openid scope call having a cognito:groups custom claim containing strings representing the groups that the user is allocated to. If the user isn't allocated to any groups then the claim doesn't appear on the token. I suspect this is why I believed it to not be initially working. I couldn't find any explicit documentation that said this his how it works thought. Hope this helps someone, cheers.
I have been implementing Jhipster at my work and loving it. I was asked to implement a security validation that one user should not be allowed to edit the entity created by other user. For this I need two things:
First, in all entities, add a ManytoOne relation with User entity.
In Backend put a validation in controller while updating the entity to check if current logged in user is same as what is stored in DB. In front end also same logic to show/hide edit button.
I have done a POC for this and it works but is little ugly, check the code:
public ResponseEntity<Entry> updateEntry(#RequestBody Entry entry) throws URISyntaxException {
log.debug("REST request to update Entry : {}", entry);
if (entry.getId() == null) {
throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull");
}
//here is my logic
Optional<Entry> entry_db = entryRepository.findOneWithEagerRelationships(entry.getId());
if(!entry_db.get().getUser().getId().equals(userService.getUserWithAuthorities().get().getId())) {
//throw someexception
}
//
Entry result = entryRepository.save(entry);
return ResponseEntity.ok()
.headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, entry.getId().toString()))
.body(result);
}
Is there any better or OOTB way of doing this??? may be something in spring security i am not aware of??
Thanks for help!!
This is a job for Spring Security Expression-Based Access Control, in particular you could annotate your method with #PreFilter and a custom PermissionEvaluator you would implement with similar logic as in your POC. The PermissionEvaluator could be generic and applied to several entity types if you define an Owned interface that models the ownership with a method like User getOwner() and that all your entity classes would implement.
See https://docs.spring.io/spring-security/site/docs/current/reference/html5/#el-access
The annotated method should be in a service rather than in a resource controller.
Also, UserService alone will not help you in finding the current authenticated user, you should use JHipster's SecurityUtils first and then ÙserService if you need more data about it.
Java or dotNet world is rich of open source frameworks and libraries. We all like to use Spring and Hibernate almost everywhere.
Everyone agrees that hibernate is a very handy tool.
What Hibernate can do ? well, Basically - Hibernate can track our domain objects changes and persist only modified data to database, that is it.
Basically, That is everything we want. I want to load some records from database, do some modifications to them, and call transaction.commit(), and all modifications get persisted, instantaneously.
That is excelent, right !
But how about web world ? In web applications database session must be closed.
I cannot load some domain objects and wait for user to do modifications through HTTP, and persist those objects after modifications.
We have to use detached objects or DTO. How it works ?
User makes modifications in HTML browser, spring Mvc automatically thransfers those HTML modifiactions to our customized DTO objects using MVC model binding,
then we do some programming effort to transfer modifications from DTO objects to hibernate domain objects and only then we persist them.
For example - we have a web form that updates Customer address, and another form which updates customer details.
We must have two different business layer methods - UpdateAddress() and UpdateDetails(), both methods must accept some kind of DTO,
one represents address information, the other represents details infprmation.
We also have custom logic that transfers data from those 2 DTO to the domain class 'Customer'.
Yes, of course, instead of DTO objects we could reuse our domain classes. But it does not make it simpler.
In both cases we will still have to implement custom logic that transfer modifications to persistent objects,
I cannot persist detached object rightaway, because usually domain classes have lots and lots of properties representing numerous relations, for ex. Customer has - Orders property. When I update customer address I don't want to update its orders.
Is there a beautifull universal way to mapping modifications from mvc model to domain objects without writing a lot of custom code and without risk of overwriting too many fields ?
It's good practice to have a data access layer, which translates into having a repository for each domain object / entity. Furthermore, all repositories share common code so you you naturally have an abstract repository:
public abstract class AbstractRepository<E extends BaseModel> implements Repository<E> {
#PersistenceContext
private EntityManager entityManager;
private Class<E> entityClass;
public AbstractRepository(Class<E> entityClass) {
this.entityClass = entityClass;
}
protected EntityManager getEM() {
return entityManager;
}
protected TypedQuery<E> createQuery(String jpql) {
return createQuery(jpql, entityClass);
}
protected <T> TypedQuery<T> createQuery(String jpql, Class<T> typeClass) {
return getEM().createQuery(jpql, typeClass);
}
#Override
public E merge(E entity) {
return getEM().merge(entity);
}
#Override
public void remove(E entity) {
getEM().remove(entity);
}
#Override
public E findById(long id) {
return getEM().find(entityClass, id);
}
}
It's also good practice to have a service layer where you are to create, update and delete instances of an entity (where you could pass through a DTO to the create and update methods if you so desire).
...
#Inject
private CustomerRepository customerRepository;
public Customer createCustomer(CustomerDto customerDto) {
Customer customer = new Customer();
customer.setEmail(customerDto.getEmail());
...
return customerRepository.merge(customer);
}
public Customer updateCustomerAddress(Customer customer, String address) {
customer.setAddress(address);
return customerRepository.merge(customer);
}
...
So it's up to you how many update methods you want. I would typically group them into common operations such as updating the customer's address, where you would pass the customer Id and the updated address from the front end (probably via ajax) to your controller listening on a specific endpoint. This endpoint is where you would use the repository to find the entity first by Id and then pass it to your service to do the address update for example.
Lastly you need to ensure that the data actually gets persisted, so in Spring you can add the #Transactional annotation either to you Spring MVC controller or to your service that does the persisting. I'm not aware of any best practices around this but I prefer adding it to my controllers so that you're always guaranteed to have a transaction no matter what service you are in.
This is a Spring Security question.
In my application, I have a User entity as a domain object. This object contains implementation to support Spring UserDetails object. The authentication (login/logout) process works fine.
The challenge is that I need to extract that object from the session to make 'business logic' decisions in my code.
I've been reading about querying SecurityContextHolder, but frankly, I still don't know what is the best approach, given that multiple Spring versions seem to be a factor in those discussions. Also, the Principal object isn't a solution for me, as it does not seem to contain any access level or role information.
Below is a simple controller to illustrate my challenge. It has my User domain object hardcoded. I need to replace that block with code that will obtain the User object from Spring Security session. I'm looking for the best way to do this within Spring 3.
Can I get this object as my domain object or do I need to get it as Spring UserDetails object and manually convert it?
Can this Security context lookup be injected somehow into my controller?
public class HomeController {
#RequestMapping(value="/home.html", method=RequestMethod.GET)
public ModelAndView getHomePage(Map<String, Object> model) {
// Get current user
User currentUser=new User();
currentUser.setUserName("Admin");
currentUser.setAccessLevel(UserAccessLevel.ADMINISTRATOR);
// Construct HomePage bean
HomeBean bean=new HomeBean();
bean.setCurrentUserName(currentUser.getUserName());
// Construct list of catalogs
Collection<String> catalogList=new ArrayList<String>();
catalogList.add("articles");
catalogList.add("files");
catalogList.add("comments");
if(currentUser.hasAdministratorAccessLevel()) {
catalogList.add("users");
}
bean.setCatalogList(catalogList);
// Construct and return ModelAndView
ModelAndView mav=new ModelAndView();
mav.setViewName(WebView.HOME_PAGE.getViewName());
mav.addObject(bean.getBeanId(), bean);
return mav;
}
=== Update 2012-01-07 ======================================================
I'm working with Luke's suggestion. The method that gets UserDetails from session and converts it to a returned my domain User object is in my UserService.
Here's my controller:
#Controller
public class HomeController {
#Autowired
private UserService userService;
#RequestMapping(value="/home.html", method=RequestMethod.GET)
public ModelAndView getHomePage(Map<String, Object> model) {
// Construct HomePage bean
HomeBean bean=new HomeBean();
User currentUser=userService.getCurrentlyAuthenticatedUser();
bean.setCurrentUserName(currentUser.getUserName());
And here's key code from UserServiceImpl.getCurrentlyAuthenticatedUser():
#Override
public User getCurrentlyAuthenticatedUser() {
User currentUser=new User();
Authentication a = SecurityContextHolder.getContext().getAuthentication();
UserDetails currentUserDetails = (UserDetails) a.getPrincipal();
if(currentUserDetails==null) {
return currentUser;
}
currentUser.setUserName(currentUserDetails.getUsername());
This works but am I doing this right? Feedback much appreciated. I am still unable to retrieve my User domain object from the session. I'm retrieving Spring's UserDetails object and with it constructing my domain User object but in the process some information is lost.
Normally, the principal object contained in the successful Authentication will be an instance of your user object. So, for a quick solution, use
Authentication a = SecurityContextHolder.getContext().getAuthentication();
User currentUser = (User)a.getPrincipal();
But also (once you get that working), you might want to look at the answer I just gave (to a similar question) on how to inject a custom security context accessor.
Spring also provides an annotation #AuthenticationPrincipal, it is used to resolve Authentication.getPrincipal(). It can be used like below...
public ResponseEntity<UserProfileResponse>UserProfile(#AuthenticationPrincipal JwtAuthenticationToken principal){