diff options
14 files changed, 341 insertions, 20 deletions
diff --git a/juick-server/src/main/resources/juick.conf.example b/juick-server/src/main/resources/juick.conf.example index 945bfae4..284d86a5 100644 --- a/juick-server/src/main/resources/juick.conf.example +++ b/juick-server/src/main/resources/juick.conf.example @@ -26,6 +26,13 @@ sphinx_user= # Sphinx search JDBC password sphinx_password= +# The domain name for Web (default value - "juick.com") +web_domain=juick.com + +# Authority cookie name (default value - "hash") +auth_cookie_name=hash + + twitter_consumer_key= twitter_consumer_secret= diff --git a/juick-spring-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java b/juick-spring-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java new file mode 100644 index 00000000..65d07dba --- /dev/null +++ b/juick-spring-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java @@ -0,0 +1,85 @@ +package com.juick.www.configuration; + +import com.juick.service.UserService; +import com.juick.www.entity.JuickUser; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +import javax.annotation.Resource; + +/** + * Created by aalexeev on 11/21/16. + */ +@EnableWebSecurity +@PropertySource("classpath:juick.conf") +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + @Resource + private Environment env; + @Resource + private UserService userService; + + protected WebSecurityConfig() { + super(true); + } + + @Bean("authManager") + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Bean("userDetailsService") + @Override + public UserDetailsService userDetailsServiceBean() throws Exception { + return username -> { + if (StringUtils.isBlank(username)) + throw new UsernameNotFoundException("Invalid user name " + username); + + com.juick.User user = userService.getUserByName(username); + + if (user != null) + return new JuickUser(user); + + throw new UsernameNotFoundException("The username " + username + " is not found"); + }; + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/settings", "/pm/**").authenticated() + .anyRequest().authenticated() + .and() + .anonymous() + .authorities("ROLE_ANONYM") + .and() + .logout() + .invalidateHttpSession(true) + .logoutUrl("/logout") + .logoutSuccessUrl("/") + .and() + .formLogin() + .loginPage("/login") + .permitAll() + .defaultSuccessUrl("/") + .failureForwardUrl("/login") + .and() + .rememberMe() + .tokenValiditySeconds(6 * 30 * 24 * 3600) + .alwaysRemember(true) + .useSecureCookie(true) + .rememberMeCookieName(env.getProperty("auth_cookie_name", "hash")) + .rememberMeCookieDomain(env.getProperty("web_domain", "juick.com")) + .and() + .csrf().disable(); + } +} diff --git a/juick-spring-www/src/main/java/com/juick/www/configuration/WwwInitializer.java b/juick-spring-www/src/main/java/com/juick/www/configuration/WwwInitializer.java index 852ec554..6b7b4ebc 100644 --- a/juick-spring-www/src/main/java/com/juick/www/configuration/WwwInitializer.java +++ b/juick-spring-www/src/main/java/com/juick/www/configuration/WwwInitializer.java @@ -6,6 +6,9 @@ import org.springframework.web.filter.CharacterEncodingFilter; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; import javax.servlet.Filter; +import javax.servlet.FilterRegistration; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; /** * Created by aalexeev on 11/20/16. @@ -14,7 +17,7 @@ public class WwwInitializer extends AbstractAnnotationConfigDispatcherServletIni @Override protected Class<?>[] getRootConfigClasses() { - return new Class<?>[]{DataConfiguration.class, SearchConfiguration.class}; + return new Class<?>[]{DataConfiguration.class, SearchConfiguration.class, WebSecurityConfig.class}; } @Override @@ -28,16 +31,23 @@ public class WwwInitializer extends AbstractAnnotationConfigDispatcherServletIni } @Override - protected Filter[] getServletFilters() { - CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); - characterEncodingFilter.setEncoding("UTF-8"); - - return new Filter[]{characterEncodingFilter}; + protected String getServletName() { + return "WWW-spring dispatcher servlet"; } @Override - protected String getServletName() { - return "WWW-spring dispatcher servlet"; + public void onStartup(ServletContext servletContext) throws ServletException { + super.onStartup(servletContext); + + CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter(); + + encodingFilter.setEncoding("UTF-8"); + encodingFilter.setForceEncoding(true); + + FilterRegistration.Dynamic registration = servletContext.addFilter( + "encodingFilter", new CharacterEncodingFilter()); + + registration.addMappingForUrlPatterns(null, true, "/*"); } } diff --git a/juick-spring-www/src/main/java/com/juick/www/configuration/WwwServletConfiguration.java b/juick-spring-www/src/main/java/com/juick/www/configuration/WwwServletConfiguration.java index 8edc1b6c..01cee39f 100644 --- a/juick-spring-www/src/main/java/com/juick/www/configuration/WwwServletConfiguration.java +++ b/juick-spring-www/src/main/java/com/juick/www/configuration/WwwServletConfiguration.java @@ -6,14 +6,13 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; -import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.thymeleaf.spring4.SpringTemplateEngine; import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver; import org.thymeleaf.spring4.view.ThymeleafViewResolver; @@ -64,20 +63,10 @@ public class WwwServletConfiguration extends WebMvcConfigurationSupport { public ThymeleafViewResolver viewResolver() { ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); viewResolver.setTemplateEngine(templateEngine()); - // NOTE 'order' and 'viewNames' are optional - viewResolver.setOrder(1); - viewResolver.setViewNames(new String[]{".html", ".xhtml"}); return viewResolver; } @Override - public RequestMappingHandlerMapping requestMappingHandlerMapping() { - RequestMappingHandlerMapping mapping = super.requestMappingHandlerMapping(); - mapping.setUseSuffixPatternMatch(false); - return mapping; - } - - @Override protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() .serializationInclusion(JsonInclude.Include.NON_DEFAULT) @@ -97,6 +86,7 @@ public class WwwServletConfiguration extends WebMvcConfigurationSupport { registry.setOrder(0); registry.addResourceHandler("/scripts.js").addResourceLocations("/"); registry.addResourceHandler("/style.css").addResourceLocations("/"); + registry.addResourceHandler("/favicon.ico").addResourceLocations("/static/favicon.ico"); } @Bean @@ -109,4 +99,8 @@ public class WwwServletConfiguration extends WebMvcConfigurationSupport { return messageSource; } + + @Override + protected void addViewControllers(ViewControllerRegistry registry) { + } } diff --git a/juick-spring-www/src/main/java/com/juick/www/controllers/HelpController.java b/juick-spring-www/src/main/java/com/juick/www/controllers/HelpController.java new file mode 100644 index 00000000..dad3ff9f --- /dev/null +++ b/juick-spring-www/src/main/java/com/juick/www/controllers/HelpController.java @@ -0,0 +1,12 @@ +package com.juick.www.controllers; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * Created by aalexeev on 11/21/16. + */ +@Controller +@RequestMapping("/help") +public class HelpController { +} diff --git a/juick-spring-www/src/main/java/com/juick/www/controllers/IndexController.java b/juick-spring-www/src/main/java/com/juick/www/controllers/IndexController.java new file mode 100644 index 00000000..2973bf30 --- /dev/null +++ b/juick-spring-www/src/main/java/com/juick/www/controllers/IndexController.java @@ -0,0 +1,24 @@ +package com.juick.www.controllers; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * Created by aalexeev on 11/21/16. + */ +@Controller +public class IndexController { + + @RequestMapping("/") + public String index( + java.security.Principal userPrincipal, + @RequestParam(required = false, defaultValue = "0") int before, + Model model) { + + model.addAttribute("currentUser", userPrincipal); + + return "index"; + } +} diff --git a/juick-spring-www/src/main/java/com/juick/www/controllers/SettingsController.java b/juick-spring-www/src/main/java/com/juick/www/controllers/SettingsController.java new file mode 100644 index 00000000..eef41c38 --- /dev/null +++ b/juick-spring-www/src/main/java/com/juick/www/controllers/SettingsController.java @@ -0,0 +1,16 @@ +package com.juick.www.controllers; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * Created by aalexeev on 11/21/16. + */ +@Controller +public class SettingsController { + + @RequestMapping("/settings") + public String showSettings() { + return "index"; + } +} diff --git a/juick-spring-www/src/main/java/com/juick/www/controllers/ShowMessageController.java b/juick-spring-www/src/main/java/com/juick/www/controllers/ShowMessageController.java new file mode 100644 index 00000000..338dc9be --- /dev/null +++ b/juick-spring-www/src/main/java/com/juick/www/controllers/ShowMessageController.java @@ -0,0 +1,52 @@ +package com.juick.www.controllers; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.regex.Pattern; + +/** + * Created by aalexeev on 11/21/16. + */ +@Controller +public class ShowMessageController { + 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 final Logger logger = LoggerFactory.getLogger(getClass()); + + + @RequestMapping("/{anything}") + public String parseAnything( + @PathVariable String anything, + @RequestParam(required = false, defaultValue = "0") int before, + Model model) { + + boolean isUsername = USER_NAME_PATTERN.matcher(anything).matches(); + boolean isPostNumber = POST_NUMBER_PATTERN.matcher(anything).matches(); + + return "redirect:/"; + } + + @RequestMapping("/{userName}/{postNumber}") + public String checkShowPost( + @PathVariable String userName, + @PathVariable String postNumber, + @RequestParam(required = false, defaultValue = "0") int before, + Model model) { + + if (!USER_NAME_PATTERN.matcher(userName).matches() || + !POST_NUMBER_PATTERN.matcher(postNumber).matches()) { + logger.warn("Invalid user name or post number, user name \"{}\", post number \"{}\"", userName, postNumber); + return "redirect:/"; + } + + return "index"; + } +} diff --git a/juick-spring-www/src/main/java/com/juick/www/controllers/TagController.java b/juick-spring-www/src/main/java/com/juick/www/controllers/TagController.java new file mode 100644 index 00000000..5c3b6287 --- /dev/null +++ b/juick-spring-www/src/main/java/com/juick/www/controllers/TagController.java @@ -0,0 +1,37 @@ +package com.juick.www.controllers; + +import com.juick.service.MessagesService; +import com.juick.service.TagService; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import javax.inject.Inject; + +/** + * Created by aalexeev on 11/21/16. + */ +@Controller +public class TagController { + @Inject + private TagService tagService; + @Inject + private MessagesService messagesService; + + + @RequestMapping("/tag/{tagName}") + public String showTags( + @PathVariable String tagName, + @RequestParam(required = false, defaultValue = "0") int before, + Model model) { + + return "index"; + } + + @RequestMapping("/tag") + public String redirectToMain() { + return "redirect:/"; + } +} diff --git a/juick-spring-www/src/main/java/com/juick/www/entity/JuickUser.java b/juick-spring-www/src/main/java/com/juick/www/entity/JuickUser.java new file mode 100644 index 00000000..20d7889d --- /dev/null +++ b/juick-spring-www/src/main/java/com/juick/www/entity/JuickUser.java @@ -0,0 +1,62 @@ +package com.juick.www.entity; + +import com.juick.User; +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; + +/** + * Created by aalexeev on 11/21/16. + */ +public class JuickUser implements UserDetails { + private static final GrantedAuthority ROLE_USER = new SimpleGrantedAuthority("ROLE_USER"); + + private final com.juick.User user; + + + public JuickUser(com.juick.User user) { + this.user = user; + } + + @Override + public Collection<? extends GrantedAuthority> getAuthorities() { + return Collections.singletonList(ROLE_USER); + } + + @Override + public String getPassword() { + return null; + } + + @Override + public String getUsername() { + return user.getName(); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return false; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return !user.isBanned(); + } + + public User getUser() { + return user; + } +} diff --git a/juick-spring-www/src/main/resources/messages_en.properties b/juick-spring-www/src/main/resources/messages_en.properties new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/juick-spring-www/src/main/resources/messages_en.properties diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/index.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/index.html new file mode 100644 index 00000000..566549bd --- /dev/null +++ b/juick-spring-www/src/main/webapp/WEB-INF/templates/index.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Title</title> +</head> +<body> + +</body> +</html>
\ No newline at end of file diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/layout/footer.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/layout/footer.html new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/juick-spring-www/src/main/webapp/WEB-INF/templates/layout/footer.html diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/layout/mainLayout.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/layout/mainLayout.html new file mode 100644 index 00000000..91a76f72 --- /dev/null +++ b/juick-spring-www/src/main/webapp/WEB-INF/templates/layout/mainLayout.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8" /> + <meta http-equiv="X-UA-Compatible" content="IE=edge" /> + <script type="text/javascript" src="/scripts.js"></script> + <link rel="stylesheet" type="text/css" href="/style.css" /> +</head> +<body> + +</body> +</html>
\ No newline at end of file |