Save the actual version of the embryon api.

This commit is contained in:
Florian
2018-05-12 23:29:55 +02:00
parent 3f9f3d5ad1
commit 3928efbae9
21 changed files with 697 additions and 78 deletions

View File

@@ -59,6 +59,11 @@
<artifactId>postgresql</artifactId> <artifactId>postgresql</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.3</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -1,32 +1,36 @@
package org.codiki.login; package org.codiki.account;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; 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.codiki.core.security.TokenService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; 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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@RequestMapping("/api/account") @RequestMapping("/api/account")
public class LoginController { public class AccountController {
private static final String HEADER_TOKEN = "token"; private static final String HEADER_TOKEN = "token";
@Autowired @Autowired
private TokenService tokenService; private AccountService accountService;
@Autowired
private LoginService loginService;
@Autowired
private TokenService tokenService;
@PostMapping("/login") @PostMapping("/login")
public UserDAO login(@RequestBody UserDAO pUser, HttpServletResponse response) { public UserDTO login(@RequestBody UserDTO pUser, HttpServletResponse response) {
return loginService.checkCredentials(response, pUser); return accountService.checkCredentials(response, pUser);
} }
@GetMapping("/logout") @GetMapping("/logout")
@@ -34,4 +38,10 @@ public class LoginController {
tokenService.removeUser(pRequest.getHeader(HEADER_TOKEN)); 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);
}
} }

View File

@@ -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> 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<User> 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;
}
}

View File

@@ -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<Category> result = categoryRepository.findById(pId);
return result.isPresent() ? new CategoryDTO(result.get()) : null;
}
@GetMapping("/")
public List<CategoryDTO> getAll() {
return StreamSupport.stream(categoryRepository.findAll().spliterator(), false)
.map(CategoryDTO::new).collect(Collectors.toList());
}
}

View File

