aboutsummaryrefslogtreecommitdiff
path: root/juick-server-web
diff options
context:
space:
mode:
authorGravatar Vitaly Takmazov2017-06-29 14:03:04 +0300
committerGravatar Vitaly Takmazov2017-06-29 14:03:04 +0300
commit02723131139806c761539a42a5fa80b68ecadee8 (patch)
treeca66f22993908758385f708eb2da4e4aeb20510a /juick-server-web
parent4cc4b08f4377b7db697abdb533c625da608eb3d3 (diff)
project structure: split server into jdbc + web
Diffstat (limited to 'juick-server-web')
-rw-r--r--juick-server-web/build.gradle76
-rw-r--r--juick-server-web/src/main/java/com/juick/server/util/HttpBadRequestException.java15
-rw-r--r--juick-server-web/src/main/java/com/juick/server/util/HttpForbiddenException.java16
-rw-r--r--juick-server-web/src/main/java/com/juick/server/util/HttpNotFoundException.java15
-rw-r--r--juick-server-web/src/main/java/com/juick/server/util/HttpUtils.java112
-rw-r--r--juick-server-web/src/main/java/com/juick/server/util/ImageUtils.java66
-rw-r--r--juick-server-web/src/main/java/com/juick/server/util/UserUtils.java36
-rw-r--r--juick-server-web/src/main/java/com/juick/server/util/WebUtils.java45
-rw-r--r--juick-server-web/src/main/java/com/juick/service/BaseRestService.java18
-rw-r--r--juick-server-web/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java83
-rw-r--r--juick-server-web/src/main/java/com/juick/service/security/JuickUserDetailsService.java34
-rw-r--r--juick-server-web/src/main/java/com/juick/service/security/NotAuthorizedAuthenticationEntryPoint.java20
-rw-r--r--juick-server-web/src/main/java/com/juick/service/security/NullUserDetailsService.java16
-rw-r--r--juick-server-web/src/main/java/com/juick/service/security/deprecated/CookieSimpleHashRememberMeServices.java113
-rw-r--r--juick-server-web/src/main/java/com/juick/service/security/deprecated/RequestParamHashRememberMeServices.java71
-rw-r--r--juick-server-web/src/main/java/com/juick/service/security/entities/JuickUser.java75
16 files changed, 811 insertions, 0 deletions
diff --git a/juick-server-web/build.gradle b/juick-server-web/build.gradle
new file mode 100644
index 00000000..fde15afe
--- /dev/null
+++ b/juick-server-web/build.gradle
@@ -0,0 +1,76 @@
+apply plugin: 'java'
+apply plugin: 'war'
+
+sourceCompatibility = 1.8
+
+dependencies {
+ compile project(':juick-server-core')
+
+ compile "com.fasterxml.jackson.core:jackson-core:${rootProject.jacksonVersion}"
+ compile "com.fasterxml.jackson.core:jackson-databind:${rootProject.jacksonVersion}"
+ compile "com.fasterxml.jackson.core:jackson-annotations:${rootProject.jacksonVersion}"
+ compile "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:${rootProject.jacksonVersion}"
+
+ providedCompile "ch.qos.logback:logback-classic:${rootProject.logbackVersion}"
+ providedCompile "ch.qos.logback:logback-core:${rootProject.logbackVersion}"
+ providedCompile "ch.qos.logback:logback-access:${rootProject.logbackVersion}"
+
+ providedCompile "org.slf4j:slf4j-api:${rootProject.slf4jVersion}"
+ providedCompile "org.slf4j:jcl-over-slf4j:${rootProject.slf4jVersion}"
+ providedCompile "org.slf4j:log4j-over-slf4j:${rootProject.slf4jVersion}"
+ providedCompile "org.slf4j:jul-to-slf4j:${rootProject.slf4jVersion}"
+
+ compile "org.apache.httpcomponents:httpclient:4.5.3"
+ providedCompile "org.apache.commons:commons-lang3:3.6"
+ providedCompile "org.apache.commons:commons-collections4:4.1"
+ providedCompile "org.apache.commons:commons-text:1.1"
+ providedCompile "commons-io:commons-io:2.5"
+ providedCompile "commons-codec:commons-codec:1.10"
+
+ compile 'com.github.ben-manes.caffeine:caffeine:2.5.2'
+
+ compile "org.springframework:spring-context:${rootProject.springFrameworkVersion}"
+ compile "org.springframework:spring-jdbc:${rootProject.springFrameworkVersion}"
+
+ compile "org.springframework.security:spring-security-web:${rootProject.springSecurityVersion}"
+ compile "org.springframework.security:spring-security-config:${rootProject.springSecurityVersion}"
+
+ providedCompile "org.apache.commons:commons-dbcp2:2.1.1"
+ compile "com.googlecode.log4jdbc:log4jdbc:1.2"
+ compile "javax.inject:javax.inject:1"
+
+ compile "rocks.xmpp:xmpp-core-client:0.7.4"
+ compile "rocks.xmpp:xmpp-extensions-client:0.7.4"
+
+ compile 'org.imgscalr:imgscalr-lib:4.2'
+
+ providedCompile "javax.servlet:javax.servlet-api:3.1.0"
+
+ providedRuntime "commons-fileupload:commons-fileupload:1.3.3"
+
+ testCompile "ch.vorburger.mariaDB4j:mariaDB4j:2.2.3"
+ testCompile "junit:junit:${rootProject.junitVersion}"
+ testCompile "org.hamcrest:hamcrest-all:${rootProject.hamcrestVersion}"
+ testCompile "org.mockito:mockito-core:${rootProject.mockitoVersion}"
+ testCompile "org.springframework:spring-test:${rootProject.springFrameworkVersion}"
+ testCompile "org.springframework.security:spring-security-test:${rootProject.springSecurityVersion}"
+
+ testRuntime "mysql:mysql-connector-java:5.1.40"
+}
+
+compileJava.options.encoding = 'UTF-8'
+
+configurations {
+ all*.exclude module: 'commons-logging'
+}
+
+configurations {
+ testArtifacts.extendsFrom testRuntime
+}
+task testJar(type: Jar) {
+ classifier "test"
+ from sourceSets.test.output
+}
+artifacts {
+ testArtifacts testJar
+}
diff --git a/juick-server-web/src/main/java/com/juick/server/util/HttpBadRequestException.java b/juick-server-web/src/main/java/com/juick/server/util/HttpBadRequestException.java
new file mode 100644
index 00000000..1ba1aecb
--- /dev/null
+++ b/juick-server-web/src/main/java/com/juick/server/util/HttpBadRequestException.java
@@ -0,0 +1,15 @@
+package com.juick.server.util;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Created by vt on 24/11/2016.
+ */
+@ResponseStatus(value = HttpStatus.BAD_REQUEST)
+public class HttpBadRequestException extends RuntimeException {
+ public HttpBadRequestException() {
+ super(StringUtils.EMPTY, null, false, false);
+ }
+}
diff --git a/juick-server-web/src/main/java/com/juick/server/util/HttpForbiddenException.java b/juick-server-web/src/main/java/com/juick/server/util/HttpForbiddenException.java
new file mode 100644
index 00000000..733453ba
--- /dev/null
+++ b/juick-server-web/src/main/java/com/juick/server/util/HttpForbiddenException.java
@@ -0,0 +1,16 @@
+package com.juick.server.util;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Created by vt on 24/11/2016.
+ */
+@ResponseStatus(value = HttpStatus.FORBIDDEN)
+public class HttpForbiddenException extends RuntimeException {
+ public HttpForbiddenException() {
+ super(StringUtils.EMPTY, null, false, false);
+ }
+
+}
diff --git a/juick-server-web/src/main/java/com/juick/server/util/HttpNotFoundException.java b/juick-server-web/src/main/java/com/juick/server/util/HttpNotFoundException.java
new file mode 100644
index 00000000..942d90e2
--- /dev/null
+++ b/juick-server-web/src/main/java/com/juick/server/util/HttpNotFoundException.java
@@ -0,0 +1,15 @@
+package com.juick.server.util;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Created by vt on 24/11/2016.
+ */
+@ResponseStatus(value = HttpStatus.NOT_FOUND)
+public class HttpNotFoundException extends RuntimeException {
+ public HttpNotFoundException() {
+ super(StringUtils.EMPTY, null, false, false);
+ }
+}
diff --git a/juick-server-web/src/main/java/com/juick/server/util/HttpUtils.java b/juick-server-web/src/main/java/com/juick/server/util/HttpUtils.java
new file mode 100644
index 00000000..31a68962
--- /dev/null
+++ b/juick-server-web/src/main/java/com/juick/server/util/HttpUtils.java
@@ -0,0 +1,112 @@
+/*
+ * Juick
+ * Copyright (C) 2008-2011, Ugnich Anton
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.juick.server.util;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.UUID;
+
+/**
+ *
+ * @author Ugnich Anton
+ */
+public class HttpUtils {
+ private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class);
+
+ public static String receiveMultiPartFile(MultipartFile attach, String tmpDir) {
+ if (attach !=null && !attach.isEmpty()) {
+ String partname = attach.getOriginalFilename();
+ if (partname != null && partname.length() > 0) {
+ String attachmentType = partname.substring(partname.length() - 3).toLowerCase();
+ if (attachmentType.equals("jpg") || attachmentType.equals("peg") || attachmentType.equals("png")) {
+ if (attachmentType.equals("peg")) {
+ attachmentType = "jpg";
+ }
+ String attachmentFName = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + attachmentType;
+ try {
+ Files.write(Paths.get(tmpDir, attachmentFName),
+ attach.getBytes());
+ return attachmentFName;
+ } catch (IOException e) {
+ logger.warn("file receive error", e);
+ }
+ }
+ }
+ }
+ return StringUtils.EMPTY;
+ }
+ public static String downloadImage(URL url, String tmpDir) throws Exception {
+ String attachmentFName = null;
+ Exception ex = null;
+
+ InputStream is = null;
+ FileOutputStream fos = null;
+ try {
+ URLConnection urlConn = url.openConnection();
+ is = urlConn.getInputStream();
+ String mime = urlConn.getContentType();
+
+ String attachmentType;
+ if (mime != null && mime.equals("image/jpeg")) {
+ attachmentType = "jpg";
+ } else if (mime != null && mime.equals("image/png")) {
+ attachmentType = "png";
+ } else {
+ throw new Exception("Wrong file type");
+ }
+
+ attachmentFName = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + attachmentType;
+ fos = new FileOutputStream(Paths.get(tmpDir, attachmentFName).toString());
+ byte[] buffer = new byte[10240];
+ int len;
+ while ((len = is.read(buffer)) > 0) {
+ fos.write(buffer, 0, len);
+ }
+ } catch (Exception e) {
+ ex = e;
+ attachmentFName = null;
+ } finally {
+ try {
+ if (is != null) {
+ is.close();
+ }
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ }
+
+ if (ex != null) {
+ throw ex;
+ } else {
+ return attachmentFName;
+ }
+ }
+}
diff --git a/juick-server-web/src/main/java/com/juick/server/util/ImageUtils.java b/juick-server-web/src/main/java/com/juick/server/util/ImageUtils.java
new file mode 100644
index 00000000..61677750
--- /dev/null
+++ b/juick-server-web/src/main/java/com/juick/server/util/ImageUtils.java
@@ -0,0 +1,66 @@
+
+package com.juick.server.util;
+
+import org.apache.commons.io.FilenameUtils;
+import org.imgscalr.Scalr;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+
+public class ImageUtils {
+
+ /**
+ * Move attached image from temp folder to image folder.
+ * Create preview images in corresponding folders.
+ *
+ * @param tempFilename Name of the image file in temp folder.
+ * @param outputFilename Name that will be used in in image folder.
+ * @param tmpDir Path string for the temp folder.
+ * @param imgDir Path string for the image folder.
+ */
+ public static void saveImageWithPreviews(String tempFilename, String outputFilename, String tmpDir, String imgDir)
+ throws IOException {
+ String ext = FilenameUtils.getExtension(outputFilename);
+
+ Path outputImagePath = Paths.get(imgDir, "p", outputFilename);
+ Files.move(Paths.get(tmpDir, tempFilename), outputImagePath);
+ BufferedImage originalImage = ImageIO.read(outputImagePath.toFile());
+
+ int width = originalImage.getWidth();
+ int height = originalImage.getHeight();
+ int maxDimension = (width > height) ? width : height;
+ BufferedImage image1024 = (maxDimension > 1024) ? Scalr.resize(originalImage, 1024) : originalImage;
+ BufferedImage image0512 = (maxDimension > 512) ? Scalr.resize(originalImage, 512) : originalImage;
+ BufferedImage image0160 = (maxDimension > 160) ? Scalr.resize(originalImage, 160) : originalImage;
+ ImageIO.write(image1024, ext, Paths.get(imgDir, "photos-1024", outputFilename).toFile());
+ ImageIO.write(image0512, ext, Paths.get(imgDir, "photos-512", outputFilename).toFile());
+ ImageIO.write(image0160, ext, Paths.get(imgDir, "ps", outputFilename).toFile());
+ }
+
+ /**
+ * Save new avatar in all required sizes.
+ *
+ * @param tempFilename Name of the image file in temp folder.
+ * @param uid User id that is used to build image file names.
+ * @param tmpDir Path string for the temp folder.
+ * @param imgDir Path string for the image folder.
+ */
+ public static void saveAvatar(String tempFilename, int uid, String tmpDir, String imgDir)
+ throws IOException {
+ String ext = FilenameUtils.getExtension(tempFilename);
+ String originalName = String.format("%s.%s", uid, ext);
+ Path originalPath = Paths.get(imgDir, "ao", originalName);
+ Files.move(Paths.get(tmpDir, tempFilename), originalPath, StandardCopyOption.REPLACE_EXISTING);
+ BufferedImage originalImage = ImageIO.read(originalPath.toFile());
+
+ String targetExt = "png";
+ String targetName = String.format("%s.%s", uid, targetExt);
+ ImageIO.write(Scalr.resize(originalImage, 96), targetExt, Paths.get(imgDir, "a", targetName).toFile());
+ ImageIO.write(Scalr.resize(originalImage, 32), targetExt, Paths.get(imgDir, "as", targetName).toFile());
+ }
+} \ No newline at end of file
diff --git a/juick-server-web/src/main/java/com/juick/server/util/UserUtils.java b/juick-server-web/src/main/java/com/juick/server/util/UserUtils.java
new file mode 100644
index 00000000..eb86370e
--- /dev/null
+++ b/juick-server-web/src/main/java/com/juick/server/util/UserUtils.java
@@ -0,0 +1,36 @@
+package com.juick.server.util;
+
+import com.juick.User;
+import com.juick.server.helpers.AnonymousUser;
+import com.juick.service.security.entities.JuickUser;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+/**
+ * Created by aalexeev on 11/14/16.
+ */
+public class UserUtils {
+ private UserUtils() {
+ throw new IllegalStateException();
+ }
+
+ public static Authentication getAuthentication() {
+ return SecurityContextHolder.getContext().getAuthentication();
+ }
+
+ public static Object getPrincipal(final Authentication authentication) {
+ return authentication == null ? null : authentication.getPrincipal();
+ }
+
+ public static User getCurrentUser() {
+ Object principal = getPrincipal(getAuthentication());
+
+ if (principal instanceof JuickUser)
+ return ((JuickUser) principal).getUser();
+
+ if (principal instanceof User)
+ return (User) principal;
+
+ return AnonymousUser.INSTANCE;
+ }
+}
diff --git a/juick-server-web/src/main/java/com/juick/server/util/WebUtils.java b/juick-server-web/src/main/java/com/juick/server/util/WebUtils.java
new file mode 100644
index 00000000..7f50c89c
--- /dev/null
+++ b/juick-server-web/src/main/java/com/juick/server/util/WebUtils.java
@@ -0,0 +1,45 @@
+package com.juick.server.util;
+
+import java.util.regex.Pattern;
+
+/**
+ * Created by aalexeev on 11/28/16.
+ */
+public class WebUtils {
+ private WebUtils() {
+ throw new IllegalStateException();
+ }
+
+ private static final Pattern USER_NAME_PATTERN = Pattern.compile("[a-zA-Z-_\\d]{2,16}");
+
+ private static final Pattern POST_NUMBER_PATTERN = Pattern.compile("-?\\d+");
+
+ private static final Pattern JID_PATTERN = Pattern.compile("^[a-zA-Z0-9\\\\-\\\\_\\\\@\\\\.]{6,64}$");
+
+
+ public static boolean isPostNumber(final String aString) {
+ return aString != null && POST_NUMBER_PATTERN.matcher(aString).matches();
+ }
+
+ public static boolean isNotPostNumber(final String aString) {
+ return !isPostNumber(aString);
+ }
+
+ public static boolean isUserName(final String aString) {
+ return aString != null && USER_NAME_PATTERN.matcher(aString).matches();
+ }
+
+ public static boolean isNotUserName(final String aString) {
+ return !isUserName(aString);
+ }
+
+ public static boolean isJid(final String aString) {
+ return aString != null && JID_PATTERN.matcher(aString).matches();
+ }
+
+ public static boolean isNotJid(final String aString) {
+ return !isJid(aString);
+ }
+
+
+}
diff --git a/juick-server-web/src/main/java/com/juick/service/BaseRestService.java b/juick-server-web/src/main/java/com/juick/service/BaseRestService.java
new file mode 100644
index 00000000..4ccc3959
--- /dev/null
+++ b/juick-server-web/src/main/java/com/juick/service/BaseRestService.java
@@ -0,0 +1,18 @@
+package com.juick.service;
+
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Created by vitalyster on 15.12.2016.
+ */
+public abstract class BaseRestService {
+ private RestTemplate rest;
+
+ public BaseRestService(RestTemplate rest) {
+ this.rest = rest;
+ }
+
+ public RestTemplate getRest() {
+ return rest;
+ }
+}
diff --git a/juick-server-web/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java b/juick-server-web/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java
new file mode 100644
index 00000000..86e21d01
--- /dev/null
+++ b/juick-server-web/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java
@@ -0,0 +1,83 @@
+package com.juick.service.security;
+
+import com.juick.User;
+import com.juick.service.security.entities.JuickUser;
+import com.juick.service.UserService;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.authentication.RememberMeAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.RememberMeServices;
+import org.springframework.util.Assert;
+import org.springframework.web.filter.OncePerRequestFilter;
+import org.springframework.web.util.WebUtils;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Created by aalexeev on 4/5/17.
+ */
+public class HashParamAuthenticationFilter extends OncePerRequestFilter {
+ public static final String PARAM_NAME = "hash";
+
+ private final UserService userService;
+ private final RememberMeServices rememberMeServices;
+
+
+ public HashParamAuthenticationFilter(
+ final UserService userService,
+ final RememberMeServices rememberMeServices) {
+ Assert.notNull(userService, "userService should not be null");
+ Assert.notNull(rememberMeServices, "rememberMeServices should not be null");
+
+ this.userService = userService;
+ this.rememberMeServices = rememberMeServices;
+ }
+
+ @Override
+ protected void doFilterInternal(
+ HttpServletRequest request,
+ HttpServletResponse response,
+ FilterChain filterChain) throws ServletException, IOException {
+
+ String hash = getHashFromRequest(request);
+
+ if (hash != null && authenticationIsRequired()) {
+ User user = userService.getUserByHash(hash);
+
+ if (!user.isAnonymous()) {
+ Authentication authentication = new RememberMeAuthenticationToken(
+ hash, new JuickUser(user), JuickUser.USER_AUTHORITY);
+
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+
+ rememberMeServices.loginSuccess(request, response, authentication);
+ }
+ }
+
+ filterChain.doFilter(request, response);
+ }
+
+ private boolean authenticationIsRequired() {
+ Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
+
+ return existingAuth == null ||
+ !existingAuth.isAuthenticated() ||
+ existingAuth instanceof AnonymousAuthenticationToken;
+ }
+
+ private String getHashFromRequest(HttpServletRequest request) {
+ String paramHash = request.getParameter(PARAM_NAME);
+ Cookie cookieHash = WebUtils.getCookie(request, PARAM_NAME);
+
+ if (paramHash == null && cookieHash != null) {
+ return cookieHash.getValue();
+ }
+ return paramHash;
+ }
+}
diff --git a/juick-server-web/src/main/java/com/juick/service/security/JuickUserDetailsService.java b/juick-server-web/src/main/java/com/juick/service/security/JuickUserDetailsService.java
new file mode 100644
index 00000000..4e645ac0
--- /dev/null
+++ b/juick-server-web/src/main/java/com/juick/service/security/JuickUserDetailsService.java
@@ -0,0 +1,34 @@
+package com.juick.service.security;
+
+import com.juick.service.UserService;
+import com.juick.service.security.entities.JuickUser;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.util.Assert;
+
+/**
+ * Created by aalexeev on 11/28/16.
+ */
+public class JuickUserDetailsService implements UserDetailsService {
+ private final UserService userService;
+
+ public JuickUserDetailsService(final UserService userService) {
+ Assert.notNull(userService, "UserService must be initialized");
+ this.userService = userService;
+ }
+
+ @Override
+ public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
+ if (StringUtils.isBlank(username))
+ throw new UsernameNotFoundException("Invalid user name " + username);
+
+ com.juick.User user = userService.getFullyUserByName(username);
+
+ if (user != null)
+ return new JuickUser(user);
+
+ throw new UsernameNotFoundException("The username " + username + " is not found");
+ }
+}
diff --git a/juick-server-web/src/main/java/com/juick/service/security/NotAuthorizedAuthenticationEntryPoint.java b/juick-server-web/src/main/java/com/juick/service/security/NotAuthorizedAuthenticationEntryPoint.java
new file mode 100644
index 00000000..b456a3d0
--- /dev/null
+++ b/juick-server-web/src/main/java/com/juick/service/security/NotAuthorizedAuthenticationEntryPoint.java
@@ -0,0 +1,20 @@
+package com.juick.service.security;
+
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Created by vitalyster on 25.11.2016.
+ */
+public class NotAuthorizedAuthenticationEntryPoint implements AuthenticationEntryPoint {
+ @Override
+ public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
+ throws IOException, ServletException {
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+}
diff --git a/juick-server-web/src/main/java/com/juick/service/security/NullUserDetailsService.java b/juick-server-web/src/main/java/com/juick/service/security/NullUserDetailsService.java
new file mode 100644
index 00000000..49e9effc
--- /dev/null
+++ b/juick-server-web/src/main/java/com/juick/service/security/NullUserDetailsService.java
@@ -0,0 +1,16 @@
+package com.juick.service.security;
+
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+/**
+ * Created by aalexeev on 11/28/16.
+ */
+public class NullUserDetailsService implements UserDetailsService {
+ @Override
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ throw new UsernameNotFoundException(
+ "loadUserByUsername called for NullUserDetailsService, user " + username + "can not be found");
+ }
+}
diff --git a/juick-server-web/src/main/java/com/juick/service/security/deprecated/CookieSimpleHashRememberMeServices.java b/juick-server-web/src/main/java/com/juick/service/security/deprecated/CookieSimpleHashRememberMeServices.java
new file mode 100644
index 00000000..189877fd
--- /dev/null
+++ b/juick-server-web/src/main/java/com/juick/service/security/deprecated/CookieSimpleHashRememberMeServices.java
@@ -0,0 +1,113 @@
+package com.juick.service.security.deprecated;
+
+import com.juick.User;
+import com.juick.server.util.HashUtils;
+import com.juick.service.security.entities.JuickUser;
+import com.juick.service.UserService;
+import com.juick.service.security.NullUserDetailsService;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.env.Environment;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.web.authentication.RememberMeServices;
+import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;
+import org.springframework.security.web.authentication.rememberme.InvalidCookieException;
+import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationException;
+import org.springframework.util.Assert;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Optional;
+
+/**
+ * Created by aalexeev on 11/28/16.
+ *
+ * @deprecated not recommended use for secure reasons
+ */
+@Deprecated
+public class CookieSimpleHashRememberMeServices extends AbstractRememberMeServices implements RememberMeServices {
+ private static final Logger logger = LoggerFactory.getLogger(CookieSimpleHashRememberMeServices.class);
+
+ private static final String COOKIE_PARAM_NAME = "hash";
+
+ private final UserService userService;
+
+ public CookieSimpleHashRememberMeServices(
+ final String key, final UserService userService, final Environment environment) {
+ super(key, new NullUserDetailsService());
+
+ Assert.notNull(userService);
+ Assert.notNull(environment);
+
+ this.userService = userService;
+
+ setCookieName(COOKIE_PARAM_NAME);
+ setCookieDomain(environment.getProperty("web_domain", "juick.com"));
+ setAlwaysRemember(true);
+ }
+
+ @Override
+ public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
+ super.logout(request, response, authentication);
+ userService.deleteLoginForUser(authentication.getName());
+ }
+
+ @Override
+ protected void onLoginSuccess(
+ HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
+ String username = successfulAuthentication.getName();
+
+ logger.debug("Creating new persistent login for user {}", username);
+
+ try {
+ int uid = userService.getUIDbyName(username);
+
+ Assert.isTrue(uid > 0);
+
+ String hash = HashUtils.generateHash(16);
+
+ userService.setLoginForUser(uid, hash);
+
+ setCookie(new String[]{hash}, getTokenValiditySeconds(), request, response);
+ } catch (Exception e) {
+ logger.error("Failed to save cookies", e);
+ }
+ }
+
+ @Override
+ protected UserDetails processAutoLoginCookie(
+ String[] cookieTokens, HttpServletRequest request, HttpServletResponse response)
+ throws RememberMeAuthenticationException, UsernameNotFoundException {
+ String hash = cookieTokens[0];
+
+ if (StringUtils.isBlank(hash)) {
+ hash = request.getParameter("hash");
+ }
+ if (StringUtils.isBlank(hash)) {
+ throw new InvalidCookieException("Cookie is invalid and hash parameter not found");
+ }
+
+ int uid = userService.getUIDbyHash(hash);
+ if (uid <= 0)
+ throw new UsernameNotFoundException("User not found by hash, cookies" + cookieTokens);
+
+ Optional<User> userOptional = userService.getUserByUID(uid);
+
+ Assert.isTrue(userOptional.isPresent());
+
+ return new JuickUser(userOptional.get());
+ }
+
+ @Override
+ protected String[] decodeCookie(String cookieValue) throws InvalidCookieException {
+ return new String[]{cookieValue};
+ }
+
+ @Override
+ protected String encodeCookie(String[] cookieTokens) {
+ return cookieTokens != null && cookieTokens.length > 0 ? cookieTokens[0] : StringUtils.EMPTY;
+ }
+}
diff --git a/juick-server-web/src/main/java/com/juick/service/security/deprecated/RequestParamHashRememberMeServices.java b/juick-server-web/src/main/java/com/juick/service/security/deprecated/RequestParamHashRememberMeServices.java
new file mode 100644
index 00000000..4874ebcf
--- /dev/null
+++ b/juick-server-web/src/main/java/com/juick/service/security/deprecated/RequestParamHashRememberMeServices.java
@@ -0,0 +1,71 @@
+package com.juick.service.security.deprecated;
+
+import com.juick.User;
+import com.juick.service.security.entities.JuickUser;
+import com.juick.service.UserService;
+import com.juick.service.security.NullUserDetailsService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.web.authentication.RememberMeServices;
+import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;
+import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationException;
+import org.springframework.util.Assert;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Created by aalexeev on 11/30/16.
+ *
+ * @deprecated for security reasons
+ */
+@Deprecated
+public class RequestParamHashRememberMeServices extends AbstractRememberMeServices implements RememberMeServices {
+ private static final String PARAM_NAME = "hash";
+
+ private final UserService userService;
+
+ public RequestParamHashRememberMeServices(String key, UserService userService) {
+ super(key, new NullUserDetailsService());
+
+ Assert.notNull(userService);
+ this.userService = userService;
+ setAlwaysRemember(false);
+ }
+
+ @Override
+ protected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
+ // do nothing
+ }
+
+ @Override
+ protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
+ return false; // always false
+ }
+
+ @Override
+ protected void cancelCookie(HttpServletRequest request, HttpServletResponse response) {
+ // do nothing
+ }
+
+ @Override
+ protected String extractRememberMeCookie(HttpServletRequest request) {
+ return PARAM_NAME; // return any not blank value
+ }
+
+ @Override
+ protected UserDetails processAutoLoginCookie(
+ String[] cookieTokens, HttpServletRequest request, HttpServletResponse response)
+ throws RememberMeAuthenticationException, UsernameNotFoundException {
+ String hash = request.getParameter(PARAM_NAME);
+
+ if (StringUtils.isNotBlank(hash)) {
+ User user = userService.getUserByHash(hash);
+ if (user.getUid() > 0)
+ return new JuickUser(user);
+ }
+ throw new UsernameNotFoundException("User not found by hash " + hash);
+ }
+}
diff --git a/juick-server-web/src/main/java/com/juick/service/security/entities/JuickUser.java b/juick-server-web/src/main/java/com/juick/service/security/entities/JuickUser.java
new file mode 100644
index 00000000..2c409a1d
--- /dev/null
+++ b/juick-server-web/src/main/java/com/juick/service/security/entities/JuickUser.java
@@ -0,0 +1,75 @@
+package com.juick.service.security.entities;
+
+import com.juick.User;
+import com.juick.server.helpers.AnonymousUser;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Created by aalexeev on 11/21/16.
+ */
+public class JuickUser implements UserDetails {
+ static final GrantedAuthority ROLE_USER = new SimpleGrantedAuthority("ROLE_USER");
+ static final GrantedAuthority ROLE_ANONYMOUS = new SimpleGrantedAuthority("ROLE_ANONYMOUS");
+
+ public static final List<GrantedAuthority> USER_AUTHORITY = Collections.singletonList(ROLE_USER);
+ public static final List<GrantedAuthority> ANONYMOUS_AUTHORITY = Collections.singletonList(ROLE_ANONYMOUS);
+
+ public static final JuickUser ANONYMOUS_USER = new JuickUser(AnonymousUser.INSTANCE, ANONYMOUS_AUTHORITY);
+
+ private final com.juick.User user;
+ private final Collection<? extends GrantedAuthority> authorities;
+
+ public JuickUser(com.juick.User user) {
+ this(user, USER_AUTHORITY);
+ }
+
+ public JuickUser(com.juick.User user, Collection<? extends GrantedAuthority> authorities) {
+ this.user = user;
+ this.authorities = authorities;
+ }
+
+ @Override
+ public Collection<? extends GrantedAuthority> getAuthorities() {
+ return authorities;
+ }
+
+ @Override
+ public String getPassword() {
+ return user.getCredentials();
+ }
+
+ @Override
+ public String getUsername() {
+ return user.getName();
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return true;
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return !user.isBanned();
+ }
+
+ public User getUser() {
+ return user;
+ }
+}