So a have a blueprint code of a web app which allows different accounts with logins to upload simple files. However, there are security flaws that allow non-admin accounts to enter database configurations and direct object referencing (download files of other accounts by URL tampering). I was recommended a solution whereby I check if the owner of the uploaded file is the same one trying to download it. But doing that in a spring #controller posed a few problems. If you look at my get statement to get a specific file, you see that I got the file object and the account object. Then I just check if the name is the same of file owner. But how do I "return" something in a controller while in an "if" statement?
#Controller
public class FileController {
#Autowired
private FileRepository fileRepository;
#Autowired
private AccountRepository accountRepository;
#RequestMapping(value = "/files", method = RequestMethod.GET)
public String list(Authentication authentication, Model model) {
model.addAttribute("files", accountRepository.findByUsername(authentication.getName()).getFileObjects());
return "files";
}
#RequestMapping(value = "/files/{id}", method = RequestMethod.GET)
public ResponseEntity<byte[]> viewFile(#PathVariable Long id) {
//1. get object or name account name
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentPrincipalName = authentication.getName();
//2. check if the file account is of the same name
FileObject fo = fileRepository.findOne(id);
if((fo.getAccount().getUsername()).equals(currentPrincipalName)) {
System.out.println("WHAT AM I SUPPOSED TO DO!?");
}
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(fo.getContentType()));
headers.add("Content-Disposition", "attachment; filename=" + fo.getName());
headers.setContentLength(fo.getContentLength());
return new ResponseEntity<>(fo.getContent(), headers, HttpStatus.CREATED);
}
#RequestMapping(value = "/files", method = RequestMethod.POST)
public String addFile(Authentication authentication, #RequestParam("file") MultipartFile file) throws IOException {
Account account = accountRepository.findByUsername(authentication.getName());
FileObject fileObject = new FileObject();
fileObject.setContentType(file.getContentType());
fileObject.setContent(file.getBytes());
fileObject.setName(file.getOriginalFilename());
fileObject.setContentLength(file.getSize());
fileObject.setAccount(account);
fileRepository.save(fileObject);
return "redirect:/files";
}
#RequestMapping(value = "/files/{id}", method = RequestMethod.DELETE)
public String delete(#PathVariable Long id) {
fileRepository.delete(id);
return "redirect:/files";
}
}
Related
So I'm making a RESTful API backend for a JavaScript frontend and want to upload a file to Google Cloud Storage. I have this function to handle the file upload:
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST, consumes = { "multipart/form-data" })
public ResponseEntity<?> uploadFile(#ModelAttribute("file") FileDTO fileDTO) {
FunctionResponse uploadResponse = cloudStorage.uploadObject(fileDTO.getFile());
if (uploadResponse.successful()) {
return new ResponseEntity<>(uploadResponse.getMessage(), HttpStatus.OK);
} else {
return new ResponseEntity<>(uploadResponse.getMessage(), HttpStatus.BAD_REQUEST);
}
}
My fileDTO class looks like this:
public class FileDTO implements Serializable {
private MultipartFile file;
public MultipartFile getFile() {
return file;
}
}
However whenever I try and access the MultipartFile it always throws a java.lang.NullPointerException.
I have tried various SO answers from threads with similar problems but none have worked so far.
I have multipart.enabled=true in my application.properties.
Any help would be appreciated and let me know if you need any more information.
When a multipart/form-data request arrives, the data must be obtained by using #RequestParam, not #ModelAttribute + if the value you need of the request is a file, then, it should be deserialized into a MultipartFile object.
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST, consumes = {"multipart/form-data"})
public ResponseEntity<?> uploadFile(#RequestParam(name = "file") MultipartFile file) {
// your handle ...
}
I am trying to make a post request using json but in postman the request is successful only if I make the request like this: email#example.com. If I make a request using the standard JSON format {"email":"email#example.com"} I get "invalid email id". I should mention that content type application/json header is checked in postman, and I am making the request in body/raw.
I have tried messing with #RequestBody/#RequestParam annotations, using consumes = "application/json" but I am unsuccessful and I couldn't find a similar issue after lots of googling either.
my controller:
#RestController
public class UserController {
#Autowired
private UserService userService;
#PostMapping(value = "/forgot-password", consumes = "application/json")
public String forgotPassword(#RequestBody String email) {
String response = userService.forgotPassword(email);
if (!response.startsWith("Invalid")) {
response = "http://localhost:8080/reset-password?token=" + response;
}
return response;
}
user service:
public String forgotPassword(String email) {
Optional<User> userOptional = Optional
.ofNullable(userRepository.findByEmail(email));
if (!userOptional.isPresent()) {
return "Invalid email id.";
}
User user = userOptional.get();
user.setToken(generateToken());
user.setTokenCreationDate(LocalDateTime.now());
user = userRepository.save(user);
return user.getToken();
}
Simply put, the #RequestBody annotation maps the HttpRequest body to a transfer or domain object.You need to put object instead of String
Your endpoint should be like Below.
#PostMapping(value = "/forgot-password", consumes = "application/json")
public String forgotPassword(#RequestBody EmailDto email) {
String response = userService.forgotPassword(email.getEmail);
// ...
return response;
}
Your DTO should be like below
public class EmailDto {
private String email;
//Getters and Setters
}
You should have Email model with string property email.
public EmailPayload {
String email;
.....
Then it will work (it will fit json you provided).
Ofcouse class name can be different, only thing that must match is email property, then in your Controller your #RequestBody will be this class, and not String you have now.
I'm using Spring Boot to build a REST API. In my situation, there are 2 controllers: ExportController and ImportController. Please check the example code here:
Export Controller:
#RestController
public class ExportController {
#GetMapping(value = "/export", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<Resource> export(#RequestParam("id") String id) {
// Processing...
}
}
Import Controller:
#RestController
public class ImportController {
#PostMapping(value = "/import", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> importData(HttpServletRequest request) {
// Processing...
// What should I do here?
String url = ...
}
}
Inside my ImportController, I want to generate a URL pointing to the /export endpoint, e.g. http://www.example.com/export?id=1234.
I don't configure anything about the host or port in the application.properties. I want to get them at runtime.
Could you please show me how to achieve it? I searched a lot on the Internet but couldn't find the answer. Thank you for your help.
If you can live with bringing spring-hateoas into your project then this will work:
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
#RestController
public class ImportController {
#PostMapping(value = "/import", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> importData(HttpServletRequest request) {
String someId = "1234";
ControllerLinkBuilder linkBuilder = ControllerLinkBuilder.linkTo(methodOn(ExportController.class).export(someId));
URI uri = linkBuilder.toUri();
return ResponseEntity.ok(uri);
}
}
This yields http://localhost:8080/export?id=1234
#RestController
public class ImportController {
#PostMapping(value = "/import", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> importData(HttpServletRequest request) {
// Processing...
String url = request.getScheme() + "://" +
request.getServerName() + ":" +
request.getServerPort() + "/export";
}
}
You can make use of ServletUriComponentsBuilder that comes with Spring framework since 3.1.RELEASE.
Given that you have access to current request, You can do something like below.
UriComponents uriComponents = ServletUriComponentsBuilder
.fromRequest(httpServletRequest)
.replacePath("/export")
.queryParam("id",1234)
.build();
String url = uriComponents.toUri();
There is a task to pass file path as #PathVariable in Spring MVC to REST Service with GET request.
We can easily do it with POST sending String of file path in JSON.
How we can do with GET request and #Controller like this?
#RequestMapping(value = "/getFile", method = RequestMethod.GET)
public File getFile(#PathVariable String path) {
// do something
}
Request:
GET /file/getFile/"/Users/user/someSourceFolder/8.jpeg"
Content-Type: application/json
You should define your controller like this:
#RequestMapping(value = "/getFile/{path:.+}", method = RequestMethod.GET)
public File getFile(#PathVariable String path) {
// do something
}
Ok.
you use to get pattern.
sending get pattern url.
Use #RequestParam.
#RequestMapping(value = "/getFile", method = RequestMethod.GET)
public File getFile(#RequestParam("path") String path) {
// do something
}
and if you use #PathVariable.
#RequestMapping(value = "/getFile/{path}", method = RequestMethod.POST)
public File getFile(#PathVariable String path) {
// do something
}
What I did works with relative paths to download/upload files in Spring.
#RequestMapping(method = RequestMethod.GET, path = "/files/**")
#NotNull
public RepositoryFile get(#PathVariable final String repositoryId,
#PathVariable final String branchName,
#RequestParam final String authorEmail,
HttpServletRequest request) {
String filePath = extractFilePath(request);
....
}
And the utilitary function I created within the controller :
private static String extractFilePath(HttpServletRequest request) {
String path = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
String bestMatchPattern = (String) request.getAttribute(
HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
AntPathMatcher apm = new AntPathMatcher();
return apm.extractPathWithinPattern(bestMatchPattern, path);
}
I am developing a project using Spring Boot. I've a controller which accepts GET requests.
Currently I'm accepting requests to the following kind of URLs:
http://localhost:8888/user/data/002
but I want to accept requests using query parameters:
http://localhost:8888/user?data=002
Here's the code of my controller:
#RequestMapping(value="/data/{itemid}", method = RequestMethod.GET)
public #ResponseBody
item getitem(#PathVariable("itemid") String itemid) {
item i = itemDao.findOne(itemid);
String itemname = i.getItemname();
String price = i.getPrice();
return i;
}
Use #RequestParam
#RequestMapping(value="user", method = RequestMethod.GET)
public #ResponseBody Item getItem(#RequestParam("data") String itemid){
Item i = itemDao.findOne(itemid);
String itemName = i.getItemName();
String price = i.getPrice();
return i;
}
While the accepted answer by afraisse is absolutely correct in terms of using #RequestParam, I would further suggest to use an Optional<> as you cannot always ensure the right parameter is used. Also, if you need an Integer or Long just use that data type to avoid casting types later on in the DAO.
#RequestMapping(value="/data", method = RequestMethod.GET)
public #ResponseBody
Item getItem(#RequestParam("itemid") Optional<Integer> itemid) {
if( itemid.isPresent()){
Item i = itemDao.findOne(itemid.get());
return i;
} else ....
}
To accept both #PathVariable and #RequestParam in the same /user endpoint:
#GetMapping(path = {"/user", "/user/{data}"})
public void user(#PathVariable(required=false,name="data") String data,
#RequestParam(required=false) Map<String,String> qparams) {
qparams.forEach((a,b) -> {
System.out.println(String.format("%s -> %s",a,b));
}
if (data != null) {
System.out.println(data);
}
}
Testing with curl:
curl 'http://localhost:8080/user/books'
curl 'http://localhost:8080/user?book=ofdreams&name=nietzsche'
In Spring boot: 2.1.6, you can use like below:
#GetMapping("/orders")
#ApiOperation(value = "retrieve orders", response = OrderResponse.class, responseContainer = "List")
public List<OrderResponse> getOrders(
#RequestParam(value = "creationDateTimeFrom", required = true) String creationDateTimeFrom,
#RequestParam(value = "creationDateTimeTo", required = true) String creationDateTimeTo,
#RequestParam(value = "location_id", required = true) String location_id) {
// TODO...
return response;
#ApiOperation is an annotation that comes from Swagger api, It is used for documenting the apis.
To accept both Path Variable and query Param in the same endpoint:
#RequestMapping(value = "/hello/{name}", method = RequestMethod.POST)
public String sayHi(
#PathVariable("name") String name,
#RequestBody Topic topic,
//#RequestParam(required = false, name = "s") String s,
#RequestParam Map<String, String> req) {
return "Hi "+name +" Topic : "+ topic+" RequestParams : "+req;
}
URL looks like : http://localhost:8080/hello/testUser?city=Pune&Pin=411058&state=Maha
I was interested in this as well and came across some examples on the Spring Boot site.
// get with query string parameters e.g. /system/resource?id="rtze1cd2"&person="sam smith"
// so below the first query parameter id is the variable and name is the variable
// id is shown below as a RequestParam
#GetMapping("/system/resource")
// this is for swagger docs
#ApiOperation(value = "Get the resource identified by id and person")
ResponseEntity<?> getSomeResourceWithParameters(#RequestParam String id, #RequestParam("person") String name) {
InterestingResource resource = getMyInterestingResourc(id, name);
logger.info("Request to get an id of "+id+" with a name of person: "+name);
return new ResponseEntity<Object>(resource, HttpStatus.OK);
}
See here also