Add role to a method - spring

I use Spring Boot with Spring Security.
To create an new user, the "creating" user should be an admin.
But if the "creating" user only has the standard role...
#PreAuthorize("hasRole('admin')")
#RequestMapping(value = "/users", method = RequestMethod.POST)
public Long createUser(#RequestBody #Valid final UserAppDto user) {
return userService.save(user);
}
...the result is that a non-admin user is able to create a new user.
In my UserApp class, for the role, I have:
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private Set<Role> roles = new HashSet<>();
My main application
#EntityScan(basePackageClasses = {Application.class, Jsr310JpaConverters.class})
#SpringBootApplication
#EnableCaching
#EnableScheduling
public class Application implements SchedulingConfigurer{
#Bean
public WebSecurityConfigurerAdapter webSecurityConfigurerAdapter() {
return new ApplicationSecurity();
}
}
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
}

Do not use pre-post annotations on controlers. They use AOP under the hood, so they do not work on controlers unless they implement interfaces or unless you use proxy-target-class=true, which may have other drawbacks (*).
This annotations should be used on services, because service beans are generaly bound in controlers as interfaces and so the AOP machinery will work as expected.
The spring-security way for limiting access to url is via http security, not via method security.
(*) in particular, never mix class proxying and JDK proxying in same application...

Related

How to get Resttemplate Bean in Entity class using autowired

