diff --git a/pom.xml b/pom.xml index c83d1a2..6080791 100644 --- a/pom.xml +++ b/pom.xml @@ -59,6 +59,11 @@ postgresql runtime + + commons-lang + commons-lang + 2.3 + diff --git a/src/main/java/org/codiki/login/LoginController.java b/src/main/java/org/codiki/account/AccountController.java similarity index 53% rename from src/main/java/org/codiki/login/LoginController.java rename to src/main/java/org/codiki/account/AccountController.java index 2fd0f9e..c217811 100644 --- a/src/main/java/org/codiki/login/LoginController.java +++ b/src/main/java/org/codiki/account/AccountController.java @@ -1,32 +1,36 @@ -package org.codiki.login; +package org.codiki.account; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.codiki.core.entities.dto.UserDAO; +import org.codiki.core.entities.dto.PasswordWrapperDTO; +import org.codiki.core.entities.dto.UserDTO; +import org.codiki.core.entities.persistence.User; import org.codiki.core.security.TokenService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/account") -public class LoginController { +public class AccountController { private static final String HEADER_TOKEN = "token"; @Autowired - private TokenService tokenService; - - @Autowired - private LoginService loginService; + private AccountService accountService; + @Autowired + private TokenService tokenService; + + @PostMapping("/login") - public UserDAO login(@RequestBody UserDAO pUser, HttpServletResponse response) { - return loginService.checkCredentials(response, pUser); + public UserDTO login(@RequestBody UserDTO pUser, HttpServletResponse response) { + return accountService.checkCredentials(response, pUser); } @GetMapping("/logout") @@ -34,4 +38,10 @@ public class LoginController { tokenService.removeUser(pRequest.getHeader(HEADER_TOKEN)); } + @PutMapping("/changePassword") + public boolean changePassword(@RequestBody final PasswordWrapperDTO pPasswordWrapper, + final HttpServletRequest pRequest, + final HttpServletResponse pResponse) { + return accountService.changePassword(tokenService.getAuthenticatedUserByToken(pRequest), pPasswordWrapper, pResponse); + } } diff --git a/src/main/java/org/codiki/account/AccountService.java b/src/main/java/org/codiki/account/AccountService.java new file mode 100644 index 0000000..eaf3760 --- /dev/null +++ b/src/main/java/org/codiki/account/AccountService.java @@ -0,0 +1,69 @@ +package org.codiki.account; + +import java.util.Optional; + +import javax.naming.AuthenticationException; +import javax.servlet.http.HttpServletResponse; + +import org.codiki.core.entities.dto.PasswordWrapperDTO; +import org.codiki.core.entities.dto.UserDTO; +import org.codiki.core.entities.persistence.User; +import org.codiki.core.repositories.UserRepository; +import org.codiki.core.security.TokenService; +import org.codiki.core.utils.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class AccountService { + + @Autowired + private UserRepository userRepository; + + @Autowired + private TokenService tokenService; + + /** + * Check the user credentials and generate him a token if they are correct. + * + * @param pUser + * The user sent from client. + * @return The user populated with the generated token. + * @throws AuthenticationException + * If the credentials are wrong. + */ + public UserDTO checkCredentials(HttpServletResponse pResponse, UserDTO pUser) { + UserDTO result = null; + + Optional user = userRepository.findByEmail(pUser.getEmail()); + + if(user.isPresent() && StringUtils.compareHash(pUser.getPassword(), user.get().getPassword())) { + tokenService.addUser(user.get()); + result = new UserDTO(user.get(), true); + } else { + pResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); + } + + return result; + } + + public boolean changePassword(final User pUser, final PasswordWrapperDTO pPasswordWrapper, + final HttpServletResponse pResponse) { + boolean result = false; + + if(pPasswordWrapper.getNewPassword().equals(pPasswordWrapper.getConfirmPassword())) { + // We fetch the connected user from database to get his hashed password + final Optional userFromDb = userRepository.findById(pUser.getId()); + if(userFromDb.isPresent() && StringUtils.compareHash(pPasswordWrapper.getOldPassword(), + userFromDb.get().getPassword())) { + result = true; + userFromDb.get().setPassword(StringUtils.hashPassword(pPasswordWrapper.getNewPassword())); + userRepository.save(userFromDb.get()); + } else { + pResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); + } + } + + return result; + } +} diff --git a/src/main/java/org/codiki/categories/CategoryController.java b/src/main/java/org/codiki/categories/CategoryController.java new file mode 100644 index 0000000..719735c --- /dev/null +++ b/src/main/java/org/codiki/categories/CategoryController.java @@ -0,0 +1,35 @@ +package org.codiki.categories; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import org.codiki.core.entities.dto.CategoryDTO; +import org.codiki.core.entities.persistence.Category; +import org.codiki.core.repositories.CategoryRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/categories") +public class CategoryController { + + @Autowired + private CategoryRepository categoryRepository; + + @GetMapping("/{id}") + public CategoryDTO findById(@PathVariable("id") final Long pId) { + final Optional result = categoryRepository.findById(pId); + return result.isPresent() ? new CategoryDTO(result.get()) : null; + } + + @GetMapping("/") + public List getAll() { + return StreamSupport.stream(categoryRepository.findAll().spliterator(), false) + .map(CategoryDTO::new).collect(Collectors.toList()); + } +} diff --git a/src/main/java/org/codiki/core/AbstractFilter.java b/src/main/java/org/codiki/core/AbstractFilter.java index cdbe8c3..ae361fc 100644 --- a/src/main/java/org/codiki/core/AbstractFilter.java +++ b/src/main/java/org/codiki/core/AbstractFilter.java @@ -110,10 +110,12 @@ public abstract class AbstractFilter implements Filter { boolean isMethodFiltered(final Route pRoute, final String pRequestMethod) { boolean result = false; - for(final HttpMethod routeMethod : pRoute.getMethod().get()) { - if(routeMethod.name().equals(pRequestMethod)) { - result = true; - break; + if(pRoute.getMethod().isPresent()) { + for(final HttpMethod routeMethod : pRoute.getMethod().get()) { + if(routeMethod.name().equals(pRequestMethod)) { + result = true; + break; + } } } diff --git a/src/main/java/org/codiki/core/entities/dto/CategoryDTO.java b/src/main/java/org/codiki/core/entities/dto/CategoryDTO.java new file mode 100644 index 0000000..6509298 --- /dev/null +++ b/src/main/java/org/codiki/core/entities/dto/CategoryDTO.java @@ -0,0 +1,36 @@ +package org.codiki.core.entities.dto; + +import org.codiki.core.entities.persistence.Category; + +public class CategoryDTO { + private Long id; + + private String name; + + public CategoryDTO() { + super(); + } + + public CategoryDTO(final Category pCategory) { + this(); + id = pCategory.getId(); + name = pCategory.getName(); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/src/main/java/org/codiki/core/entities/dto/PasswordWrapperDTO.java b/src/main/java/org/codiki/core/entities/dto/PasswordWrapperDTO.java new file mode 100644 index 0000000..4f95629 --- /dev/null +++ b/src/main/java/org/codiki/core/entities/dto/PasswordWrapperDTO.java @@ -0,0 +1,34 @@ +package org.codiki.core.entities.dto; + +public class PasswordWrapperDTO { + + private String oldPassword; + + private String newPassword; + + private String confirmPassword; + + public String getOldPassword() { + return oldPassword; + } + + public void setOldPassword(String oldPassword) { + this.oldPassword = oldPassword; + } + + public String getNewPassword() { + return newPassword; + } + + public void setNewPassword(String newPassword) { + this.newPassword = newPassword; + } + + public String getConfirmPassword() { + return confirmPassword; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } +} diff --git a/src/main/java/org/codiki/core/entities/dto/PostDTO.java b/src/main/java/org/codiki/core/entities/dto/PostDTO.java new file mode 100644 index 0000000..ef8473c --- /dev/null +++ b/src/main/java/org/codiki/core/entities/dto/PostDTO.java @@ -0,0 +1,105 @@ +package org.codiki.core.entities.dto; + +import java.util.Date; + +import org.codiki.core.entities.persistence.Post; + +public class PostDTO { + + private String key; + + private String title; + + private String text; + + private String description; + + private String image; + + private Date creationDate; + + private UserDTO author; + + private CategoryDTO category; + + public PostDTO() { + super(); + } + + public PostDTO(final Post pPost) { + this(); + key = pPost.getKey(); + title = pPost.getTitle(); + text = pPost.getText(); + description = pPost.getDescription(); + image = pPost.getImage(); + creationDate = pPost.getCreationDate(); + author = new UserDTO(pPost.getAuthor()); + category = new CategoryDTO(pPost.getCategory()); + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public Date getCreationDate() { + return creationDate; + } + + public void setCreationDate(Date creationDate) { + this.creationDate = creationDate; + } + + public UserDTO getAuthor() { + return author; + } + + public void setAuthor(UserDTO author) { + this.author = author; + } + + public CategoryDTO getCategory() { + return category; + } + + public void setCategory(CategoryDTO category) { + this.category = category; + } + +} diff --git a/src/main/java/org/codiki/core/entities/dto/UserDAO.java b/src/main/java/org/codiki/core/entities/dto/UserDTO.java similarity index 87% rename from src/main/java/org/codiki/core/entities/dto/UserDAO.java rename to src/main/java/org/codiki/core/entities/dto/UserDTO.java index 724c63d..ced140a 100644 --- a/src/main/java/org/codiki/core/entities/dto/UserDAO.java +++ b/src/main/java/org/codiki/core/entities/dto/UserDTO.java @@ -5,7 +5,7 @@ import java.util.Date; import org.codiki.core.entities.persistence.Role; import org.codiki.core.entities.persistence.User; -public class UserDAO { +public class UserDTO { private String key; @@ -23,18 +23,24 @@ public class UserDAO { private String token; - public UserDAO() { + public UserDTO() { super(); } - public UserDAO(final User pUser) { + public UserDTO(final User pUser) { key = pUser.getKey(); name = pUser.getName(); email = pUser.getEmail(); image = pUser.getImage(); inscriptionDate = pUser.getInscriptionDate(); role = pUser.getRole(); - token = pUser.getToken().getValue(); + } + + public UserDTO(final User pUser, final boolean pWithToken) { + this(pUser); + if(pWithToken) { + token = pUser.getToken().getValue(); + } } public String getKey() { diff --git a/src/main/java/org/codiki/core/entities/persistence/Category.java b/src/main/java/org/codiki/core/entities/persistence/Category.java index 25de86a..17f4ea7 100644 --- a/src/main/java/org/codiki/core/entities/persistence/Category.java +++ b/src/main/java/org/codiki/core/entities/persistence/Category.java @@ -14,6 +14,8 @@ import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.Table; +import org.codiki.core.entities.dto.CategoryDTO; + @Entity @Table(name="category") @Inheritance(strategy = InheritanceType.JOINED) @@ -42,6 +44,19 @@ public class Category implements Serializable { @OneToMany(mappedBy = "category") protected List listPosts; + /* ******************* */ + /* Constructors */ + /* ******************* */ + public Category() { + super(); + } + + public Category(final CategoryDTO pCategory) { + this(); + id = pCategory.getId(); + name = pCategory.getName(); + } + /* ******************* */ /* Getters & Setters */ /* ******************* */ diff --git a/src/main/java/org/codiki/core/entities/persistence/Post.java b/src/main/java/org/codiki/core/entities/persistence/Post.java index 289cc9d..0182915 100644 --- a/src/main/java/org/codiki/core/entities/persistence/Post.java +++ b/src/main/java/org/codiki/core/entities/persistence/Post.java @@ -18,7 +18,10 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import org.codiki.core.entities.dto.PostDTO; import org.codiki.core.utils.DateUtils; +import org.hibernate.annotations.Generated; +import org.hibernate.annotations.GenerationTime; @Entity @Table(name="post") @@ -35,6 +38,8 @@ public class Post implements Serializable { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + // This annotation serves to fetch the attribute after an insert into db + @Generated(GenerationTime.ALWAYS) private String key; private String title; @@ -70,6 +75,15 @@ public class Post implements Serializable { public Post() { super(); } + + public Post(final PostDTO pPost) { + title = pPost.getTitle(); + image = pPost.getImage(); + description = pPost.getDescription(); + creationDate = new Date(); + text = pPost.getText(); + category = new Category(pPost.getCategory()); + } /* ******************* */ /* Getters & Setters */ diff --git a/src/main/java/org/codiki/core/entities/security/Token.java b/src/main/java/org/codiki/core/entities/security/Token.java index 15afb7e..dc1b95f 100644 --- a/src/main/java/org/codiki/core/entities/security/Token.java +++ b/src/main/java/org/codiki/core/entities/security/Token.java @@ -11,7 +11,7 @@ public class Token { private static final int DELAY = 30; /** The Constant BITS_NUMBER. */ - private static final int BITS_NUMBER = 130; + private static final int BITS_NUMBER = 1000; /** The Constant RADIX. */ private static final int RADIX = 32; diff --git a/src/main/java/org/codiki/core/repositories/CategoryRepository.java b/src/main/java/org/codiki/core/repositories/CategoryRepository.java new file mode 100644 index 0000000..104c8b2 --- /dev/null +++ b/src/main/java/org/codiki/core/repositories/CategoryRepository.java @@ -0,0 +1,10 @@ +package org.codiki.core.repositories; + +import org.codiki.core.entities.persistence.Category; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CategoryRepository extends CrudRepository { + +} diff --git a/src/main/java/org/codiki/core/repositories/PostRepository.java b/src/main/java/org/codiki/core/repositories/PostRepository.java index 460ffc8..f428d36 100644 --- a/src/main/java/org/codiki/core/repositories/PostRepository.java +++ b/src/main/java/org/codiki/core/repositories/PostRepository.java @@ -1,10 +1,32 @@ package org.codiki.core.repositories; +import java.util.List; +import java.util.Optional; + import org.codiki.core.entities.persistence.Post; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository public interface PostRepository extends CrudRepository { + @Query("SELECT p FROM Post p WHERE p.key = :postKey") + Optional getByKey(@Param("postKey") final String pPostKey); + + @Query(value = "SELECT * FROM post p INNER JOIN \"user\" u ON u.id = p.creator_id ORDER BY p.creation_date DESC LIMIT :limit", + nativeQuery = true) + List getLast(@Param("limit") final Integer pLimit); + + @Query(value = "SELECT * FROM post p WHERE category_id = :categoryId ORDER BY creation_date DESC", + nativeQuery = true) + List getByCategoryId(@Param("categoryId") final Long pCategoryId); + + @Query(value = "SELECT * FROM post WHERE creator_id = :creatorId ORDER BY creation_date DESC", + nativeQuery = true) + List getByCreator(@Param("creatorId") final Long pCreatorId); + + @Query(value = "SELECT * FROM Post WHERE id = :id", nativeQuery = true) + Optional getOne(@Param("id") final Long id); } diff --git a/src/main/java/org/codiki/core/repositories/UserRepository.java b/src/main/java/org/codiki/core/repositories/UserRepository.java index eec187a..2f224fa 100644 --- a/src/main/java/org/codiki/core/repositories/UserRepository.java +++ b/src/main/java/org/codiki/core/repositories/UserRepository.java @@ -2,12 +2,38 @@ package org.codiki.core.repositories; import java.util.Optional; +import javax.transaction.Transactional; + import org.codiki.core.entities.persistence.User; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository public interface UserRepository extends CrudRepository { + Optional findByEmail(@Param("email") final String pEmail); + + /** + * Checks if the password in parameters is the passwords of the user in + * database. + * + * @param pId + * The user id. + * @param pPassword + * The password to check. + * @return {@code true} if the password is the user password in database, + * {@code false} otherwise. + */ + @Query(value = "SELECT CASE WHEN EXISTS(" + + " SELECT id FROM \"user\" WHERE id = :id AND password = :password" + + ") THEN TRUE ELSE FALSE END", nativeQuery = true) + boolean checkPassword(@Param("id") final Long pId, @Param("password") final String pPassword); + + @Query(value = "UPDATE \"user\" SET password = :password WHERE id = :id", nativeQuery = true) + @Transactional + @Modifying + void updatePassword(@Param("id") final Long pId, @Param("password") final String pPassword); } diff --git a/src/main/java/org/codiki/core/security/AuthenticationFilter.java b/src/main/java/org/codiki/core/security/AuthenticationFilter.java index 50b4819..d2fb4ed 100644 --- a/src/main/java/org/codiki/core/security/AuthenticationFilter.java +++ b/src/main/java/org/codiki/core/security/AuthenticationFilter.java @@ -6,7 +6,6 @@ import java.util.List; import javax.servlet.FilterChain; import javax.servlet.ServletException; -import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -26,7 +25,7 @@ public class AuthenticationFilter extends AbstractFilter { @Override protected List getRoutes() { return Arrays.asList( - new Route("\\/api\\/posts.*") + new Route("\\/api\\/posts\\/myPosts") ); } @@ -34,7 +33,7 @@ public class AuthenticationFilter extends AbstractFilter { protected void filter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("Token : " + request.getHeader("token")); - if(tokenService.isUserConnected(request.getHeader("token"))) { + if("OPTIONS".equals(request.getMethod()) || tokenService.isUserConnected(request.getHeader("token"))) { chain.doFilter(request, response); } else { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); diff --git a/src/main/java/org/codiki/core/security/TokenService.java b/src/main/java/org/codiki/core/security/TokenService.java index d0ccbe4..2b24b85 100644 --- a/src/main/java/org/codiki/core/security/TokenService.java +++ b/src/main/java/org/codiki/core/security/TokenService.java @@ -1,8 +1,12 @@ package org.codiki.core.security; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.TreeMap; +import javax.servlet.http.HttpServletRequest; + import org.codiki.core.entities.persistence.User; import org.springframework.stereotype.Service; @@ -10,6 +14,8 @@ import org.springframework.stereotype.Service; public class TokenService { /** Map of connected users. */ private static final Map connectedUsers; + + private static final String HEADER_TOKEN = "token"; /** * Class constructor @@ -49,11 +55,15 @@ public class TokenService { */ @SuppressWarnings("unlikely-arg-type") private void clearExpiredUsers() { - connectedUsers.entrySet().stream().forEach(user -> { - if(!user.getValue().getToken().isValid()) { - connectedUsers.remove(user).getKey(); - } - }); + synchronized (this) { + List usersToRemove = new LinkedList<>(); + connectedUsers.entrySet().stream().forEach(user -> { + if(!user.getValue().getToken().isValid()) { + usersToRemove.add(user.getValue()); + } + }); + usersToRemove.stream().forEach(connectedUsers::remove); + } } /** @@ -103,4 +113,8 @@ public class TokenService { connectedUsers.remove(pToken); } } + + public User getAuthenticatedUserByToken(final HttpServletRequest pRequest) { + return connectedUsers.get(pRequest.getHeader(HEADER_TOKEN)); + } } diff --git a/src/main/java/org/codiki/core/services/ParserService.java b/src/main/java/org/codiki/core/services/ParserService.java new file mode 100644 index 0000000..1533971 --- /dev/null +++ b/src/main/java/org/codiki/core/services/ParserService.java @@ -0,0 +1,131 @@ +package org.codiki.core.services; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang.StringEscapeUtils; +import org.codiki.core.utils.StringUtils; +import org.springframework.stereotype.Service; + +@Service +public class ParserService { + + private static final String REG_CODE = "\\[code lg="([a-z]+)"\\](.*)\\[\\/code\\]\\n"; + private static final String REG_CODE_REPLACE = "
\\2
"; + private static final String REG_IMAGES = "\\[img src="([^\"| ]+)"( alt="([^\"| ]+)")? \\/\\]"; + private static final String REG_IMAGES_REPLACE = "\"\\3\""; + private static final String REG_LINKS = "\\[link href="([^\"| ]+)" txt="([^\"| ]+)" \\/\\]"; + private static final String REG_LINKS_REPLACE = "\\2"; + + static final Pattern PATTERN_CODE; + static final Pattern PATTERN_CODE_REPLACE; + static final Pattern PATTERN_IMAGES; + static final Pattern PATTERN_IMAGES_REPLACE; + static final Pattern PATTERN_LINKS; + static final Pattern PATTERN_LINKS_REPLACE; + + static { + PATTERN_CODE = Pattern.compile(REG_CODE); + PATTERN_CODE_REPLACE = Pattern.compile(REG_CODE_REPLACE); + PATTERN_IMAGES = Pattern.compile(REG_IMAGES); + PATTERN_IMAGES_REPLACE = Pattern.compile(REG_IMAGES_REPLACE); + PATTERN_LINKS = Pattern.compile(REG_LINKS); + PATTERN_LINKS_REPLACE = Pattern.compile(REG_LINKS_REPLACE); + } + + public String parse(String pSource) { + return unParseDolars(parseCode(parseHeaders(parseImages(parseLinks(parseBackSpaces(StringEscapeUtils.escapeHtml(parseDolars(pSource)))))))); + } + + private String parseDolars(final String pSource) { + return pSource.replace("$", "£ø"); + } + + private String unParseDolars(final String pSource) { + return pSource.replace("£ø", "$"); + } + + protected String parseHeaders(final String pSource) { + String result = pSource; + for(int i = 1 ; i <= 3 ; i++) { + result = parseHeadersHX(result, i); + } + return result; + } + + protected String parseHeadersHX(final String pSource, final int pNumHeader) { + String result = pSource; + + // (.*)(\[hX\](.*)\[\/hX\])+(.*) + final String regex = StringUtils.concat("(.*)(\\[h", pNumHeader, "\\](.*)\\[\\/h", pNumHeader, "\\])+(.*)"); + + final Pattern pattern = Pattern.compile(regex); + + Matcher matcher = pattern.matcher(result); + + while(matcher.find()) { + // \1\3\4 + result = matcher.replaceFirst(StringUtils.concat(matcher.group(1), + "", matcher.group(3), "", matcher.group(4))); + matcher = pattern.matcher(result); + } + + return result; + } + + protected String parseBackSpaces(final String pSource) { + return pSource.replaceAll("\r?\n", "
").replaceAll("\\[\\/code\\]", "[/code]\n"); + } + + protected String parseImages(final String pSource) { + String result = pSource; + + Matcher matcher = PATTERN_IMAGES.matcher(result); + + while(matcher.find()) { + String altStr = matcher.group(3); + + if(altStr == null) { + altStr = ""; + } + + result = matcher.replaceFirst(StringUtils.concat("\"",")); + matcher = PATTERN_IMAGES.matcher(result); + } + + return result; + } + + protected String parseLinks(final String pSource) { + String result = pSource; + + Matcher matcher = PATTERN_LINKS.matcher(result); + + while(matcher.find()) { + result = matcher.replaceFirst(StringUtils.concat("", matcher.group(2), "")); + matcher = PATTERN_LINKS.matcher(result); + } + + return result; + } + + protected String parseCode(final String pSource) { + String result = pSource; + + Matcher matcher = PATTERN_CODE.matcher(result); + + while(matcher.find()) { + // replace the '
' in group by '\n' + String codeContent = matcher.group(2).replaceAll("", "\n"); + if(codeContent.startsWith("\n")) { + codeContent = codeContent.substring(1); + } + + result = matcher.replaceFirst(StringUtils.concat("
", codeContent, "
")); + matcher = PATTERN_CODE.matcher(result); + } + + return result; + } + +} \ No newline at end of file diff --git a/src/main/java/org/codiki/login/LoginService.java b/src/main/java/org/codiki/login/LoginService.java deleted file mode 100644 index 1e92141..0000000 --- a/src/main/java/org/codiki/login/LoginService.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.codiki.login; - -import java.util.Optional; - -import javax.naming.AuthenticationException; -import javax.servlet.http.HttpServletResponse; - -import org.codiki.core.entities.dto.UserDAO; -import org.codiki.core.entities.persistence.User; -import org.codiki.core.repositories.UserRepository; -import org.codiki.core.security.TokenService; -import org.codiki.core.utils.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -@Service -public class LoginService { - - @Autowired - private UserRepository userRepository; - - @Autowired - private TokenService tokenService; - - /** - * Check the user credentials and generate him a token if they are correct. - * - * @param pUser - * The user sent from client. - * @return The user populated with the generated token. - * @throws AuthenticationException - * If the credentials are wrong. - */ - public UserDAO checkCredentials(HttpServletResponse pResponse, UserDAO pUser) { - UserDAO result = null; - - Optional user = userRepository.findByEmail(pUser.getEmail()); - - if(user.isPresent() && StringUtils.compareHash(pUser.getPassword(), user.get().getPassword())) { - tokenService.addUser(user.get()); - result = new UserDAO(user.get()); - } else { - pResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); - } - - return result; - } - - -} diff --git a/src/main/java/org/codiki/posts/PostController.java b/src/main/java/org/codiki/posts/PostController.java index da819d7..91f8634 100644 --- a/src/main/java/org/codiki/posts/PostController.java +++ b/src/main/java/org/codiki/posts/PostController.java @@ -1,9 +1,23 @@ package org.codiki.posts; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.codiki.core.entities.dto.PostDTO; import org.codiki.core.entities.persistence.Post; import org.codiki.core.repositories.PostRepository; +import org.codiki.core.security.TokenService; +import org.codiki.core.services.ParserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -11,11 +25,92 @@ import org.springframework.web.bind.annotation.RestController; @RequestMapping("/api/posts") public class PostController { + private static final int LIMIT_POSTS_HOME = 20; + + @Autowired + private ParserService parserService; + @Autowired private PostRepository postRepository; + @Autowired + private TokenService tokenService; + + @Autowired + private PostService postService; + @GetMapping - public Iterable getAll() { - return postRepository.findAll(); + public List getAll() { + return StreamSupport.stream(postRepository.findAll().spliterator(), false) + .map(PostDTO::new).collect(Collectors.toList()); + } + + @GetMapping("/{postKey}") + public PostDTO getByKey(@PathVariable("postKey") final String pPostKey, + final HttpServletResponse response) { + PostDTO result = null; + + final Optional post = postRepository.getByKey(pPostKey); + if(post.isPresent()) { + result = new PostDTO(post.get()); + result.setText(parserService.parse(result.getText())); + } else { + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + } + + return result; + } + + @GetMapping("/last") + public List getLast() { + return postRepository.getLast(LIMIT_POSTS_HOME).stream() + .map(PostDTO::new).collect(Collectors.toList()); + } + + @PostMapping("/search") + public List search() { + // TODO + return null; + } + + @GetMapping("/byCategory/{categoryId}") + public List getByCategory(@PathVariable("categoryId") final Long pCategoryId) { + return postRepository.getByCategoryId(pCategoryId).stream() + .map(PostDTO::new).collect(Collectors.toList()); + } + + @GetMapping("/myPosts") + public List getMyPosts(final HttpServletRequest pRequest, final HttpServletResponse pResponse) { + return postRepository.getByCreator(tokenService + .getAuthenticatedUserByToken(pRequest).getId()) + .parallelStream().map(PostDTO::new).collect(Collectors.toList()); + } + + @PostMapping("/preview") + public PostDTO preview(@RequestBody final PostDTO pPost) { + final PostDTO result = new PostDTO(); + + result.setTitle(pPost.getTitle()); + result.setImage(pPost.getImage() == null || "".equals(pPost.getImage()) + ? "https://news-cdn.softpedia.com/images/news2/this-is-the-default-wallpaper-of-the-gnome-3-20-desktop-environment-500743-2.jpg" + : pPost.getImage()); + result.setDescription(pPost.getDescription()); + result.setText(parserService.parse(pPost.getText())); + + return result; + } + + @PostMapping("/") + public PostDTO insert(@RequestBody final PostDTO pPost, final HttpServletRequest pRequest, + final HttpServletResponse pResponse) { + PostDTO result = null; + + Optional postCreated = postService.insert(pPost, pRequest, pResponse); + + if(postCreated.isPresent()) { + result = new PostDTO(postCreated.get()); + } + + return result; } } diff --git a/src/main/java/org/codiki/posts/PostService.java b/src/main/java/org/codiki/posts/PostService.java new file mode 100644 index 0000000..5e741db --- /dev/null +++ b/src/main/java/org/codiki/posts/PostService.java @@ -0,0 +1,41 @@ +package org.codiki.posts; + +import java.util.Optional; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.codiki.core.entities.dto.PostDTO; +import org.codiki.core.entities.persistence.Post; +import org.codiki.core.repositories.PostRepository; +import org.codiki.core.security.TokenService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class PostService { + + @Autowired + private PostRepository postRepository; + + @Autowired + private TokenService tokenService; + + public Optional insert(final PostDTO pPost, final HttpServletRequest pRequest, + final HttpServletResponse pResponse) { + Optional result = Optional.empty(); + + final String userToken = pRequest.getHeader("token"); + + if(userToken.equals(pPost.getAuthor().getToken())) { + final Post postToSave = new Post(pPost); + postToSave.setAuthor(tokenService.getAuthenticatedUserByToken(pRequest)); + postRepository.save(postToSave); + result = Optional.of(postToSave); + } else { + pResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); + } + + return result; + } +}