Spring WEB MVC + produces = MediaType.IMAGE_JPEG_VALUE + #ResponseStatus(HttpStatus.FORBIDDEN) = HTTP status 406 - spring

I'm writing some code for user authorization. For users with 2 factored authorization enabled I'm writing code for 2fa secret update:
#RestController
public class CurrentUserController {
#PostMapping(value = "update-2fa-secret", produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] update2FaSecret() {
UserEntity user = userRepository.findOne(currentUserId);
if (user.is2FaEnabled() != Boolean.TRUE)
throw new HttpForbiddenException("2fa disabled for current user");
String secret = createNewSecret();
user.setSecret2Fa(secret);
userRepository.save(user);
return createQRCode(secret, user.getEmail());
}
}
And Exception:
#ResponseStatus(HttpStatus.FORBIDDEN)
public class HttpForbiddenException extends RuntimeException {
............
}
And when Exception happens I get response from the server with 406 Http status and without body (content).
I don't understand why this happens and how to solve it. Can somebody explain it to me please?

I've solved this issue in the next way:
#RestController
public class CurrentUserController {
#PostMapping(value = "update-2fa-secret", produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] update2FaSecret(HttpServletResponse response) {
UserEntity user = userRepository.findOne(currentUserId);
if (user.is2FaEnabled() != Boolean.TRUE) { //fix is here
response.setStatus(HttpStatus.FORBIDDEN.value()); //403
return new byte[0];
}
String secret = createNewSecret();
user.setSecret2Fa(secret);
userRepository.save(user);
return createQRCode(secret, user.getEmail());
}
}

Related

