Spring #PreAuthorize passes null to Service [duplicate] - spring

This question already has an answer here:
Spring - SpEL evaluates entity argument as null reference in #PreAuthorize("hasPermission")
(1 answer)
Closed 5 years ago.
I have an issue with #PreAuthorize and a sevice that checks if the athenticated user may access the searched item.
The one service callDistributionRequest that gets the item is working fine - #PreAuthorize recieves and passes the right distId. The other one updateDistributionRequestExportFileName gets also the right distId and passes it to the distributionRequestService. On the method userBelongsToRecipientOfTheDistributionRequest distId comes as a null
The Spring RestController with the two web services
#RestController
#RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class DistributionRequestRESTController {
#Autowired
private #Getter #Setter DistributionRequestService distributionRequestService;
private final Logger log = LoggerFactory.getLogger(this.getClass());
private String logResponse = " - response: ";
#Autowired
public DistributionRequestRESTController(DistributionRequestService distributionRequestService) {
this.distributionRequestService = distributionRequestService;
}
#RequestMapping(value = Consts.URLDISTRIBUTIONREQUEST + Consts.URLDISTREQID)
public DistributionRequest callDistributionRequest(#PathVariable long distId) {
String loginfo = "get distribution with id: " + distId;
//log.info(loginfo);
DistributionRequest found = distributionRequestService.findOne(distId);
log.info(loginfo + logResponse + JSONParser.toJsonString(found));
return found;
}
#RequestMapping(method = RequestMethod.POST, value = Consts.URLDISTRIBUTIONREQUEST + Consts.URLDISTREQID + Consts.URLUPDATE + Consts.URLFILENAME)
public DistributionRequest updateDistributionRequestExportFileName(
#PathVariable long distId,
#RequestBody String fileName,
#AuthenticationPrincipal UserDetails user) {
String loginfo = user.getUsername() + " try to update filename with : " + fileName;
//log.info(loginfo);
DistributionRequest updated =
distributionRequestService.updateExportFilename(distId, fileName);
log.info(loginfo + logResponse + JSONParser.toJsonString(updated));
return updated;
}
}
The Service interface:
public interface DistributionRequestService {
#PreAuthorize(value = "hasAnyAuthority('USER', 'ADMIN') and #distributionRequestOwnerService.userBelongsToRecipientOfTheDistributionRequest(#distId)")
DistributionRequest findOne(Long distId);
#PreAuthorize(value = "hasAnyAuthority('USER', 'ADMIN') and #distributionRequestOwnerService.userBelongsToRecipientOfTheDistributionRequest(#distId)")
DistributionRequest updateExportFilename(Long distId, String filename);
}
And the class that checks if the user may access the searched item
#Service(value = "distributionRequestOwnerService")
public class DistributionRequestOwnerServiceImpl implements DistributionRequestOwnerService {
#Autowired
private AccountService accountService;
#Autowired
private DistributionRequestsRepository distributionRequestsRepository;
#Override
public boolean userBelongsToRecipientOfTheDistributionRequest(Long distId) {
return userBelongsToRecipientOfTheDistributionRequest(distId, null);
}
#Override
public boolean userBelongsToRecipientOfTheDistributionRequest(Long distributionRequestId, String username) {
DistributionRequest distributionRequest = distributionRequestsRepository.findOne(distributionRequestId);
ServiceAccount currentUser;
if (username == null)
currentUser = accountService.getCurrentUser();
else
currentUser = accountService.findByUsername(username);
if (distributionRequest != null
&& distributionRequest.getRecipientId() == currentUser.getRecipientId())
return true;
throw new AercacheWSException(Consts.EXCEPTIONMISSINGELEMENTORPERMITION);
}
}
Any ideas?
thanks in advance

