diff --git a/mozen/src/main/java/mozen/auth/WebSecurity.java b/mozen/src/main/java/mozen/auth/WebSecurity.java index 9b243b1cddc59b8d61e92d51878d2854a4477cd7..b220efd0c98edd7ada7aa0e00140cf8d6979f273 100644 --- a/mozen/src/main/java/mozen/auth/WebSecurity.java +++ b/mozen/src/main/java/mozen/auth/WebSecurity.java @@ -1,6 +1,10 @@ package mozen.auth; +import java.util.Arrays; +import java.util.Collections; + import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -8,6 +12,9 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; @EnableWebSecurity public class WebSecurity extends WebSecurityConfigurerAdapter{ @@ -35,4 +42,20 @@ public class WebSecurity extends WebSecurityConfigurerAdapter{ public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder); } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + final CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Collections.unmodifiableList(Arrays.asList("*"))); + configuration.setAllowedMethods(Collections.unmodifiableList(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH"))); + // setAllowCredentials(true) is important, otherwise: + // The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. + configuration.setAllowCredentials(true); + // setAllowedHeaders is important! Without it, OPTIONS preflight request + // will fail with 403 Invalid CORS request + configuration.setAllowedHeaders(Collections.unmodifiableList(Arrays.asList("Authorization", "Cache-Control", "Content-Type"))); + final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } } \ No newline at end of file diff --git a/mozen/src/main/java/mozen/business/IModelDao.java b/mozen/src/main/java/mozen/business/IModelDao.java index 709de7260db0e99597876491a56847c86f9e040a..839ca7f9f1046beaedeea0baed50637140d09cf3 100644 --- a/mozen/src/main/java/mozen/business/IModelDao.java +++ b/mozen/src/main/java/mozen/business/IModelDao.java @@ -2,6 +2,7 @@ package mozen.business; import java.util.Collection; +import mozen.model.CustomLayer; import mozen.model.Model; import mozen.model.TagCategory; @@ -10,8 +11,14 @@ public interface IModelDao { void updateModel(Model m); void removeModel(long id); + void addLayer(CustomLayer l); + void updateLayer(CustomLayer l); + void removeLayer(long id); + Collection<TagCategory> getTags(); Model findModel(long id); Collection<Model> findModelsByName(String name); + + CustomLayer findLayer(long id); } \ No newline at end of file diff --git a/mozen/src/main/java/mozen/business/IModelManager.java b/mozen/src/main/java/mozen/business/IModelManager.java index 87d62fa0143094fd4578c5dc3a5e9f8f7cde9723..03ce64da871c57aa9a130d3b6c882d95a9fe679c 100644 --- a/mozen/src/main/java/mozen/business/IModelManager.java +++ b/mozen/src/main/java/mozen/business/IModelManager.java @@ -2,15 +2,26 @@ package mozen.business; import java.util.Collection; +import org.springframework.web.multipart.MultipartFile; + +import mozen.model.CustomLayer; import mozen.model.Model; import mozen.model.TagCategory; +import mozen.model.User; public interface IModelManager { - Long addModel(Model m); - void updateModel(Model m); - void removeModel(Long id); + Long addModel(Model m, User user) throws Exception; + void updateModel(Model m, long id, User user) throws Exception; + void removeModel(long id, User user) throws Exception; + + Long addLayer(long modelId, CustomLayer layer, User user) throws Exception; + void updateLayer(long id, CustomLayer layer, User user) throws Exception; + void removeLayer(long id, User user) throws Exception; + + void addFile(MultipartFile file, long id, User user) throws Exception; + void addLayerFile(MultipartFile file, long id, User user) throws Exception; - Model getModel(Long id); + Model getModel(long id); Collection<TagCategory> getTags(); Collection<Model> findModel(String name); } \ No newline at end of file diff --git a/mozen/src/main/java/mozen/business/ModelDao.java b/mozen/src/main/java/mozen/business/ModelDao.java index 044ab9f92ada199f3212c529e112c0b82522390d..82c0feeb2067e991fd5281d3ee425485add9f1f7 100644 --- a/mozen/src/main/java/mozen/business/ModelDao.java +++ b/mozen/src/main/java/mozen/business/ModelDao.java @@ -10,6 +10,7 @@ import javax.transaction.Transactional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; +import mozen.model.CustomLayer; import mozen.model.Model; import mozen.model.TagCategory; @@ -33,8 +34,7 @@ public class ModelDao implements IModelDao { @Override public void removeModel(long id) { - Model m = findModel(id); - em.remove(m); + em.remove(findModel(id)); } @Override @@ -45,24 +45,44 @@ public class ModelDao implements IModelDao { @Override public Collection<Model> findModelsByName(String name) { String query = "SELECT m FROM Model m WHERE m.name LIKE :name ORDER BY votes ASC"; - TypedQuery<Model> q = em.createQuery(query, Model.class); - q.setParameter("name", "%"+name+"%"); - try { - return q.getResultList(); - } catch (Exception e) { - return null; - } + TypedQuery<Model> q = em.createQuery(query, Model.class); + q.setParameter("name", "%" + name + "%"); + try { + return q.getResultList(); + } catch (Exception e) { + return null; + } } @Override public Collection<TagCategory> getTags() { String query = "SELECT t FROM TagCategory t"; - TypedQuery<TagCategory> q = em.createQuery(query, TagCategory.class); - try { - return q.getResultList(); - } catch (Exception e) { - return null; - } + TypedQuery<TagCategory> q = em.createQuery(query, TagCategory.class); + try { + return q.getResultList(); + } catch (Exception e) { + return null; + } + } + + @Override + public void addLayer(CustomLayer l) { + em.persist(l); + } + + @Override + public void updateLayer(CustomLayer l) { + em.merge(l); + } + + @Override + public void removeLayer(long id) { + em.remove(findLayer(id)); + } + + @Override + public CustomLayer findLayer(long id) { + return em.find(CustomLayer.class, id); } } \ No newline at end of file diff --git a/mozen/src/main/java/mozen/business/ModelManager.java b/mozen/src/main/java/mozen/business/ModelManager.java index fbd4e639e3198880ca2e745360fb7109507772c9..780d2be3c1be186b01c4fea3ebf236a633f680c0 100644 --- a/mozen/src/main/java/mozen/business/ModelManager.java +++ b/mozen/src/main/java/mozen/business/ModelManager.java @@ -4,9 +4,12 @@ import java.util.Collection; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import mozen.model.CustomLayer; import mozen.model.Model; import mozen.model.TagCategory; +import mozen.model.User; @Service public class ModelManager implements IModelManager { @@ -15,23 +18,42 @@ public class ModelManager implements IModelManager { IModelDao dao; @Override - public Long addModel(Model m) { - dao.addModel(m); - return m.getId(); + public Long addModel(Model model, User user) throws Exception { + model.setAuthor(user); + dao.addModel(model); + return model.getId(); } @Override - public void updateModel(Model m) { - dao.updateModel(m); + public void addFile(MultipartFile file, long id, User user) throws Exception { + Model model = dao.findModel(id); + if (model == null) throw new Exception("Unknown model"); + model.setFile(file.getBytes()); + model.setFileType(file.getContentType()); + dao.updateModel(model); } @Override - public void removeModel(Long id) { + public void updateModel(Model model, long id, User user) throws Exception { + Model modelToUpdate = dao.findModel(id); + if (modelToUpdate == null) throw new Exception("Unknown model"); + if(!isModelAuthor(modelToUpdate, user)) throw new Exception("Not the author"); + + model.setId(id); + dao.updateModel(model); + } + + @Override + public void removeModel(long id, User user) throws Exception { + Model model = dao.findModel(id); + if (model == null) throw new Exception("Unknown model"); + if(!isModelAuthor(model, user)) throw new Exception("Not the author"); + dao.removeModel(id); } @Override - public Model getModel(Long id) { + public Model getModel(long id) { return dao.findModel(id); } @@ -45,6 +67,55 @@ public class ModelManager implements IModelManager { @Override public Collection<TagCategory> getTags() { return dao.getTags(); + } + + @Override + public Long addLayer(long modelId, CustomLayer layer, User user) throws Exception { + Model model = dao.findModel(modelId); + if (model == null) throw new Exception("Unknown model"); + if(!isModelAuthor(model, user)) throw new Exception("Not the author"); + + layer.setModel(model); + dao.addLayer(layer); + + return layer.getId(); + } + + @Override + public void updateLayer(long id, CustomLayer layer, User user) throws Exception { + CustomLayer layerToUpdate = dao.findLayer(id); + if (layerToUpdate == null) throw new Exception("Unknown layer"); + if(!isLayerAuthor(layerToUpdate, user)) throw new Exception("Not the author"); + + layer.setId(id); + dao.updateLayer(layer); } - + + @Override + public void removeLayer(long id, User user) throws Exception { + CustomLayer layer = dao.findLayer(id); + if (layer == null) throw new Exception("Unknown layer"); + if(!isLayerAuthor(layer, user)) throw new Exception("Not the author"); + + dao.removeLayer(id); + } + + @Override + public void addLayerFile(MultipartFile file, long id, User user) throws Exception { + CustomLayer layer = dao.findLayer(id); + if (layer == null) throw new Exception("Unknown layer"); + if(!isLayerAuthor(layer, user)) throw new Exception("Not the author"); + layer.setFile(file.getBytes()); + + dao.updateLayer(layer); + } + + private boolean isLayerAuthor(CustomLayer layer, User user) { + return layer.getModel().getAuthor() == user; + } + + private boolean isModelAuthor(Model model, User user) { + return model.getAuthor() == user; + } + } \ No newline at end of file diff --git a/mozen/src/main/java/mozen/model/CustomLayer.java b/mozen/src/main/java/mozen/model/CustomLayer.java index 73342bfab326a241b3ada79898b19acbc0973593..752c58a3cd6362989a27cf2c1d5a22a7049e5d49 100644 --- a/mozen/src/main/java/mozen/model/CustomLayer.java +++ b/mozen/src/main/java/mozen/model/CustomLayer.java @@ -10,6 +10,7 @@ import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; +import javax.persistence.Lob; import javax.persistence.ManyToOne; import javax.persistence.Table; @@ -24,17 +25,13 @@ public class CustomLayer implements Serializable{ @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Basic - @Column(nullable = false) - private Long localID; - @Basic @Column(nullable = false) private String name; - @Basic + @Lob @Column - private String filePath; + private byte[] file; @Basic @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @@ -43,11 +40,10 @@ public class CustomLayer implements Serializable{ public CustomLayer() { } - public CustomLayer(Long id, Long localID, String name, String filePath, Model model) { + public CustomLayer(Long id, String name, byte[] file, Model model) { this.id = id; - this.localID = localID; this.name = name; - this.filePath = filePath; + this.file = file; this.model = model; } @@ -59,14 +55,6 @@ public class CustomLayer implements Serializable{ this.id = id; } - public Long getLocalID() { - return this.localID; - } - - public void setLocalID(Long localID) { - this.localID = localID; - } - public String getName() { return this.name; } @@ -76,12 +64,12 @@ public class CustomLayer implements Serializable{ } @JsonIgnore - public String getFilePath() { - return this.filePath; + public byte[] getFile() { + return this.file; } - public void setFilePath(String filePath) { - this.filePath = filePath; + public void setFile(byte[] file) { + this.file = file; } @JsonIgnore diff --git a/mozen/src/main/java/mozen/model/Model.java b/mozen/src/main/java/mozen/model/Model.java index 8d0b907c20498391e71e973f26f738a7eb18a921..59f04f0095eb3839b6d425445d26cf8bfe2266aa 100644 --- a/mozen/src/main/java/mozen/model/Model.java +++ b/mozen/src/main/java/mozen/model/Model.java @@ -12,6 +12,7 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinTable; +import javax.persistence.Lob; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; @@ -62,9 +63,13 @@ public class Model implements Serializable{ @Column private int votes; + @Lob + @Column + private byte[] file; + @Basic @Column - private String filePath; + private String fileType; @Basic @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @@ -83,7 +88,7 @@ public class Model implements Serializable{ public Model() { } - public Model(Long id, String name, String shortDescription, String longDescription, String added, String lastModified, int votes, String filePath, User author, Set<Tag> tags, Set<CustomLayer> customLayers) { + public Model(Long id, String name, String shortDescription, String longDescription, String added, String lastModified, int votes, byte[] file, String fileType, User author, Set<Tag> tags, Set<CustomLayer> customLayers) { this.id = id; this.name = name; this.shortDescription = shortDescription; @@ -91,7 +96,8 @@ public class Model implements Serializable{ this.added = added; this.lastModified = lastModified; this.votes = votes; - this.filePath = filePath; + this.file = file; + this.fileType = fileType; this.author = author; this.tags = tags; this.customLayers = customLayers; @@ -154,12 +160,12 @@ public class Model implements Serializable{ } @JsonIgnore - public String getFilePath() { - return this.filePath; + public byte[] getFile() { + return this.file; } - public void setFilePath(String filePath) { - this.filePath = filePath; + public void setFile(byte[] file) { + this.file = file; } public User getAuthor() { @@ -184,6 +190,14 @@ public class Model implements Serializable{ public void setCustomLayers(Set<CustomLayer> customLayers) { this.customLayers = customLayers; - } + } + + public String getFileType() { + return this.fileType; + } + + public void setFileType(String fileType) { + this.fileType = fileType; + } } \ No newline at end of file diff --git a/mozen/src/main/java/mozen/utils/DatabaseFiller.java b/mozen/src/main/java/mozen/utils/DatabaseFiller.java index 5d54c98d3098d6763b85fc7301e7c2f7f98958c7..7078c814c204785a828a7765befe0e29e81c0cef 100644 --- a/mozen/src/main/java/mozen/utils/DatabaseFiller.java +++ b/mozen/src/main/java/mozen/utils/DatabaseFiller.java @@ -75,11 +75,9 @@ public class DatabaseFiller { Set<CustomLayer> m1layers = new HashSet<CustomLayer>(); CustomLayer l1 = new CustomLayer(); l1.setName("super layer 1"); - l1.setLocalID((long) 1); CustomLayer l2 = new CustomLayer(); l2.setName("super layer 2"); - l2.setLocalID((long) 2); m1layers.add(l1); m1layers.add(l2); diff --git a/mozen/src/main/java/mozen/web/ModelController.java b/mozen/src/main/java/mozen/web/ModelController.java index a9e58a06824dab8d57dad964877bb177c9a12e8a..351b81f5383d7802b421915d7ebbd23493ba97a8 100644 --- a/mozen/src/main/java/mozen/web/ModelController.java +++ b/mozen/src/main/java/mozen/web/ModelController.java @@ -3,20 +3,30 @@ package mozen.web; import java.util.Collection; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; import mozen.business.IModelManager; import mozen.business.IUserManager; import mozen.model.Model; import mozen.model.ResponseMessage; import mozen.model.TagCategory; +import mozen.model.User; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -32,39 +42,126 @@ public class ModelController { IUserManager userManager; @GetMapping("") - public ResponseEntity<?> getUserModels(@RequestParam(value = "id", required = false) Long id) { - - if(id != null) { - // User u = userManager.getUser(id); - // return ResponseEntity.ok().body(u.getModels()); + public ResponseEntity<?> getModelDetails(@RequestParam(value = "id", required = false) Long id) { + User user = getUser(); + if(user != null) { + return ResponseEntity.ok().body(user.getModels()); } else { return ResponseEntity.ok().body(modelManager.getModel(id)); } - return null; } @PostMapping("") public ResponseEntity<ResponseMessage> addModel(Model model) { ResponseMessage response = new ResponseMessage(false, ""); + User user = getUser(); + if(user == null) { + response.setError(true); + response.setMessage("User unknown"); + return ResponseEntity.ok(response); + } try { - modelManager.addModel(model); + modelManager.addModel(model, user); } catch (Exception e) { response.setError(true); - response.setMessage("nah"); + response.setMessage(e.getMessage()); + } + + return ResponseEntity.ok(response); + } + + @PutMapping("") + public ResponseEntity<ResponseMessage> updateModel(@RequestParam(value = "id", required = true) Long id, Model model) { + ResponseMessage response = new ResponseMessage(false, ""); + User user = getUser(); + if(user == null) { + response.setError(true); + response.setMessage("User unknown"); + return ResponseEntity.ok(response); + } + + try { + modelManager.updateModel(model, id, user); + } catch (Exception e) { + response.setError(true); + response.setMessage(e.getMessage()); } return ResponseEntity.ok(response); } @DeleteMapping("") - public ResponseEntity<?> deleteModel(@RequestParam(value = "id", required = true) Long id) { - modelManager.removeModel(id); - return ResponseEntity.ok(null); + public ResponseEntity<ResponseMessage> deleteModel(@RequestParam(value = "id", required = true) Long id) { + ResponseMessage response = new ResponseMessage(false, ""); + User user = getUser(); + if(user == null) { + response.setError(true); + response.setMessage("User unknown"); + return ResponseEntity.ok(response); + } + + try { + modelManager.removeModel(id, user); + } catch (Exception e) { + response.setError(true); + response.setMessage(e.getMessage()); + } + + return ResponseEntity.ok(response); } @GetMapping("/tags") public Collection<TagCategory> getTags() { return modelManager.getTags(); } + + @PostMapping("/upload") + public ResponseEntity<ResponseMessage> uploadModelFile(@RequestParam(value = "id", required = true) Long id, @RequestParam("file") MultipartFile file) { + ResponseMessage response = new ResponseMessage(false, ""); + User user = getUser(); + if(user == null) { + response.setError(true); + response.setMessage("User unknown"); + return ResponseEntity.ok(response); + } + + try { + modelManager.addFile(file, id, user); + } catch (Exception e) { + response.setError(true); + response.setMessage(e.getMessage()); + } + + return ResponseEntity.ok(response); + } + + @PostMapping("/download") + public ResponseEntity<?> downloadModelFile(@RequestParam(value = "id", required = true) Long id) { + ResponseMessage response = new ResponseMessage(false, ""); + + try { + Model model = modelManager.getModel(id); + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType(model.getFileType())) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + model.getName() + "\"") + .body(new ByteArrayResource(model.getFile())); + } catch (Exception e) { + response.setError(true); + response.setMessage(e.getMessage()); + } + + return ResponseEntity.ok(response); + } + + private User getUser() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + + if (!(auth instanceof AnonymousAuthenticationToken)) { + UserDetails userDetails = (UserDetails) auth.getPrincipal(); + return userManager.getUserByUsername(userDetails.getUsername()); + } else { + return null; + } + } } \ No newline at end of file diff --git a/mozen/src/main/java/mozen/web/UserController.java b/mozen/src/main/java/mozen/web/UserController.java index f62dc80eefbdfd0f792c6818b0d2895bd9c81b1e..d09ce028acff63d2ac4ce4d304a7cff40b1c5118 100644 --- a/mozen/src/main/java/mozen/web/UserController.java +++ b/mozen/src/main/java/mozen/web/UserController.java @@ -4,6 +4,8 @@ import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; @@ -26,7 +28,8 @@ public class UserController { @GetMapping("") public User getUser() { - return new User(); + UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + return manager.getUserByUsername(userDetails.getUsername()); } @PostMapping("/signup") @@ -36,5 +39,4 @@ public class UserController { ResponseMessage response = new ResponseMessage(false, ""); return ResponseEntity.ok(response); } - } \ No newline at end of file