FeignException com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.springframework.http.ResponseEntity`

Any Help please !!
I receive this error when I'm calling my endpoint which call Feign in the background :
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of
`org.springframework.http.ResponseEntity` (no Creators, like default constructor, exist): cannot deserialize
from Object value (no delegate- or property-based Creator)
at [Source: (BufferedReader); line: 1, column: 2]
This is my endpoint inside Controller :
#RestController
#RequestMapping(Routes.URI_PREFIX)
public class CartoController {
#Autowired
private ReadCartographyApiDelegate readCartographyApiDelegate;
#GetMapping(value = "/cartographies/{uid}", produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseWrapper<ReadCartographyResponse> readCarto(HttpServletRequest request,
#PathVariable(name = "uid") String uid) {
ResponseEntity<ReadCartographyResponse> result ;
try {
result = readCartographyApiDelegate.readCartography(uid);
}catch (Exception e){
throw new TechnicalException("Error during read Carto");
}
return responseWrapperWithIdBuilder.of(result.getBody());
}
}
Interface ReadCartographyApiDelegate generated automatically by openApi from yaml file :
#javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "...")
public interface ReadCartographyApiDelegate {
default Optional<NativeWebRequest> getRequest() {
return Optional.empty();
}
default ResponseEntity<ReadCartographyResponse> readCartography(String uid) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
String exampleString = "null";
ApiUtil.setExampleResponse(request, "application/json", exampleString);
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
}
This my ReadCartoApiDelegateImpl which implements ReadCartographyApiDelegate interface :
#Service
public class ReadCartographyApiDelegateImpl implements ReadCartographyApiDelegate {
private EcomGtmClient ecomGtmClient;
public ReadCartographyApiDelegateImpl(EcomGtmClient ecomGtmClient) {
this.ecomGtmClient = ecomGtmClient;
}
#Override
public ResponseEntity<ReadCartographyResponse> readCartography(String uid) {
ResponseEntity<ReadCartographyResponse> response = ecomGtmClient.readCartography(uid);
return response;
}
}
This is the feign client :
#FeignClient(name = "ecomGtmSvc", url = "http://localhost/")
public interface EcomGtmClient {
#GetMapping(value = "/read-carto/{uid}")
ResponseEntity<ReadCartographyResponse> readCartography(#PathVariable("uid") String uid);
}
The problem is that ResponseEntity (spring class) class doesn't contain default constructor which is needed during creating of instance. is there Any config to resolve this issue ?
If you want access to the body or headers on feign responses, you should use the feign.Response class. ResponseEntity does not work with feign because it is not meant to. I think it is best if you just return Response from your feign client method. You should then be able to pass the body to the ResponseEntity instance in the Controller.
What is your reason to even use the response-wrapper, i can't really figure that out from your code?
Sadly I couldn't find any documentation on the Response class, but here's the link to the source on GitHub.
https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/Response.java
My Suggestion would be
#FeignClient(name = "ecomGtmSvc", url = "http://localhost/")
public interface EcomGtmClient {
#GetMapping(value = "/read-carto/{uid}")
ReadCartographyResponse readCartography(#PathVariable("uid") String uid);
}
#RestController
#RequestMapping(Routes.URI_PREFIX)
public class CartoController {
#Autowired
private ReadCartographyApiDelegate readCartographyApiDelegate;
#GetMapping(value = "/cartographies/{uid}", produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseWrapper<ReadCartographyResponse> readCarto(HttpServletRequest request,
#PathVariable(name = "uid") String uid) {
ReadCartographyResponse result ;
try {
result = readCartographyApiDelegate.readCartography(uid);
}catch (Exception e){
throw new TechnicalException("Error during read Carto");
}
// I don't know where you get the builder from, so I assume it does something import and is needed
return responseWrapperWithIdBuilder.of(result);
}
}
Of course you'd also have to change all intermediate classes.
The Response Output was the correct Object that I have to put, cause every time I need to check the status from my feign client endpoint to do différent logic
#FeignClient(name = "ecomGtmSvc", url = "http://localhost/")
public interface EcomGtmClient {
#GetMapping(value = "/read-carto/{uid}")
ReadCartographyResponse readCartography(#PathVariable("uid") String uid);
}

How to restrict access to the Spring MVC controller

I am writing a web service with an authorization and registration form. There are two types of users: regular and administrator. There is a controller that sends to the admin page at a given URL:
#Controller
public class ViewPageController {
#RequestMapping(value = "/admin", method = RequestMethod.GET)
public String sendAdminPage(){
return "AdminPage";
}
}
But ordinary users can also access this page. It is necessary that only those who logged in as an administrator get to the admin page. There are options for how this can be organized? Maybe save the logged in user in the session? (Preferably without Spring Security)
the easy way define a Aspect and A annotation.some code like this
#Inherited
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Authorize {
//
String[] value() default {};
}
AuthorizationAspect.java
#Slf4j
#Aspect
#Component
#RequiredArgsConstructor
public class AuthorizationAspect {
private final AuthorizationService authorizationService;
private final CacheUtil cacheUtil;
private static final String PRE = "AUTH";
#Before("#annotation(com.jin.learn.config.security.Authorize)")
public void checkPermission(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Long accountId = JWTUtil.getUserIdFromRequest(request);
Set<String> authorization = cacheUtil.getAllSet(PRE + accountId);
if(authorization==null){
authorization = authorizationService.findByAccountId(accountId);
cacheUtil.save(PRE + accountId, authorization);
}
Authorize authorize = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(Authorize.class);
String[] needAuthorization = authorize.value();
if (needAuthorization.length == 0) return;
if (authorization!=null && !authorization.isEmpty()) {
if (!authorization.containsAll(Arrays.asList(needAuthorization))){
throw new SystemException(ExceptionCode.NO_PERMISSION);
}
} else {
throw new SystemException(ExceptionCode.NO_PERMISSION);
}
}
}
use like this
#Authorize(value="needRight")
#RequestMapping(value = "/admin", method = RequestMethod.GET)
public String sendAdminPage(){
return "AdminPage";
}
besides,there are some security framework shiro and spring-security

Spring Rest Api Design For Following a User in Twitter Clone Appliction?

I Want to Design a Demo Twitter Clone Application where user can follow any other user . however i am doubting my rest api design . please suggest me am i right .
Can I pass followerId in url rather than passing it as requestbody as we already know followerId in Advance and server does not create followerId here ?
and if better option could be there like put/patch or any rest api design ?
Please suggest me better design if possible
Here JwtUser is Authenticated User
public class FollowerDto {
private Long followerId;
private boolean following;
public FollowerDto() {
}
public FollowerDto(Long followerId, boolean following) {
this.followerId = followerId;
this.following = following;
}
public boolean getFollowing() {
return following;
}
public void setFollowing(boolean following) {
this.following = following;
}
public Long getFollowerId() {
return followerId;
}
public void setFollowerId(Long followerId) {
this.followerId = followerId;
}
}
#PostMapping("/follower")
#ResponseStatus(HttpStatus.CREATED)
public StatusDto addFollower(#RequestBody #Valid final FollowerDto
followerDto, #CurrentUser final JwtUser user, final
HttpServletResponse response) {
RestPreconditions.checkRequestElementNotNull(followerDto);
RestPreconditions.checkArgumentCondition(followerDto.getFollowing());
return userService.addFollower(user, followerDto.getFollowerId(),
response);
}
// Service Layer
#Override
public StatusDto addFollower(final JwtUser jwtUser, final Long followerId, final HttpServletResponse response) {
final User follower = userRepository.findById(followerId).orElse(null);
ServicePreconditions.checkEntityExists(follower, "Follower does not exist with id " + followerId);
final User currentUser = userRepository.findByEmail(jwtUser.getEmail());
if (currentUser != null) {
ServicePreconditions.checkOKArgument(!currentUser.equals(follower));
final Set<User> existingFollowers = currentUser.getFollowers();
if (existingFollowers != null) {
existingFollowers.add(follower);
} else {
currentUser.setFollowers(Sets.<User>newHashSet(follower));
}
userRepository.save(currentUser);
final URI uri = ServletUriComponentsBuilder.fromCurrentRequestUri().path("/{idOfNewResource}").buildAndExpand(follower.getId()).toUri();
response.setHeader(HttpHeaders.LOCATION, uri.toASCIIString());
return new StatusDto("Follower Added Successfully to user having email " + jwtUser.getEmail());
}
return new StatusDto("Follower is not Added to user with email " + jwtUser.getEmail());
}

406 error always happening with spring rest controller

I'd like to have your opinion on a error always throwed in my spring boot rest controller. I got the following first controller accepting reading requests :
#RequestMapping(value="/read/{file:.+}" , method = RequestMethod.GET)
public ResponseEntity myFunction(#PathVariable("file") String file) {
String[] parts = file.split("\\.");
String extension = parts[1];
List<SousBloc> resWord;
List<SousBloc> resPdf;
List<CvAvecBlocs> resExcel;
RestTemplate rt = new RestTemplate();
rt.getMessageConverters().add(new StringHttpMessageConverter());
if(extension.equals("xlsx")){
resExcel = rt.getForObject("http://localhost:8080/readExcel/"+file, List.class, 200);
return new ResponseEntity<>(resExcel, HttpStatus.OK);
}
else if(extension.equals("pdf")){
resPdf = rt.getForObject("http://localhost:8080/readPdf/"+file, List.class, 200);
return new ResponseEntity<>(resPdf, HttpStatus.OK);
}
else if(extension.equals("docx")){
resWord = rt.getForObject("http://localhost:8080/readWord/"+file, List.class, 200);
return new ResponseEntity<>(resWord, HttpStatus.OK);
}
return null;
}
There is my Reading Word Controller :
#Controller
public class ReadWordController {
private static String UPLOADED_FOLDER = "C:\\cvsUploades\\";
#Autowired
ReadWord readWord;
#RequestMapping(value="/readWord/{file:.+}" , method = RequestMethod.GET)
public ResponseEntity readingWord(#PathVariable("file") String file) throws IOException {
String path = UPLOADED_FOLDER+file;
List<SousBloc> sousBlocs = readWord.extract(path);
return new ResponseEntity<>(sousBlocs, HttpStatus.OK);
}
}
Well this controller works fine and does the job.
Now there is my Reading Pdf Controller :
#Controller
public class ReadPdfController {
private static String UPLOADED_FOLDER = "C:\\cvsUploades\\";
#Autowired
ReadPdf readPdf;
#RequestMapping(value="/readPdf/{file:.+}" , method = RequestMethod.GET)
public ResponseEntity readingPdf(#PathVariable("file") String file) throws IOException {
String path = UPLOADED_FOLDER+file;
List<SousBloc> blocs = readPdf.extract(path);
return new ResponseEntity<>(blocs, HttpStatus.OK);
}
}
It is contructed on the same model of the Reading Word Controller but it does not work. In debug, the program works fine until the return new ResponseEntity<>(blocs, HttpStatus.OK); that throws a 406 error null...
Do you know why ?
EDIT: I tried something strange and it worked... I put the following code :
#Controller
public class ReadWordController {
private static String UPLOADED_FOLDER = "C:\\cvsUploades\\";
#Autowired
ReadWord readWord;
#Autowired
ReadPdf readPdf;
#RequestMapping(value="/readWord/{file:.+}" , method = RequestMethod.GET)
public ResponseEntity readingWord(#PathVariable("file") String file) throws IOException {
/*String path = UPLOADED_FOLDER+file;
List<SousBloc> sousBlocs = readWord.extract(path);
return new ResponseEntity<>(sousBlocs, HttpStatus.OK);*/
String path = "C:\\cvsUploades\\file.pdf";
List<SousBloc> blocs = readPdf.extract(path);
return new ResponseEntity<>(blocs, HttpStatus.OK);
}
}

#PathVariable Validation in Spring 4

How can i validate my path variable in spring. I want to validate id field, since its only single field i do not want to move to a Pojo
#RestController
public class MyController {
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseEntity method_name(#PathVariable String id) {
/// Some code
}
}
I tried doing adding validation to the path variable but its still not working
#RestController
#Validated
public class MyController {
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseEntity method_name(
#Valid
#Nonnull
#Size(max = 2, min = 1, message = "name should have between 1 and 10 characters")
#PathVariable String id) {
/// Some code
}
}
You need to create a bean in your Spring configuration:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
You should leave the #Validated annotation on your controller.
And you need an Exceptionhandler in your MyController class to handle theConstraintViolationException :
#ExceptionHandler(value = { ConstraintViolationException.class })
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleResourceNotFoundException(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> violation : violations ) {
strBuilder.append(violation.getMessage() + "\n");
}
return strBuilder.toString();
}
After those changes you should see your message when the validation hits.
P.S.: I just tried it with your #Size validation.
To archive this goal I have apply this workaround for getting a response message equals to a real Validator:
#GetMapping("/check/email/{email:" + Constants.LOGIN_REGEX + "}")
#Timed
public ResponseEntity isValidEmail(#Email #PathVariable(value = "email") String email) {
return userService.getUserByEmail(email).map(user -> {
Problem problem = Problem.builder()
.withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE)
.withTitle("Method argument not valid")
.withStatus(Status.BAD_REQUEST)
.with("message", ErrorConstants.ERR_VALIDATION)
.with("fieldErrors", Arrays.asList(new FieldErrorVM("", "isValidEmail.email", "not unique")))
.build();
return new ResponseEntity(problem, HttpStatus.BAD_REQUEST);
}).orElse(
new ResponseEntity(new UtilsValidatorResponse(EMAIL_VALIDA), HttpStatus.OK)
);
}

Resources