Found the solution duplicate to
as #teppic pointed parameter in interfaces should be annotated.
public interface DistributionRequestService {
#PreAuthorize(value = "hasAnyAuthority('USER', 'ADMIN') and #distributionRequestOwnerService.userBelongsToRecipientOfTheDistributionRequest(#distId)")
DistributionRequest findOne(#Param("distId") Long distId);
#PreAuthorize(value = "hasAnyAuthority('USER', 'ADMIN') and #distributionRequestOwnerService.userBelongsToRecipientOfTheDistributionRequest(#distId)")
DistributionRequest updateExportFilename(#Param("distId") Long distId, String filename);
}

Related

How to invoke #Service from another project with REST call in Spring?

I need to use #Service from another Spring project. I will need a REST call to invoke it but how am I suppose to do it?
This is the service:
#Service
public class LocationsService implements ILocationsService {
private final Logger logger = LoggerFactory.getLogger("LocationsService");
private final ILocationRepository locationRepository;
private final IEvseRepository evseRepository;
private final IConnectorRepository connectorRepository;
#PersistenceContext
private EntityManager entityManager;
#Autowired
public LocationsService(ILocationRepository locationRepository, IEvseRepository evseRepository, IConnectorRepository connectorRepository, EntityManager entityManager) {
this.locationRepository = locationRepository;
this.evseRepository = evseRepository;
this.connectorRepository = connectorRepository;
this.entityManager = entityManager;
}
public Location getLocation(String countryCode, String partyId, String id) {
return locationRepository.findByCountryCodeAndPartyIdAndId(countryCode, partyId, id);
}
public Location deleteLocation(String countryCode, String partyId, String id) {
Location location = locationRepository.findByCountryCodeAndPartyIdAndId(countryCode, partyId, id);
if (location == null) {
logger.info("Location does not exist.");
return null;
}
locationRepository.deleteById(location.getLocId());
return location;
}
I need to call the service in this controller. The controller is in a different project:
#RestController
#RequestMapping(value = "/locations", produces = MediaType.APPLICATION_JSON_VALUE)
#Api(tags = "Locations management")
public class LocationController {
#Autowired
private LocationsService locationsService;
#RequestMapping(method = RequestMethod.GET , produces = MediaType.APPLICATION_JSON_VALUE)
#ApiOperation(value = "Get Locations", notes = "Get locations", nickname = "getLocations",
authorizations = #Authorization(value = "Bearer"))
public ResponseEntity<List<LocationDto>> getLocations() {
List<LocationDto> locations = new ArrayList<>();
return new ResponseEntity<>(locations, HttpStatus.OK);
}
}
I searched for solutions but found nothing helpful and will appreciate any help.
Thank you!

How to mock BindingResult in Spring Boot test?

In a TestClass I added #Import(MandatairesFormValidator.class)
When I mock the controller I can go inside the controller but I can't access the method validate.validate() as suggested here: How to mock BindingResult in Spring Boot Test
The controller is:
#PatchMapping(value = "/{id}", consumes = APPLICATION_JSON_UTF8_VALUE, produces = APPLICATION_JSON_UTF8_VALUE)
ResponseEntity<Object> modifierDonnees(#PathVariable final Long id,
#Valid #RequestBody final ModificationUI modificationUI, BindingResult bindingResult) {
validator.validate(modificationUI, bindingResult);
if (bindingResult.hasErrors()) {
List<String> errors = bindingResult.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.toList());
throw new MandataireValidationException(errors);
}
}
The UnitTestController:
#RunWith(MockitoJUnitRunner.class)
public class ControllerTest {
ResultActions resultActions = mockMvc.perform(patch(URL_RACINE_CTRL + "/1").contentType(APPLICATION_JSON_UTF8_VALUE)
.content(
"{\"modificationMandataire\":{\"mandataireOrigine\":{}, " +
"\"mandataireSoustraction1\":{}, " +
"\"mandataireSoustraction2\":{}}}")).andExpect(MockMvcResultMatchers.status().isBadRequest());
using: #Import(MandatairesFormValidator.class) the validator in the Controller is null ;
using: #Mock
private MandatairesFormValidator mandatairesFormValidator; it skips the 'validator.validate()' method -->
BindingResult bindingResult = mock(BindingResult.class);
when(bindingResult.hasErrors()).thenReturn(true);
The validator:
#Component
public class MandatairesFormValidator implements Validator {
public static final String international_number="(0{1,2}|\\+\\d{2,})(-|\\.|\\s*|\\d*)*";
public boolean supports(Class<?> clazz) {
return ModificationUI.class.isAssignableFrom(clazz);
}
private Boolean isValidNatel(String telephone){
return Objects.nonNull(telephone) && (telephone.matches(international_number));
}
public void validate(Object o, Errors errors) {
ModificationUI modificationUI = (ModificationUI) o;
ModificationMandataireUI modificationMandataire = modificationUI.getModificationMandataire();
MandataireSoustractionUI mandataireSoustractionUI1 = modificationAffaireMandataire.getMandataireSoustraction1();
MandataireSoustractionUI mandataireSoustractionUI2 = modificationAffaireMandataire.getMandataireSoustraction2();
Boolean isValidNatel1 = (Objects.nonNull(mandataireSoustractionUI1) && isValidNatel(mandataireSoustractionUI1.getTelephone()) || Objects.isNull(mandataireSoustractionUI1));
Boolean isValidNatel2 = (Objects.nonNull(mandataireSoustractionUI2) && isValidNatel(mandataireSoustractionUI2.getTelephone()) || Objects.isNull(mandataireSoustractionUI2));
if ( !isValidNatel1 ) {
String telephone1=mandataireSoustractionUI1.getTelephone();
String msg1= Objects.nonNull(telephone1) ? telephone1 : "";
errors.reject("telephone", "Téléphone 1 " + msg1 +" pas valide");
}
if ( !isValidNatel2 ) {
String telephone2=mandataireSoustractionUI2.getTelephone();
String msg2= Objects.nonNull(telephone2) ? telephone2 : "";
errors.reject("telephone", "Téléphone 2" + msg2 + " pas valide");
}
}
}`
I tried using

how can injection service class depending on user's area code

I want call get bus/bus station information open api from area code in user's address.
Area is city, city provide different bus information api each other.
TrafficController.java
#Controller
public class TrafficController {
....
#Autowired
public UserService userService;
public TrafficService trafficService;
....
#RequestMapping(value="/api/traffic/stations")
public List<....> getNearStations(HttpServerletRequest req) {
String userId = SessionAttrs.getUserId(req.getSession());
User user = userSerivce.getUser(userId);
trafficService = (TrafficService) Class.forName("com.myservice.service.impl.TrafficService_"+user.house.apt.address.area_code + "_Impl").newInstence();
return trafficService.getStations(user.house.apt.latitude, user.house.apt.longitude, 300, 2);
}
}
TrafficService_11_Impl.java
#Service
public Class TrafficService_11_Impl implemnet TrafficService {
#Autowired
TrafficRepository trafficRepository;
#Override
public List<Station> getStations(double lat, double lng, int ratius, int retryCount) {
final String cacheKey = "getStations " + lat + " " + lng + " " + radius;
TrafficeCache cache = trafficRepository.fineOne(chcheKey); // run time NullPointerException, trafficRepository is null
if ( null == cache) {
...
get area code 11 api call logic
...
}
...
return ...;
}
}
TrafficRepository.java
public interface TrafficRepository extends JpaRepository<TrafficCache, String> {
}
This code occured runtime NullPointerException at
TrafficService_11_Impl.java at
TrafficeCache cache = trafficRepository.fineOne(chcheKey);
run time NullPointerException, trafficRepository is null
Otherly in TrafficController.java
#Autowired
public TrafficService trafficService
and drop code
trafficService = (TrafficService) Class.forName("com.myservice.service.impl.TrafficService_"+user.house.apt.address.area_code + "_Impl").newInstence();
was correct result
How can injection service class depending on user's area code in String boot?
TrafficController.java
#Controller
public class TrafficController {
....
#Autowired
private ApplicationContext context;
#Autowired
public UserService userService;
public TrafficService trafficService;
....
#RequestMapping(value="/api/traffic/stations")
public List<....> getNearStations(HttpServerletRequest req) {
String userId = SessionAttrs.getUserId(req.getSession());
User user = userSerivce.getUser(userId);
trafficService = (TrafficService) context.getBean(Class.forName("com.myservice.service.impl.TrafficService_"+user.house.apt.address.area_code + "_Impl"));
return trafficService.getStations(user.house.apt.latitude, user.house.apt.longitude, 300, 2);
}
}
It's correct result

Java: GroupSequenceProvider for Validation, object is null in getValidationGroups method

This is what I am trying to achieve:
I have an update request object and user is allowed to do Partial Updates. But I want to validate the field only if it is in the request body. Otherwise, it is OK to be null. To achieve this, I am using GroupSequenceProvider to let the Validator know what groups to validate. What am I doing wrong here? If there is a blunder, how do I fix it?
Documentation: https://docs.jboss.org/hibernate/validator/5.1/reference/en-US/html/chapter-groups.html#example-implementing-using-default-group-sequence-provider
#GroupSequenceProvider(UpdateUserRegistrationGroupSequenceProvider.class)
public class UpdateUserRegistrationRequestV1 {
#NotBlank(groups = {EmailExistsInRequest.class})
#Email(groups = {EmailExistsInRequest.class})
#SafeHtml(whitelistType = SafeHtml.WhiteListType.NONE, groups = {EmailExistsInRequest.class})
private String email;
#NotNull(groups = {PasswordExistsInRequest.class})
#Size(min = 8, max = 255, groups = {PasswordExistsInRequest.class})
private String password;
#NotNull(groups = {FirstNameExistsInRequest.class})
#Size(max = 255, groups = {FirstNameExistsInRequest.class})
#SafeHtml(whitelistType = SafeHtml.WhiteListType.NONE, groups = {FirstNameExistsInRequest.class})
private String firstName;
// THERE ARE GETTERS AND SETTERS BELOW
}
Group Sequence Provider Code:
public class UpdateUserRegistrationGroupSequenceProvider implements DefaultGroupSequenceProvider<UpdateUserRegistrationRequestV1> {
public interface EmailExistsInRequest {}
public interface PasswordExistsInRequest {}
public interface FirstNameExistsInRequest {}
#Override
public List<Class<?>> getValidationGroups(UpdateUserRegistrationRequestV1 updateUserRegistrationRequestV1) {
List<Class<?>> defaultGroupSequence = new ArrayList<Class<?>>();
defaultGroupSequence.add(Default.class);
defaultGroupSequence.add(UpdateUserRegistrationRequestV1.class);
if(StringUtils.hasText(updateUserRegistrationRequestV1.getEmail())) {
defaultGroupSequence.add(EmailExistsInRequest.class);
}
if(StringUtils.hasText(updateUserRegistrationRequestV1.getPassword())) {
defaultGroupSequence.add(PasswordExistsInRequest.class);
}
if(StringUtils.hasText(updateUserRegistrationRequestV1.getFirstName())) {
defaultGroupSequence.add(FirstNameExistsInRequest.class);
}
return defaultGroupSequence;
}
}
I am using Spring MVC, so this is how my controller method looks,
#RequestMapping(value = "/{userId}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.NO_CONTENT)
public void updateUser(#PathVariable("userId") Long userId,
#RequestBody #Valid UpdateUserRegistrationRequestV1 request) {
logger.info("Received update request = " + request + " for userId = " + userId);
registrationService.updateUser(userId, conversionService.convert(request, User.class));
}
Now the problem is, the parameter "updateUserRegistrationRequestV1" in the UpdateUserRegistrationGroupSequenceProvider.getValidationGroups method is null. This is the request object that I am sending in the request body and I am sending email field with it.
What am I doing wrong?
I too went through the same issue ,and hopefully solved it
You just have to check the object is null and put all your conditions inside it.
public List<Class<?>> getValidationGroups(Employee object) {
List<Class<?>> sequence = new ArrayList<>();
//first check if the object is null
if(object != null ){
if (!object.isDraft()) {
sequence.add(Second.class);
}
}
// Apply all validation rules from default group
sequence.add(Employee.class);
return sequence;
}

Assitance regarding JUnit Testing for Spring Controller Dao

I am new to Junit.Please help me to test Spring hibernate Controller with ContentType is application/json
Below is my Controller
#Controller
#RequestMapping(value="/users")
public class UserServiceImpl implements UserService{
private static Logger logger = Logger.getLogger(UserService.class);
private UserDao userDao;
#Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
#RequestMapping(method = RequestMethod.POST,headers = "content-type=application/json")
#ResponseBody
public long addUser(#RequestBody UserForm user) {
logger.info("Creating new user {}"+ user);
return userDao.create(user);
}
#RequestMapping(value = "/{userId}", method = RequestMethod.GET)
#ResponseBody
public User findUser(#PathVariable(value = "userId") String userId) {
logger.info("Reading user with id {}"+ userId);
User user = userDao.find(userId);
Validate.isTrue(user != null, "Unable to find user with id: " + userId);
return user;
}
#RequestMapping(value = "/{userId}", method = RequestMethod.PUT,headers = "content-type=application/json")
#ResponseStatus(value = HttpStatus.NO_CONTENT)
public void updateUser(#PathVariable(value = "userId") String userId, #RequestBody UserForm user) {
logger.info("Updating user with id {} with {}"+ userId +"->"+ user);
Validate.isTrue(userId.equals(user.getUserId()), "userId doesn't match URL userId: " + user.getUserId());
userDao.update(user);
}
#RequestMapping(value = "/{userId}", method = RequestMethod.DELETE)
#ResponseStatus(value = HttpStatus.NO_CONTENT)
public void deleteUser(#PathVariable(value = "userId") String userId) {
logger.info("Deleting user with id {}"+ userId);
userDao.delete(userId);
}
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
public List<User> list() {
logger.info("Listing users");
return new ArrayList<User>(userDao.getUsers());
}
}
Can any one Send me the Junit Test case for Any one of the CRUD operations.
Thanks in Advance
Srikanth
If you just want to test your controller, then I would say that mock the DAO. You don't have to care about content types and such because Spring takes care of them. You are interested what the controller method is returning. If you want to test your DAO that User actually is saved to database, that's another story.
But just for testing that controller does what it is supposed to, something like this for example. Example uses EasyMock. I haven't compiled this example so it might have typos.
import static org.easymock.EasyMock.createNiceMock;
public class ControllerTest {
private UserServiceImpl userService;
private UserDao userDaoMock;
#Before
public void setup() {
userDaoMock = createNiceMock(UserDao.class);
userService = new UserServiceImpl();
userSerivce.setUserDao(userDaoMock);
}
#Test
public void testAddUser() {
UserForm userForm = new UserForm();
long expectedResult = 5L;
expect(userDaoMock.create(userForm)).andReturn(expectedResult);
replay(userDaoMock);
long actualResult = userService.addUser(userForm);
verify(userDaoMock);
assertEquals(expectedResult, actualResult);
}
}

Resources