i am trying to write a method in my Entity class before populating the properties of entity class. To Do that i need to call a rest service . I like to bring my resttemplate into my Entity class using Autowired . Is that something possible , If yes how we can do that .
#Entity
#Table(name = "IC_ORDER")
public class ICOrder implements Serializable {
#Id
#GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
#GeneratedValue(generator = "UUIDGenerator")
#Type(type = "uuid-char")
UUID id;
#Transient
#Autowired
private RestTemplate restTemplate;
#SneakyThrows
int start(Context context,String userId) {
//restTemplate.postForEntity // Trying to call a rest service here . But getting
restTemplate as null , Want to avoid new Resttemplate here.
}
This is the code in Config class
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
Entities belong to the domain layer. It is better to make them not depends on any external framework or library stuff such as RestTemplate as much as you can. Also, having the domain layer interacts with the external systems directly just does not feel right to me. They should be done in a higher level layer such as service layer. So it is better to have a separate service class like OrderService to do such logic. You can then easily inject the RestTemplate into this service class.
If you insist doing it inside the domain model , please checkout this for how to use #Configurable and AspectJ to do it.

Mock security context for JPA integration test

I'm trying to test a spring JPA repository interface to ensure my mappings are correct. My entity extends a base entity which is annotated with..
#EntityListeners(BaseEntityEventListener.class)
#MappedSuperclass
public abstract class BaseEntity {...
The event listener populates some audit properties..
public class BaseEntityEventListener {
#PrePersist
public void onPreInsert(BaseEntity baseEntity){
MyUserPrincipal principal = (MyUserPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String username = principal.getUsername();
baseEntity.setCreationUser(username);
Timestamp ts = new Timestamp(System.currentTimeMillis());
baseEntity.setCreationDate(ts);
}...
This is ok but when I want to test the repository I get a null pointer for the SecurityContextHolder.
#RunWith(SpringRunner.class)
#SpringBootTest
public class RepositoryTest {
#Autowired private MyRepository myRepo;
#Test
public void testSaveEntity() throws Exception {
Entity entity = new Entity(TEST_ID);
myRepo.save(entity);
}...
When event listener class is called from test the security context is not set. I have tried using #WithMockUser but this doesn't seem to work. Could I maybe wrap call to security context in a service and then somehow mock this call in my integration test. How do I set mock on entity listener if this is an option. When I use #CreatedBy and #CreatedDate the security context is not an issue but I need to manually use #PreInsert for a separate reason.
What is the error you got? Maybe because it cannot get the user.
You can try:
val userPrincipal =
new org.springframework.security.core.userdetails.User(username,
"",
true,
true,
true,
true,
authorities);
val auth = new TestingAuthenticationToken(userPrincipal, null, authorities);
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);

How to keep a data between requests at web service?

Suppose I develop a ticket order web service. There are some steps to order a ticket and need to keep some users data between the steps.
Suppose I use Spring (Boot) technology stack and MVC
How is better to implement it?
Use stateless REST and move the date back and forth from step to step using cookies?
Store it in session context?
Use stateful beans (what are they like in Spring? Prototype? )
Use some stateful protocol, like SOAP (is it stateful?)
It depends.
1 If you want to use multiple instances of your web service (for balance load, etc) then your choice is a stateless REST and token-based authentication
2 If you don't need this functionality you can store your session information in MVC Model (It will put it in session, anyway)
#RestController
#SessionAttributes("armUserSession")
public class SessionController {
#Autowired
private LoginService loginService;
#ModelAttribute("armUserSession")
public ArmUserSession getArmUserSession() {
return new ArmUserSession();
}
#CrossOrigin
#RequestMapping({"/login"})
public ArmUserSession login(#ModelAttribute("armUserSession") ArmUserSession userSession,
Model model,
#RequestParam(required = false) String login,
#RequestParam(required = false) String password) {
if (!userSession.isLoggedIn()) {
userSession = loginService.login(login, password);
model.addAttribute("armUserSession", userSession);
}
return userSession;
}
#CrossOrigin
#RequestMapping({"/logout"})
public ArmUserSession logout(SessionStatus status) {
status.setComplete();
return new ArmUserSession();
}
}
3 You can use session scoped beans too, but it is a little more complicated.
By default Spring beans are singletons. When you want to use session scoped bean (they are not singletons) in singleton your need a proxy.
#Service
public class LoginServiceImpl implements LoginService {
#Autowired
private ArmUserSessionProxy armUserSessionProxy;
#Override
public ArmUserSession login(String login, String password) {
ArmUserSession armUserSession = armUserSessionProxy.getArmUserSession();
...................................
}
#Component
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ArmUserSessionProxy {
private ArmUserSession armUserSession = new ArmUserSession();
public ArmUserSession getArmUserSession() {
return armUserSession;
}
}

Authentication using request scoped context with Spring

I am trying to create custom "userContext" as a SpringBean with request scope but I am unable to do so. Basically I have a Jersey REST api and I want to do authentication and authorization using my custom filters in which I autowire my "userContext" bean. The process looks like this:
REST API called (I expect Spring to create new instance of userContext bean)
AuthenticationFilter autowires new instance of userBean and populates it
AuthorizationFilter autowires the same instance which is populated now and authorize the user
When i first call the REST api (after server restart), it works as expected, but any other call fails, because AutorizationFilter gets an empty instance of userBean. I expect some fundamental misunderstanding of scopes on my part.
Btw: I'd like to avoid using ThreadLocal directly since Request scope should take care of it
I would like to know, why authorizationFilter doesn't see the populated version of the userBean and why the first call works. Thanks in advance for any help.
Just some parts of the code:
#Secured({Role.ADMIN}) //custom annotation
#GET
#Path("{id}")
public Response getUserById(#PathParam("id") Long id) throws IOException, MainException {
#Secured //custom annotation
#Provider
#Priority(Priorities.AUTHENTICATION)
#Scope(value="request", proxyMode= ScopedProxyMode.TARGET_CLASS)
public class AuthenticationFilter implements ContainerRequestFilter
#Autowired
private User userContext;
#Secured //custom annotation
#Provider
#Priority(Priorities.AUTHORIZATION)
#Scope(value="request", proxyMode= ScopedProxyMode.TARGET_CLASS)
public class AuthorizationFilter implements ContainerRequestFilter {
#Autowired
private User userContext;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#Component
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class User extends ModelBase implements Serializable {
Since Providers are not request scoped, I had to inject whole ApplicationContext so I could directly modify the right instance of userContext bean (which is request scoped). Basically I did something like this in both filters:
#Autowired
private ApplicationContext applicationContext;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
User userBean=applicationContext.getBean(User.class);
....
userBean.setSomething("aaa");
Then I could just autowire such bean in my REST Resources, because they are request scoped by defaul:
#Component
#Api(value="/users", description = "Endpoint for Users listing")
#Consumes({MediaType.APPLICATION_JSON, Constants.API_VERSIONS.V1_HEADER_XML, Constants.API_VERSIONS.V1_HEADER_JSON})
#Produces({MediaType.APPLICATION_JSON, Constants.API_VERSIONS.V1_HEADER_XML, Constants.API_VERSIONS.V1_HEADER_JSON})
#Path("/users")
public class UserResource {
private static final Logger logger = LoggerFactory.getLogger(UserResource.class);
#Autowired
private User authenticatedUser;
This solution should be viable and resistent against thread race conditions etc.

Spring Boot JPA #Transactional #Service does not update, but #Transactional in controller does

I have a very basic Spring Boot/JPA stack app, with a controller, service layer, and repository that does not persist updates as I understand it should.
A trivial Entity:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
protected Customer() {}
public Customer(String name) { this.name = name; }
// standard getters,setters //
}
A trivial Repository:
#Repository
public interface CustomerRepository extends CrudRepository<Customer, Long> {}
A simple Service layer:
// If the service is #Transactional and the controller is not, the update does NOT occur
#Transactional
#Service
public class CustomerService {
private static final Logger LOG = getLogger(CustomerService.class);
#Autowired
private CustomerRepository customerRepository;
boolean updateCustomerName(Long id, String name) {
Customer customer = customerRepository.findOne(id);
if (customer == null) { return false; }
// Modifies the entity
customer.setName(name);
// No explicit save()
return true;
}
}
And a REST controller that uses it all:
// If the controller is #Transactional and the service is not, the update occurs
#RestController
#RequestMapping("/mvc")
public class CustomerController {
#Autowired
private CustomerService customerService;
#RequestMapping(path = "{id}", method = RequestMethod.PUT)
public ResponseEntity updateCustomerName(#PathVariable Long id, #RequestParam("name") String name) {
customerService.updateCustomerName(id,name);
return ResponseEntity.noContent().build();
}
}
These are wired together with a simple one-liner SpringBootApplication
I have SQL debug logs enabled and see the selects, update, etc.
With the code above: When the service method is invoked by the controller, the modified entity is not persisted. SQL logs show the select of the entity but no update.
There is also no update if nothing is marked #Transactional
However, simply by moving the #Transactional annotation from the service class to the controller class, the SQL update does occur.
If I add an explicit customerRepository.save(customer) to the service method, the update also occurs. But my understanding is that the ORM should automatically save modified persistent entities.
I'm sure the issue has something to do with the EntityManager lifecycle in the web request, but I'm puzzled. Do I need to do additional configuration?
Complete example at https://github.com/monztech/SO-41515160
EDIT: This was solved, see below. Per the Spring spec #Transactional does not work in package-private methods and mistakenly did not make the update service method public.
The update will occur if the method is public and the service class has the #Transactional annotation.
I do have another question, however. Why is the #Transactional annotation necessary? (the update does not occur without it) Shouldn't the entity manager still persist the object because of the open session in view mechanism that Spring uses, independent of any transaction?
Make your updateCustomerName method public.

Resources