@@ -110,10 +110,12 @@ public abstract class AbstractFilter implements Filter {
boolean isMethodFiltered(final Route pRoute, final String pRequestMethod) { boolean isMethodFiltered(final Route pRoute, final String pRequestMethod) {
boolean result = false; boolean result = false;
for(final HttpMethod routeMethod : pRoute.getMethod().get()) { if(pRoute.getMethod().isPresent()) {
if(routeMethod.name().equals(pRequestMethod)) { for(final HttpMethod routeMethod : pRoute.getMethod().get()) {
result = true; if(routeMethod.name().equals(pRequestMethod)) {
break; result = true;
break;
}
} }
} }

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -5,7 +5,7 @@ import java.util.Date;
import org.codiki.core.entities.persistence.Role; import org.codiki.core.entities.persistence.Role;
import org.codiki.core.entities.persistence.User; import org.codiki.core.entities.persistence.User;
public class UserDAO { public class UserDTO {
private String key; private String key;
@@ -23,18 +23,24 @@ public class UserDAO {
private String token; private String token;
public UserDAO() { public UserDTO() {
super(); super();
} }
public UserDAO(final User pUser) { public UserDTO(final User pUser) {
key = pUser.getKey(); key = pUser.getKey();
name = pUser.getName(); name = pUser.getName();
email = pUser.getEmail(); email = pUser.getEmail();
image = pUser.getImage(); image = pUser.getImage();
inscriptionDate = pUser.getInscriptionDate(); inscriptionDate = pUser.getInscriptionDate();
role = pUser.getRole(); 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() { public String getKey() {

View File

@@ -14,6 +14,8 @@ import javax.persistence.ManyToOne;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import javax.persistence.Table; import javax.persistence.Table;
import org.codiki.core.entities.dto.CategoryDTO;
@Entity @Entity
@Table(name="category") @Table(name="category")
@Inheritance(strategy = InheritanceType.JOINED) @Inheritance(strategy = InheritanceType.JOINED)
@@ -42,6 +44,19 @@ public class Category implements Serializable {
@OneToMany(mappedBy = "category") @OneToMany(mappedBy = "category")
protected List<Post> listPosts; protected List<Post> listPosts;
/* ******************* */
/* Constructors */
/* ******************* */
public Category() {
super();
}
public Category(final CategoryDTO pCategory) {
this();
id = pCategory.getId();
name = pCategory.getName();
}
/* ******************* */ /* ******************* */
/* Getters & Setters */ /* Getters & Setters */
/* ******************* */ /* ******************* */

View File

@@ -18,7 +18,10 @@ import javax.persistence.Table;
import javax.persistence.Temporal; import javax.persistence.Temporal;
import javax.persistence.TemporalType; import javax.persistence.TemporalType;
import org.codiki.core.entities.dto.PostDTO;
import org.codiki.core.utils.DateUtils; import org.codiki.core.utils.DateUtils;
import org.hibernate.annotations.Generated;
import org.hibernate.annotations.GenerationTime;
@Entity @Entity
@Table(name="post") @Table(name="post")
@@ -35,6 +38,8 @@ public class Post implements Serializable {
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; private Long id;
// This annotation serves to fetch the attribute after an insert into db
@Generated(GenerationTime.ALWAYS)
private String key; private String key;
private String title; private String title;
@@ -70,6 +75,15 @@ public class Post implements Serializable {
public Post() { public Post() {
super(); 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 */ /* Getters & Setters */

View File

@@ -11,7 +11,7 @@ public class Token {
private static final int DELAY = 30; private static final int DELAY = 30;
/** The Constant BITS_NUMBER. */ /** The Constant BITS_NUMBER. */
private static final int BITS_NUMBER = 130; private static final int BITS_NUMBER = 1000;
/** The Constant RADIX. */ /** The Constant RADIX. */
private static final int RADIX = 32; private static final int RADIX = 32;

View File

@@ -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<Category, Long> {
}

View File

@@ -1,10 +1,32 @@
package org.codiki.core.repositories; package org.codiki.core.repositories;
import java.util.List;
import java.util.Optional;
import org.codiki.core.entities.persistence.Post; 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.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@Repository @Repository
public interface PostRepository extends CrudRepository<Post, Long> { public interface PostRepository extends CrudRepository<Post, Long> {
@Query("SELECT p FROM Post p WHERE p.key = :postKey")
Optional<Post> 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<Post> getLast(@Param("limit") final Integer pLimit);
@Query(value = "SELECT * FROM post p WHERE category_id = :categoryId ORDER BY creation_date DESC",
nativeQuery = true)
List<Post> getByCategoryId(@Param("categoryId") final Long pCategoryId);
@Query(value = "SELECT * FROM post WHERE creator_id = :creatorId ORDER BY creation_date DESC",
nativeQuery = true)
List<Post> getByCreator(@Param("creatorId") final Long pCreatorId);
@Query(value = "SELECT * FROM Post WHERE id = :id", nativeQuery = true)
Optional<Post> getOne(@Param("id") final Long id);
} }

View File

@@ -2,12 +2,38 @@ package org.codiki.core.repositories;
import java.util.Optional; import java.util.Optional;
import javax.transaction.Transactional;
import org.codiki.core.entities.persistence.User; 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.CrudRepository;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@Repository @Repository
public interface UserRepository extends CrudRepository<User, Long> { public interface UserRepository extends CrudRepository<User, Long> {
Optional<User> findByEmail(@Param("email") final String pEmail); Optional<User> 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);
} }

View File

@@ -6,7 +6,6 @@ import java.util.List;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@@ -26,7 +25,7 @@ public class AuthenticationFilter extends AbstractFilter {
@Override @Override
protected List<Route> getRoutes() { protected List<Route> getRoutes() {
return Arrays.asList( 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 { protected void filter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Token : " + request.getHeader("token")); 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); chain.doFilter(request, response);
} else { } else {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

View File

@@ -1,8 +1,12 @@
package org.codiki.core.security; package org.codiki.core.security;
import java.util.LinkedList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import org.codiki.core.entities.persistence.User; import org.codiki.core.entities.persistence.User;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -10,6 +14,8 @@ import org.springframework.stereotype.Service;
public class TokenService { public class TokenService {
/** Map of connected users. */ /** Map of connected users. */
private static final Map<String, User> connectedUsers; private static final Map<String, User> connectedUsers;
private static final String HEADER_TOKEN = "token";
/** /**
* Class constructor * Class constructor
@@ -49,11 +55,15 @@ public class TokenService {
*/ */
@SuppressWarnings("unlikely-arg-type") @SuppressWarnings("unlikely-arg-type")
private void clearExpiredUsers() { private void clearExpiredUsers() {
connectedUsers.entrySet().stream().forEach(user -> { synchronized (this) {
if(!user.getValue().getToken().isValid()) { List<User> usersToRemove = new LinkedList<>();
connectedUsers.remove(user).getKey(); 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); connectedUsers.remove(pToken);
} }
} }
public User getAuthenticatedUserByToken(final HttpServletRequest pRequest) {
return connectedUsers.get(pRequest.getHeader(HEADER_TOKEN));
}
} }

View File

@@ -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=&quot;([a-z]+)&quot;\\](.*)\\[\\/code\\]\\n";
private static final String REG_CODE_REPLACE = "<pre class=\"line-numbers\"><code class=\"language-\\1\">\\2</code></pre>";
private static final String REG_IMAGES = "\\[img src=&quot;([^\"| ]+)&quot;( alt=&quot;([^\"| ]+)&quot;)? \\/\\]";
private static final String REG_IMAGES_REPLACE = "<img src=\"\\1\" alt=\"\\3\" />";
private static final String REG_LINKS = "\\[link href=&quot;([^\"| ]+)&quot; txt=&quot;([^\"| ]+)&quot; \\/\\]";
private static final String REG_LINKS_REPLACE = "<a href=\"\\1\">\\2</a>";
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("&pound;&oslash;", "$");
}
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<hX>\3</hX>\4
result = matcher.replaceFirst(StringUtils.concat(matcher.group(1),
"<h", pNumHeader, ">", matcher.group(3), "</h", pNumHeader, ">", matcher.group(4)));
matcher = pattern.matcher(result);
}
return result;
}
protected String parseBackSpaces(final String pSource) {
return pSource.replaceAll("\r?\n", "<br/>").replaceAll("\\[\\/code\\]<br\\/><br\\/>", "[/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("<img src=\"", matcher.group(1), "\" alt=\"", altStr, "\" />"));
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("<a href=\"", matcher.group(1), "\">", matcher.group(2), "</a>"));
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 '<br/>' in group by '\n'
String codeContent = matcher.group(2).replaceAll("<br\\/>", "\n");
if(codeContent.startsWith("\n")) {
codeContent = codeContent.substring(1);
}
result = matcher.replaceFirst(StringUtils.concat("<pre class=\"line-numbers\"><code class=\"language-", matcher.group(1), "\">", codeContent, "</code></pre>"));
matcher = PATTERN_CODE.matcher(result);
}
return result;
}
}

View File

@@ -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> 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;
}
}

View File

@@ -1,9 +1,23 @@
package org.codiki.posts; 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.entities.persistence.Post;
import org.codiki.core.repositories.PostRepository; 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.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; 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.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@@ -11,11 +25,92 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/api/posts") @RequestMapping("/api/posts")
public class PostController { public class PostController {
private static final int LIMIT_POSTS_HOME = 20;
@Autowired
private ParserService parserService;
@Autowired @Autowired
private PostRepository postRepository; private PostRepository postRepository;
@Autowired
private TokenService tokenService;
@Autowired
private PostService postService;
@GetMapping @GetMapping
public Iterable<Post> getAll() { public List<PostDTO> getAll() {
return postRepository.findAll(); 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> 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<PostDTO> getLast() {
return postRepository.getLast(LIMIT_POSTS_HOME).stream()
.map(PostDTO::new).collect(Collectors.toList());
}
@PostMapping("/search")
public List<Post> search() {
// TODO
return null;
}
@GetMapping("/byCategory/{categoryId}")
public List<PostDTO> getByCategory(@PathVariable("categoryId") final Long pCategoryId) {
return postRepository.getByCategoryId(pCategoryId).stream()
.map(PostDTO::new).collect(Collectors.toList());
}
@GetMapping("/myPosts")
public List<PostDTO> 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<Post> postCreated = postService.insert(pPost, pRequest, pResponse);
if(postCreated.isPresent()) {
result = new PostDTO(postCreated.get());
}
return result;
} }
} }

View File

@@ -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<Post> insert(final PostDTO pPost, final HttpServletRequest pRequest,
final HttpServletResponse pResponse) {
Optional<Post> 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;
}
}