Commit 450bb536 by jhrabal

wip

parent c28a429f
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
......@@ -11,7 +12,7 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/>
<relativePath />
</parent>
......@@ -179,7 +180,7 @@
</plugins>
</build>
<repositories>
<repositories>
<repository>
<id>com.springsource.repository.maven.release</id>
<url>http://repo.springsource.org/release/</url>
......@@ -195,7 +196,7 @@
<id>com.springsource.repository.bundles.external</id>
<url>http://repository.springsource.com/maven/bundles/external</url>
</repository>
</repositories>
</repositories>
</project>
-- default admin user
insert into APP_ROLE (ID, NAME) values (1, 'ADMIN');
insert into APP_USER (ID, EMAIL, FIRST_NAME, LAST_NAME, PASSWORD, PASSWORD_SALT) VAlUES (1, 'janhrabal@seznam.cz', 'Jan', 'Hrabal', 'NKz1rz7kSEBXFGbolvEhdomvcDQYmD0IKGADVpuoxL1ztsE1NAnOMvbiSkvc3vwLQvBdPHw449XzgRderNJc9MMnnMTEgJdl3S6dtBGiEYDRHK19toXdwttfaDrXewjyZBJkzy7CxE/BOad4XkiTreIFAUGRedK9TGZ+RWbrJ2KIRrkSX3H1J2eT7HLF8bblkxz2qhjsF5s0k37e3sFI0xAdyCy6qAYS4/MW4WYQ3o0YyZc4krGE2k3y9kfPxWEh/favQKoFIX92ZkRh6ZIXNF7i4oUBl1pcg6r5ykCT83IAWm9avM768NEitEVOx0V8P0PQ2WxGA3n7nicKmwYjow==', 'GACR2Rea1kIhZAlImqK8HauZwTah5eMyKiTzr9HDriryN92YkE5UkWe3Gn7oRLkKEftaNfEfa2Ujj18Rrsed2a6QN69UZCkpRHnwgoBp5ckOOaC6s4undHSjYZW5rJx8CuKXTJpO1TS1LlsjwCyir8oA2gGm480jgGwOefm+r2s=');
insert into APP_USER_ROLE (USER_ID, ROLE_ID) values (1, 1);
CREATE DATABASE suite
WITH
OWNER = postgres
ENCODING = 'utf8'
TABLESPACE = pg_default
CONNECTION LIMIT = -1;
--
CREATE SEQUENCE ID_GENERATOR;
-- version procedure
CREATE OR REPLACE FUNCTION process_version_check() RETURNS TRIGGER AS $version_check$
BEGIN
IF (OLD.VERSION > NEW.VERSION) THEN
RAISE EXCEPTION 'row_version_conflict' using
ERRCODE = 99001,
MESSAGE = ('Cannot update record % in table %. Actual version: %, provided version %', NEW.ID, TG_TABLE_NAME, OLD.VERSION, NEW.VERSION);
END IF;
NEW.VERSION = OLD.VERSION + 1;
RETURN NEW;
END;
$version_check$ LANGUAGE plpgsql;
-- end of version procedure
-- shortid procedure
CREATE OR REPLACE FUNCTION pseudo_encrypt(VALUE bigint) returns bigint AS $$
DECLARE
l1 int;
l2 int;
r1 int;
r2 int;
i int:=0;
BEGIN
l1:= (VALUE >> 16) & 65535;
r1:= VALUE & 65535;
WHILE i < 3 LOOP
l2 := r1;
r2 := l1 # ((((1366 * r1 + 150889) % 714025) / 714025.0) * 32767)::int;
l1 := l2;
r1 := r2;
i := i + 1;
END LOOP;
RETURN ((r1 << 16) + l1);
END;
$$ LANGUAGE plpgsql strict immutable;
CREATE OR REPLACE FUNCTION shortid(n bigint) RETURNS text
LANGUAGE plpgsql IMMUTABLE STRICT AS $$
DECLARE
alphabet text:='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
base int:=length(alphabet);
_n bigint:=pseudo_encrypt(abs(n));
output text:='';
BEGIN
LOOP
output := output || substr(alphabet, 1+(_n%base)::int, 1);
_n := _n / base;
EXIT WHEN _n=0;
END LOOP;
RETURN output;
END $$;
-- end of shortid procedure
CREATE TABLE APP_USER (
ID INT8 NOT NULL,
VERSION INT8 NOT NULL DEFAULT 0,
DELETED BOOLEAN DEFAULT FALSE,
EMAIL VARCHAR(250) NOT NULL,
PASSWORD VARCHAR(500),
PASSWORD_SALT VARCHAR(250),
FIRST_NAME VARCHAR(250),
LAST_NAME VARCHAR(250),
CONSTRAINT PK_APP_USER PRIMARY KEY(ID)
);
CREATE INDEX APP_USER_EMAIL_IDX ON APP_USER(lower(EMAIL));
CREATE TABLE APP_ROLE (
ID INT8 NOT NULL,
NAME VARCHAR(250) NOT NULL,
CONSTRAINT PK_APP_ROLE PRIMARY KEY(ID)
);
CREATE TABLE APP_USER_ROLE (
USER_ID INT8 NOT NULL,
ROLE_ID INT8 NOT NULL
);
CREATE INDEX APP_USER_ROLE_IDX ON APP_USER_ROLE(USER_ID, ROLE_ID);
package com.jh.common.jpa;
/**
* The Class AbstractHibernateConfiguration.
*/
public class AbstractHibernateConfiguration {
}
......@@ -9,11 +9,12 @@ import java.util.Set;
import java.util.function.Function;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.MatchMode;
......@@ -21,7 +22,6 @@ import org.hibernate.criterion.Order;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.jpa.HibernateEntityManager;
import org.hibernate.query.NativeQuery;
import org.hibernate.transform.Transformers;
......@@ -40,27 +40,18 @@ public abstract class AbstractHibernateRepository {
/** The session factory. */
@PersistenceContext
private EntityManager entityManager;
protected EntityManager entityManager;
/**
* Gets the session.
*
* @return the session
*/
protected Session getSession() {
return ((HibernateEntityManager) entityManager).getSession();
}
/**
* Criteria.
*
* @param clazz the clazz
* @return the criteria
*/
protected Criteria criteria(Class<?> clazz) {
return getSession().createCriteria(clazz);
protected <T> CriteriaQuery<T> criteria(Class<T> clazz) {
return entityManager.getCriteriaBuilder().createQuery(clazz);
}
......@@ -69,6 +60,15 @@ public abstract class AbstractHibernateRepository {
return (NativeQuery<T>) entityManager.createNativeQuery(query);
}
@SuppressWarnings("unchecked")
protected <T> T singleResult(Query q) {
try {
return (T) q.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
/**
* TODO comment
*
......@@ -176,9 +176,9 @@ public abstract class AbstractHibernateRepository {
*/
public <T extends AbstractIdEntity> void save(T entity) {
if (entity.getId() == null) {
getSession().save(entity);
entityManager.persist(entity);
} else {
getSession().update(entity);
entityManager.merge(entity);
}
}
......@@ -190,7 +190,7 @@ public abstract class AbstractHibernateRepository {
*/
public <T extends AbstractIdEntity> void delete(T entity) {
if (entity.getId() != null) {
getSession().delete(entity);
entityManager.remove(entity);
}
}
......@@ -225,10 +225,10 @@ public abstract class AbstractHibernateRepository {
sb.append(" and ref not in (:refs)");
}
Query q = getSession().createQuery(sb.toString());
Query q = entityManager.createQuery(sb.toString());
q.setParameter("root", entity);
if (!elements.isEmpty()) {
q.setParameterList("refs", elements);
q.setParameter("refs", elements);
}
q.executeUpdate();
......
/*
* Created on 17. 3. 2018 21:20:00
*
*/
package com.jh.common.resource;
import java.util.Locale;
import java.util.Objects;
import java.util.function.Function;
/**
* The Interface ResourceLoader.
*
* @author Jan Hrabal
*/
public interface ResourceLoader {
/**
* Gets the string.
*
* @param resourceName the resource name
* @param locale the locale
* @return the string
*/
String getString(String resourceName, Locale locale);
/**
* Gets the string.
*
* @param resourceName the resource name
* @return the string
*/
String getString(String resourceName);
/**
* Resolve resource name.
*
* @param templateName the template name
* @param locale the locale
* @param existenceValidator the existence validator
* @return the string
*/
static String resolveResourceName(String templateName, Locale locale, Function<String, Boolean> existenceValidator) {
Objects.requireNonNull(existenceValidator, "Provide non-null existence validator");
String extension = null;
String root = templateName;
int i = templateName.lastIndexOf('.');
int p = Math.max(templateName.lastIndexOf('/'), templateName.lastIndexOf('\\'));
if (i > p) {
extension = templateName.substring(i+1);
root = templateName.substring(0, i);
}
StringBuilder sb = null;
String country = locale.getCountry();
String lang = locale.getLanguage();
sb = new StringBuilder(root);
sb.append("_");
sb.append(lang);
sb.append("-");
sb.append(country);
if (extension != null) {
sb.append(".").append(extension);
}
String s = sb.toString();
if (existenceValidator.apply(s)) {
return s;
}
sb = new StringBuilder(root);
sb.append("_");
sb.append(lang);
if (extension != null) {
sb.append(".").append(extension);
}
s = sb.toString();
if (existenceValidator.apply(s)) {
return s;
}
//default
return templateName;
};
}
package com.jh.common.security.controller;
package com.jh.common.security;
/**
* The Class AuthError.
......
......@@ -5,8 +5,6 @@ import java.util.List;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import com.jh.common.security.controller.AuthError;
/**
......
......@@ -2,7 +2,6 @@ package com.jh.common.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
......@@ -11,10 +10,9 @@ import org.springframework.security.core.AuthenticationException;
* The Class UserAuthenticationProvider.
*/
//TODO
public class UserAuthenticationProvider implements AuthenticationProvider {
public class AuthServiceAuthenticationProvider implements AuthenticationProvider {
/** The auth service. */
@Autowired
private AuthService authService;
......@@ -28,9 +26,8 @@ public class UserAuthenticationProvider implements AuthenticationProvider {
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
String email = String.valueOf(token.getPrincipal());
String password = String.valueOf(token.getCredentials());
String email = String.valueOf(authentication.getPrincipal());
String password = String.valueOf(authentication.getCredentials());
//TODO refactor
return authService.authenticate(email, password);
......@@ -46,7 +43,13 @@ public class UserAuthenticationProvider implements AuthenticationProvider {
*/
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
return true;
}
@Autowired
public void setAuthService(AuthService authService) {
this.authService = authService;
}
......
package com.jh.common.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import com.jh.common.security.repository.AppUserRepository;
import com.jh.common.security.service.AppUserAuthService;
/**
* The Class ReactApplicationSecurityContext.
*/
public class ReactApplicationSecurityContext extends WebSecurityConfigurerAdapter {
public class JhSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* @param http
......@@ -24,12 +34,9 @@ public class ReactApplicationSecurityContext extends WebSecurityConfigurerAdapte
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/auth/signup").permitAll()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(authFilter(), UsernamePasswordAuthenticationFilter.class)
.logout()
.clearAuthentication(true)
.permitAll();
......@@ -38,7 +45,7 @@ public class ReactApplicationSecurityContext extends WebSecurityConfigurerAdapte
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint());
http.headers().frameOptions().sameOrigin();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
......@@ -54,22 +61,6 @@ public class ReactApplicationSecurityContext extends WebSecurityConfigurerAdapte
/**
* Auth filter.
*
* @return the json authentication filter
* @throws Exception the exception
*/
protected JsonAuthenticationFilter authFilter() throws Exception {
JsonAuthenticationFilter filter = new JsonAuthenticationFilter();
filter.setFilterProcessesUrl("/api/auth/login");
filter.setAuthenticationSuccessHandler(new RestAuthenticationSuccessHandler());
filter.setAuthenticationManager(authenticationManagerBean());
filter.setUsernameParameter("email");
return filter;
}
/**
* Override this method to provide custom {@link AuthenticationEntryPoint}.
*
* It returns {@link RestAuthenticationEntryPoint} by default
......@@ -77,35 +68,33 @@ public class ReactApplicationSecurityContext extends WebSecurityConfigurerAdapte
* @return the authentication entry point
*/
protected AuthenticationEntryPoint authenticationEntryPoint() {
return new RestAuthenticationEntryPoint();
return new AuthenticationEntryPoint() {
@Override
public final void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
};
}
/* *** LOGIN & SIGNUP configuration *** */
/**
* Auth client resources.
*
* @return the web mvc configurer adapter
*/
@Bean
public WebMvcConfigurerAdapter authClientResources() {
return new WebMvcConfigurerAdapter() {
protected AuthenticationSuccessHandler authenticationSuccessHandler() {
return new SimpleUrlAuthenticationSuccessHandler() {
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/auth/**").addResourceLocations("classpath:/com/jh/common/security/controller/");
public void onAuthenticationSuccess( final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication ) throws ServletException, IOException {
this.clearAuthenticationAttributes( request );
}
// @Override
// public void addViewControllers(ViewControllerRegistry registry) {
// registry.addViewController("/auth/login").setViewName("forward:/auth/loginForm.html");
// registry.addViewController("/auth/signup").setViewName("forward:/auth/signupForm.html");
// }
};
}
@Bean
public AuthService authService() {
return new AppUserAuthService();
}
@Bean
public AppUserRepository appUserRepository() {
return new AppUserRepository();
}
}
/*
* Created on 27. 7. 2017 14:37:22
*
*/
package com.jh.common.security;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* The Class JsonAuthenticationFilter.
*
* @author Jan Hrabal
*/
public class JsonAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
/** The object mapper. */
/* Default object mapper */
private ObjectMapper objectMapper = new ObjectMapper();
/**
* @param request
* @param response
* @return
* @see org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#attemptAuthentication(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response){
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
LoginRequest loginRequest = readLoginRequest(request);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
/**
* Read login request.
*
* @param request the request
* @return the login request
*/
protected LoginRequest readLoginRequest(HttpServletRequest request) {
try {
return objectMapper.readValue(request.getReader(), LoginRequest.class);
} catch (IOException e) {
throw new RuntimeException("Cannot parse login request", e);
}
}
/**
* Sets the object mapper.
*
* @param objectMapper the new object mapper
*/
@Autowired(required = false)
public void setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
}
class LoginRequest {
private String username;
private String password;
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
}
......@@ -15,7 +15,7 @@ import org.springframework.util.StringUtils;
/**
* The Class PasswordSecurity.
*/
public abstract class PasswordSecurity {
public abstract class PasswordUtils {
/** The Constant ITERATIONS. */
private static final int ITERATIONS = 1;
......
package com.jh.common.security;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
/**
* The Class RestAuthenticationEntryPoint.
*/
public final class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
/**
* @param request
* @param response
* @param authException
* @throws IOException
* @see org.springframework.security.web.AuthenticationEntryPoint#commence(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.springframework.security.core.AuthenticationException)
*/
@Override
public final void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
\ No newline at end of file
package com.jh.common.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
/**
* The Class RestAuthenticationSuccessHandler.
*/
public class RestAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler{
/**
* Instantiates a new rest authentication success handler.
*/
public RestAuthenticationSuccessHandler(){
super();
}
/**
* @param request
* @param response
* @param authentication
* @throws ServletException
* @throws IOException
* @see org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler#onAuthenticationSuccess(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.springframework.security.core.Authentication)
*/
@Override
public void onAuthenticationSuccess( final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication ) throws ServletException, IOException{
//super.onAuthenticationSuccess( request, response, authentication );
this.clearAuthenticationAttributes( request );
}
}
\ No newline at end of file
/*
* Created on 28. 7. 2017 9:44:08
*
*/
package com.jh.common.security.api;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.jh.common.security.AuthError;
import com.jh.common.security.AuthService;
import com.jh.common.security.PasswordUtils;
import com.jh.common.security.ResetPassword;
import com.jh.common.security.Signup;
import com.jh.common.utils.Utils;
import com.jh.common.web.error.ErrorMessage;
import com.jh.common.web.error.RestApiException;
/**
* The Class AuthController.
*
* @author Jan Hrabal
*/
@Controller
public class AuthApiController {
/** The auth service. */
@Autowired
private AuthService authService;
@Value("${auth.controller.signup.enabled:true}")
private boolean signupEnabled;
@Value("${auth.controller.reset.enabled:true}")
private boolean resetEnabled;
/**
* Do logout.
*
* @param session the session
*/
protected void doLogout(HttpSession session) {
SecurityContextHolder.clearContext();
if (session != null) {
session.invalidate();
}
}
/**
* Logout api.
*
* @param session the session
*/
@RequestMapping({"/auth/logout"})
@ResponseStatus(HttpStatus.NO_CONTENT)
public void logoutApi(HttpSession session) {
doLogout(session);
}
@RequestMapping(path = "/auth/login", method = RequestMethod.POST)
public @ResponseBody LoginResponse login(@RequestBody LoginRequest login) {
Utils.sleep(250);
Authentication auth = null;
try {
auth = authService.authenticate(login.getUsername(), login.getPassword());
} catch (AuthenticationException e) {
//do nothing - will be resolved later
}
if (auth == null) {
throw new RestApiException(HttpStatus.FORBIDDEN, ErrorMessage.withCode("AUTH.BAD_USERNAME_OR_PASSWORD"));
}
return new LoginResponse("Authentication", "Basic " + Base64.getEncoder().encodeToString((login.getUsername() + ":" + login.getPassword()).getBytes()));
}
/**
* Signup.
*
* @param signup the signup
* @param locale the locale
* @return the response entity
*/
@RequestMapping(path = "/auth/signup", method = RequestMethod.POST)
public ResponseEntity<List<AuthError>> signup(@RequestBody Signup signup, Locale locale) {
if (!signupEnabled) {
return ResponseEntity.notFound().build();
}
//validate
if (signup == null) {
return new ResponseEntity<>(Collections.singletonList(new AuthError(null, "BadRequest")), HttpStatus.BAD_REQUEST);
}
signup.setLocale(locale);
List<AuthError> errors = new ArrayList<>();
//TODO validate email
if (!StringUtils.hasText(signup.getUsername())) {
errors.add(new AuthError("username", "BadUsername"));
}
if (!PasswordUtils.validatePassword(signup.getPassword())) {
errors.add(new AuthError("password", "BadPassword"));
}
try {
List<AuthError> errs = authService.register(signup);
if (errs == null || errs.isEmpty()) {
Authentication auth = authService.authenticate(signup.getUsername(), signup.getPassword());
SecurityContextHolder.getContext().setAuthentication(auth);
} else {
errors.addAll(errs);
}
} catch (Exception e) {
e.printStackTrace();
errors.add(new AuthError(null, e.getMessage()));
}
//everything was OK - log user in
if (errors.isEmpty()) {
//return no errors
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
@RequestMapping(path = "/auth/resetRequest", method = RequestMethod.POST)
public ResponseEntity<List<AuthError>> resetToken(@RequestBody ResetPassword request) {
if (!resetEnabled) {
return ResponseEntity.notFound().build();
}
authService.generateResetToken(request.getUsername());
return ResponseEntity.accepted().build();
}
@RequestMapping(path = "/auth/resetPassword", method = RequestMethod.POST)
public ResponseEntity<List<AuthError>> reset(@RequestBody ResetPassword resetPassword) {
if (!resetEnabled) {
return ResponseEntity.notFound().build();
}
String token = resetPassword.getToken();
if (!StringUtils.hasText(token)) {
return new ResponseEntity<>(Collections.singletonList(new AuthError(null, "BadRequest")), HttpStatus.BAD_REQUEST);
}
List<AuthError> errors = new ArrayList<>();
try {
List<AuthError> errs = authService.resetPassword(resetPassword);
if (errs == null || errs.isEmpty()) {
Authentication auth = authService.authenticate(resetPassword.getUsername(), resetPassword.getPassword());
SecurityContextHolder.getContext().setAuthentication(auth);
} else {
errors.addAll(errs);
}
} catch (Exception e) {
e.printStackTrace();
errors.add(new AuthError(null, e.getMessage()));
}
if (errors.isEmpty()) {
//return no errors
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
}
package com.jh.common.security.api;
public class LoginRequest {
private String username;
private String password;
public LoginRequest() {
}
public LoginRequest(String username, String password) {
super();
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
package com.jh.common.security.api;
public class LoginResponse {
private String header;
private String token;
public LoginResponse() {
}
public LoginResponse(String header, String token) {
super();
this.header = header;
this.token = token;
}
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
LoginTitle=Přihlášení
LoginUsername=Přihlašovací jméno
LoginPassword=Heslo
LoginButton=Přihlásit
LoginButtonBack=Zpět
LoginResetPasswordMessage=Zapomenuté heslo?
LoginButtonResetPassword=Obnovit heslo
LoginSignupMessage=Nemáte ještě svůj účet?
LoginButtonSignup=Vytvořit nový účet
LoginErrorEmailRequired=Přihlašovací jméno musí být vyplněno
LoginErrorPasswordRequired=Heslo musí být vyplněno
LoginErrorBadCredentials=Neplatné přihlašovací údaje
LoginErrorUnexpected=Nastala neočekávaná chyba
window.login = {
login: function(user, opts) {
jh.post('/api/auth/login', user, function(status, data) {
if (status > 199 && status < 300) {
var user = data;
if (typeof opts.onSuccess == "function") {
opts.onSuccess(user);
}
} else if (status == 401 || status == 403) {
opts.onError([{ field: "username", code: 'BadUsernameOrPassword' }]);
} else {
if (typeof opts.onError == "function") {
var err = data;
if (err) {
if (!(err instanceof Array)) {
err = [err];
}
var errs = [];
var i = null;
for (i = 0; i < err.length; i++) {
errs.push(err[i] || { code: 'UnexpectedError' });
}
opts.onError(errs);
}
}
}
});
},
reset: function(login, opts) {
jh.post('/api/auth/resetRequest', { username: login }, function(status, data) {
if (status > 199 && status < 300) {
var user = data;
if (typeof opts.onSuccess == "function") {
opts.onSuccess(user);
}
} else {
if (typeof opts.onError == "function") {
var err = data;
if (err) {
if (!(err instanceof Array)) {
err = [err];
}
var errs = [];
var i = null;
for (i = 0; i < err.length; i++) {
errs.push(err[i] || { code: 'UnexpectedError' });
}
opts.onError(errs);
}
}
}
});
},
form: function(opts) {
if (!opts) {
opts = {};
}
var
that = this,
formId = opts.formId || "loginForm",
usernameId = opts.usernameId || "loginUsername",
passwordId = opts.passwordId || "loginPassword",
loginErrorId = opts.loginErrorId || "loginErrorId",
resetLinkId = opts.resetLinkId || "resetLink",
loginButtonId = opts.loginButtonId || "loginButton",
activityIndicatorId = opts.activityIndicatorId || "authActivityIndicator",
onSubmitHandler = typeof opts.onSubmitHandler == "function" ? opts.onSubmitHandler : function() {},
onSuccessHandler = typeof opts.onSuccessHandler == "function" ? opts.onSuccessHandler : function() {
window.location = opts.loginTargetUri || "/";
},
onErrorHandler = typeof opts.onErrorHandler == "function" ? opts.onErrorHandler : function(errs) {
if (!errs || !errs.length) {
return;
}
var
e = null,
i = null,
el = null,
field = null,
errEl = null;
for (i = 0; i < errs.length; i++) {
e = errs[i];
if (e.field) {
if (e.field == "username") {
field = usernameId;
} else if (e.field == "password") {
field = passwordId;
}
}
if (field) {
el = jh.dom(field);
jh.addClass(el, "error");
errEl = jh.dom(field + 'Error');
errEl.innerHTML = e.label || e.error;
jh.show(errEl);
} else {
errEl = jh.dom(loginErrorId);
errEl.innerHTML = e.label || e.error;
jh.show(errEl);
}
}
},
clearErrors = typeof opts.clearErrors == "function" ? opts.clearErrors : function() {
clearError(usernameId + "Error");
clearError(passwordId + "Error");
clearError(loginErrorId);
},
clearError = typeof opts.clearError == "function" ? opts.clearError : function (id) {
var el = jh.dom(id);
el.innerHTML = "";
jh.hide(el);
},
disable = function() {
jh.disable(loginButtonId);
jh.disable(usernameId);
jh.disable(passwordId);
jh.show(activityIndicatorId);
},
enable = function() {
jh.enable(loginButtonId);
jh.enable(usernameId);
jh.enable(passwordId);
jh.hide(activityIndicatorId);
},
form = jh.dom(formId),
username = jh.dom(usernameId),
password = jh.dom(passwordId),
translate = typeof opts.translate === "function" ? opts.translate : function(key) {
if (window.__labels) {
return window.__labels[key] || key;
}
return key;
};
var resetLink = jh.dom(resetLinkId);
resetLink.onclick = function(e) {
if (event) {
event.returnValue = false;
}
if (e) {
e.preventDefault();
}
if (!username.value) {
onErrorHandler([{
error: translate('UsernameRequired'),
field: "username"
}]);
return false;
}
that.reset(username.value, {
onSuccess: function() {
onErrorHandler([{
error: translate('ResetTokenSent')
}]);
},
onError: onErrorHandler
});
return false;
};
form.onsubmit = function() {
var
user = {};
user.username = username.value;
user.password = password.value;
disable();
clearErrors();
onSubmitHandler(user);
var errors = [];
if (!user.username) {
errors.push({
error: translate('UsernameRequired'),
field: "username"
});
}
if (!user.password) {
errors.push({
error: translate('PasswordRequired'),
field: "password"
});
}
if (errors.length) {
onErrorHandler(errors);
enable();
return false;
}
//clear errors
that.login(user, {
onSuccess: function(user) {
onSuccessHandler(user);
},
onError: function(errors) {
var
errs = [];
for (var i = 0; i < errors.length; i++) {
err = errors[i] || {};
var label = translate(err.code || 'UnexpectedError');
errs.push({
field: err.field,
label: label
});
}
onErrorHandler(errs);
enable();
}
});
return false;
};
}
};
<!DOCTYPE html>
<html lang="en" class="auth-body">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="cache-control" content="max-age=0">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT">
<meta http-equiv="pragma" content="no-cache">
<link rel="stylesheet" type="text/css" th:href="${_styleHref}" href="p.css"/>
<title th:text="#{login.title}">Login</title>
<!-- [[#{login.error.usernameRequired}]] -->
<script type="text/javascript" th:utext="${_labels}">
/* TODO */
window.__labels = [];
window.__labels['UsernameRequired'] = "Vyplňte prosím e-mail";
window.__labels['PasswordRequired'] = "Vyplňte prosím heslo";
window.__labels['UnexpectedError'] = "Došlo k neočekávané chybě";
window.__labels['UsernameTaken'] = "Uživatel již existuje. Zvolte prosím jiný e-mail";
window.__labels['BadUsername'] = "Neplatný e-mail";
window.__labels['BadPassword'] = "Neplatné heslo";
window.__labels['BadUsernameOrPassword'] = "Neplatný e-mail nebo heslo";
window.__labels['ResetTokenSent'] = "Na emailovou adresu byl zaslán postup pro obnovení hesla";
</script>
</head>
<body class="auth-body">
<div class="auth-page">
<div class="auth-form">
<h1 class="small" th:text="#{login.title}">Login</h1>
<div id="loginError" class="auth-form-field-error" style="display:none;"></div>
<form id="loginForm">
<div id="loginErrorId"></div>
<div class="auth-form-input">
<input id="loginUsername" type="text" placeholder="username (email)" th:placeholder="#{login.login}"/>
<div id="loginUsernameError" class="auth-form-field-error"></div>
</div>
<div class="auth-form-input">
<input id="loginPassword" type="password" placeholder="password" th:placeholder="#{login.password}"/>
<div id="loginPasswordError" class="auth-form-field-error"></div>
</div>
<div class="auth-button-container">
<button id="loginButton" th:text="#{login.loginAction}">Login</button>
<div id="authActivityIndicator" class="activity-indicator-container" style="display:none;">
<div class="ball-pulse">
<div></div>
<div></div>
<div></div>
</div>
</div>
</div>
<div>
<p class="message" th:if="${signupEnabled}">
<a href="/auth/signup" th:text="#{login.signupLink}">Create new account</a>
</p>
<p class="message" th:if="${homeEnabled}">
<a href="/" th:text="#{login.homeLink}">Home</a>
</p>
</div>
<div>
<p class="message" th:if="${resetEnabled}">
<a href="/auth/reset" id="resetLink" th:text="#{login.resetLink}">Having trouble logging in?</a>
</p>
</div>
</form>
</div>
</div>
<script type="text/javascript" src="/utils.js"></script>
<script type="text/javascript" src="/auth/login.js"></script>
<script defer="defer">
(function() {
login.form({
formId: "loginForm",
usernameId: "loginUsername",
passwordId: "loginPassword",
loginTargetUri: "[[${loginTargetUri}]]",
resetTargetUri: "[[${resetTargetUri}]]"
});
})();
</script>
</body>
</html>
\ No newline at end of file
#LOGIN page
login.title=Login
login.login=Email
login.loginPlaceholder=your@email
login.password=Password
login.passwordPlaceholder=your secure password
login.loginAction=Login
login.signupLink=Create new account
login.homeLink=Home
login.resetLink=Having trouble logging in?
#SIGNUP page
signup.title=Signup
signup.username=Email
signup.usernamePlaceholder=your@email
signup.password=Password
signup.passwordPlaceholder=your secure password
signup.signupAction=Create my free account
signup.disclaimer=By clicking "Create my free account", you agree to our <a href="/terms.html" target="_terms"> terms of service</a>
signup.loginLink=Login
#RESET page
reset.title=Reset password
reset.username=Email
reset.usernamePlaceholder=your@email
reset.password=New password
reset.passwordPlaceholder=your new password
reset.resetAction=Change password
reset.loginLink=Login
#RESET email
email.reset.subject=Password reset request
email.reset.title=Password reset request
email.reset.text=A new request for a password change has been issued.\n\nTo reset your password, go to <a href="{0}">{0}</a>\n\nThe link is active for 3 days
email.reset.html=A new request for a password change has been issued.<br/><br/>To reset your password, go to <a href="{0}">{0}</a><br/><br/><i><sup>*</sup>The link is active for 3 days</i>
#AUTH errors
auth.error.usernameRequired=Email is required
auth.error.passwordRequired=Password is required
auth.error.unexpectedError=Unexpected error occured
auth.error.usernameTaken=Email is already registered
auth.error.badUsername=Bad email
auth.error.badPassword=Bad password
auth.error.badUsernameOrPassword=Bad email or password
auth.error.resetTokenSent=An email with instructions has been sent to your email address
auth.error.resetInvalidToken=Invalid token
#LOGIN page
login.title=Přihlášení
login.login=Email
login.loginPlaceholder=váš@email
login.password=Heslo
login.passwordPlaceholder=vaše bezpečné heslo
login.loginAction=Přihlásit
login.signupLink=Založit nový účet
login.homeLink=Domů
login.resetLink=Máte problémy s přihlášením?
#SIGNUP page
signup.title=Registrace
signup.username=Email
signup.usernamePlaceholder=váš@email
signup.password=Heslo
signup.passwordPlaceholder=vaše bezpečné heslo
signup.signupAction=Založit účet zdarma
signup.disclaimer=Kliknutím na "Založit účet zdarma" souhlasíte s&#160;našimi <a href="/terms.html" target="_terms"> podmínkami služby</a>
signup.loginLink=Přihlášení
#RESET page
reset.title=Obnova hesla
reset.username=Email
reset.usernamePlaceholder=váš@email
reset.password=Nové heslo
reset.passwordPlaceholder=vaše nové heslo
reset.resetAction=Nastavit
reset.loginLink=Login
#RESET email
email.reset.subject=Požadavek na obnovení hesla
email.reset.title=Požadavek na obnovení hesla
email.reset.text=Přijali jsme nový požadavek na změnu Vašeho hesla.\n\nPro obnovení Vašeho hesla prosím navštivte {0}\n\nOdkaz je funkční po dobu 3 dnů
email.reset.html=Přijali jsme nový požadavek na změnu Vašeho hesla.<br/><br/>Pro obnovení Vašeho hesla prosím navštivte <a href="{0}">{0}</a><br/><br/><i><sup>*</sup>Odkaz je funkční po dobu 3 dnů</i>
#AUTH errors
auth.error.usernameRequired=Vyplňte prosím e-mail
auth.error.passwordRequired=Vyplňte prosím heslo
auth.error.unexpectedError=Došlo k neočekávané chybě
auth.error.usernameTaken=Uživatel již existuje. Zvolte prosím jiný e-mail
auth.error.badUsername=Neplatný e-mail
auth.error.badPassword=Neplatné heslo
auth.error.badUsernameOrPassword=Neplatný e-mail nebo heslo
auth.error.resetTokenSent=E-mail s instrukcemi byl zaslán na vaši e-mailovou adresu
window.reset = {
getParameter: function(parameterName) {
if (!location.search || !location.search.length) {
return null
}
var result = null,
tmp = [];
var items = location.search.substr(1).split("&");
for (var index = 0; index < items.length; index++) {
tmp = items[index].split("=");
if (tmp[0] === parameterName) {
result = decodeURIComponent(tmp[1]);
}
}
return result;
},
reset: function(user, opts) {
jh.post('/api/auth/resetPassword', user, function(status, data) {
if (status > 199 && status < 300) {
var user = data;
if (typeof opts.onSuccess == "function") {
opts.onSuccess(user);
}
} else {
if (typeof opts.onError == "function") {
var err = data;
if (err) {
if (!(err instanceof Array)) {
err = [err];
}
var errs = [];
var i = null;
for (i = 0; i < err.length; i++) {
errs.push(err[i] || { code: 'UnexpectedError' });
}
opts.onError(errs);
}
}
}
});
},
form: function(opts) {
if (!opts) {
opts = {};
}
var
that = this,
formId = opts.formId || "resetForm",
usernameId = opts.usernameId || "resetUsername",
passwordId = opts.passwordId || "resetPassword",
resetErrorId = opts.resetErrorId || "resetErrorId",
loginButtonId = opts.resetButtonId || "resetButton",
activityIndicatorId = opts.activityIndicatorId || "authActivityIndicator",
onSubmitHandler = typeof opts.onSubmitHandler == "function" ? opts.onSubmitHandler : function() {},
onSuccessHandler = typeof opts.onSuccessHandler == "function" ? opts.onSuccessHandler : function() {
window.location= '/';
},
onErrorHandler = typeof opts.onErrorHandler == "function" ? opts.onErrorHandler : function(errs) {
if (!errs || !errs.length) {
return;
}
var
e = null,
i = null,
el = null,
field = null,
errEl = null;
for (i = 0; i < errs.length; i++) {
e = errs[i];
if (e.field) {
if (e.field == "username") {
field = usernameId;
} else if (e.field == "password") {
field = passwordId;
}
}
if (field) {
el = jh.dom(field);
jh.addClass(el, "error");
errEl = jh.dom(field + 'Error');
errEl.innerHTML = e.label || e.error;
jh.show(errEl);
} else {
errEl = jh.dom(resetErrorId);
errEl.innerHTML = e.label || e.error;
jh.show(errEl);
}
}
},
disable = function() {
jh.disable(loginButtonId);
jh.disable(usernameId);
jh.disable(passwordId);
jh.show(activityIndicatorId);
},
enable = function() {
jh.enable(loginButtonId);
jh.enable(usernameId);
jh.enable(passwordId);
jh.hide(activityIndicatorId);
},
clearErrors = typeof opts.clearErrors == "function" ? opts.clearErrors : function() {
clearError(usernameId + "Error");
clearError(passwordId + "Error");
clearError(resetErrorId);
},
clearError = typeof opts.clearError == "function" ? opts.clearError : function (id) {
var el = jh.dom(id);
el.innerHTML = "";
jh.hide(el);
},
form = jh.dom(formId),
username = jh.dom(usernameId),
password = jh.dom(passwordId),
translate = typeof opts.translate === "function" ? opts.translate : function(key) {
if (window.__labels) {
return window.__labels[key] || key;
}
return key;
};
form.onsubmit = function() {
var
user = {};
user.username = username.value;
user.password = password.value;
user.token = that.getParameter("t");
disable();
clearErrors();
onSubmitHandler(user);
var errors = [];
if (!user.username) {
errors.push({
code: 'UsernameRequired',
label: translate('UsernameRequired'),
field: "username"
});
}
if (!user.password) {
errors.push({
code: 'PasswordRequired',
label: translate('PasswordRequired'),
field: "password"
});
}
if (errors.length) {
onErrorHandler(errors);
enable();
return false;
}
//clear errors
that.reset(user, {
onSuccess: function(user) {
onSuccessHandler(user);
},
onError: function(errors) {
var
errs = [];
for (var i = 0; i < errors.length; i++) {
err = errors[i] || {};
var label = translate(err.code || 'UnexpectedError');
errs.push({
field: err.field,
label: label,
code: err.code
});
}
onErrorHandler(errs);
enable();
}
});
return false;
};
}
};
<!DOCTYPE html>
<html lang="en" class="auth-body">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="cache-control" content="max-age=0">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT">
<meta http-equiv="pragma" content="no-cache">
<link rel="stylesheet" type="text/css" th:href="${_styleHref}" href="p.css"/>
<title th:text="#{reset.title}">reset</title>
<script type="text/javascript" th:utext="${_labels}">
/* TODO */
window.__labels = [];
window.__labels['UsernameRequired'] = "Vyplňte prosím e-mail";
window.__labels['PasswordRequired'] = "Vyplňte prosím heslo";
window.__labels['UnexpectedError'] = "Došlo k neočekávané chybě";
window.__labels['BadUsername'] = "Neplatný e-mail";
window.__labels['BadPassword'] = "Neplatné heslo";
window.__labels['BadUsernameOrPassword'] = "Neplatný e-mail nebo heslo";
</script>
</head>
<body class="auth-body">
<div class="auth-page">
<div class="auth-form">
<h1 class="small" th:text="#{reset.title}">reset</h1>
<div id="resetError" class="auth-form-field-error" style="display:none;"></div>
<form id="resetForm">
<div id="resetErrorId"></div>
<div class="auth-form-input">
<input id="resetUsername" type="text" placeholder="username (email)" th:placeholder="#{reset.usernamePlaceholder}"/>
<div id="resetUsernameError" class="auth-form-field-error"></div>
</div>
<div class="auth-form-input">
<input id="resetPassword" type="password" placeholder="password" th:placeholder="#{reset.passwordPlaceholder}"/>
<div id="resetPasswordError" class="auth-form-field-error"></div>
</div>
<div class="auth-button-container">
<button id="resetButton" th:text="#{reset.resetAction}">Reset password</button>
<div id="authActivityIndicator" class="activity-indicator-container" style="display:none;">
<div class="ball-pulse">
<div></div>
<div></div>
<div></div>
</div>
</div>
</div>
<div>
<p class="message">
<a href="/auth/login" th:text="#{reset.loginLink}">Login</a>
</p>
</div>
</form>
</div>
</div>
<script type="text/javascript" src="/utils.js"></script>
<script type="text/javascript" src="/auth/reset.js"></script>
<script defer="defer">
(function() {
reset.form({
formId: "resetForm",
usernameId: "resetUsername",
passwordId: "resetPassword"
});
})();
</script>
</body>
</html>
\ No newline at end of file
window.signup = {
signup: function(user, opts) {
jh.post('/api/auth/signup', user, function(status, data) {
if (status > 199 && status < 300) {
var user = data;
if (typeof opts.onSuccess == "function") {
opts.onSuccess(user);
}
} else {
if (typeof opts.onError == "function") {
var err = data;
if (err) {
if (!(err instanceof Array)) {
err = [err];
}
var errs = [];
var i = null;
for (i = 0; i < err.length; i++) {
errs.push(err[i] || { code: 'UnexpectedError' });
}
opts.onError(errs);
}
}
}
});
},
form: function(opts) {
if (!opts) {
opts = {};
}
var
that = this,
formId = opts.formId || "signupForm",
usernameId = opts.usernameId || "signupUsername",
passwordId = opts.passwordId || "signupPassword",
signupErrorId = opts.signupErrorId || "signupErrorId",
loginButtonId = opts.signupButtonId || "signupButton",
activityIndicatorId = opts.activityIndicatorId || "authActivityIndicator",
onSubmitHandler = typeof opts.onSubmitHandler == "function" ? opts.onSubmitHandler : function() {},
onSuccessHandler = typeof opts.onSuccessHandler == "function" ? opts.onSuccessHandler : function() {
window.location= '/';
},
onErrorHandler = typeof opts.onErrorHandler == "function" ? opts.onErrorHandler : function(errs) {
if (!errs || !errs.length) {
return;
}
var
e = null,
i = null,
el = null,
field = null,
errEl = null;
for (i = 0; i < errs.length; i++) {
e = errs[i];
if (e.field) {
if (e.field == "username") {
field = usernameId;
} else if (e.field == "password") {
field = passwordId;
}
}
if (field) {
el = jh.dom(field);
jh.addClass(el, "error");
errEl = jh.dom(field + 'Error');
errEl.innerHTML = e.label || e.error;
jh.show(errEl);
} else {
errEl = jh.dom(signupErrorId);
errEl.innerHTML = e.label || e.error;
jh.show(errEl);
}
}
},
disable = function() {
jh.disable(loginButtonId);
jh.disable(usernameId);
jh.disable(passwordId);
jh.show(activityIndicatorId);
},
enable = function() {
jh.enable(loginButtonId);
jh.enable(usernameId);
jh.enable(passwordId);
jh.hide(activityIndicatorId);
},
clearErrors = typeof opts.clearErrors == "function" ? opts.clearErrors : function() {
clearError(usernameId + "Error");
clearError(passwordId + "Error");
clearError(signupErrorId);
},
clearError = typeof opts.clearError == "function" ? opts.clearError : function (id) {
var el = jh.dom(id);
el.innerHTML = "";
jh.hide(el);
},
form = jh.dom(formId),
username = jh.dom(usernameId),
password = jh.dom(passwordId),
translate = typeof opts.translate === "function" ? opts.translate : function(key) {
if (window.__labels) {
return window.__labels[key] || key;
}
return key;
};
form.onsubmit = function() {
var
user = {};
user.username = username.value;
user.password = password.value;
disable();
clearErrors();
onSubmitHandler(user);
var errors = [];
if (!user.username) {
errors.push({
code: 'UsernameRequired',
label: translate('UsernameRequired'),
field: "username"
});
}
if (!user.password) {
errors.push({
code: 'PasswordRequired',
label: translate('PasswordRequired'),
field: "password"
});
}
if (errors.length) {
onErrorHandler(errors);
enable();
return false;
}
//clear errors
that.signup(user, {
onSuccess: function(user) {
onSuccessHandler(user);
},
onError: function(errors) {
var
errs = [];
for (var i = 0; i < errors.length; i++) {
err = errors[i] || {};
var label = translate(err.code || 'UnexpectedError');
errs.push({
field: err.field,
label: label,
code: err.code
});
}
onErrorHandler(errs);
enable();
}
});
return false;
};
}
};
<!DOCTYPE html>
<html lang="en" class="auth-body">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="cache-control" content="max-age=0">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT">
<meta http-equiv="pragma" content="no-cache">
<link rel="stylesheet" type="text/css" th:href="${_styleHref}" href="p.css"/>
<title th:text="#{signup.title}">Signup</title>
<script type="text/javascript" th:utext="${_labels}">
/* TODO */
window.__labels = [];
window.__labels['UsernameRequired'] = "Vyplňte prosím e-mail";
window.__labels['PasswordRequired'] = "Vyplňte prosím heslo";
window.__labels['UnexpectedError'] = "Došlo k neočekávané chybě";
window.__labels['UsernameTaken'] = "Uživatel již existuje. Zvolte prosím jiný e-mail";
window.__labels['BadUsername'] = "Neplatný e-mail";
window.__labels['BadPassword'] = "Neplatné heslo";
window.__labels['BadUsernameOrPassword'] = "Neplatný e-mail nebo heslo";
</script>
</head>
<body class="auth-body">
<div class="auth-page">
<div class="auth-form">
<h1 class="small" th:text="#{signup.title}">Signup</h1>
<div id="signupError" class="auth-form-field-error" style="display:none;"></div>
<form id="signupForm">
<div id="signupErrorId"></div>
<div class="auth-form-input">
<input id="signupUsername" type="text" placeholder="username (email)" th:placeholder="#{signup.usernamePlaceholder}"/>
<div id="signupUsernameError" class="auth-form-field-error"></div>
</div>
<div class="auth-form-input">
<input id="signupPassword" type="password" placeholder="password" th:placeholder="#{signup.passwordPlaceholder}"/>
<div id="signupPasswordError" class="auth-form-field-error"></div>
</div>
<div class="auth-button-container">
<button id="signupButton" th:text="#{signup.signupAction}">Create new account</button>
<div id="authActivityIndicator" class="activity-indicator-container" style="display:none;">
<div class="ball-pulse">
<div></div>
<div></div>
<div></div>
</div>
</div>
</div>
<div>
<p class="message" th:utext="#{signup.disclaimer}"></p>
</div>
<div>
<p class="message">
<a href="/auth/login" th:text="#{signup.loginLink}">Login</a>
</p>
</div>
</form>
</div>
</div>
<script type="text/javascript" src="/utils.js"></script>
<script type="text/javascript" src="/auth/signup.js"></script>
<script defer="defer">
(function() {
signup.form({
formId: "signupForm",
usernameId: "signupUsername",
passwordId: "signupPassword"
});
})();
</script>
</body>
</html>
\ No newline at end of file
/*
* Created on 24. 1. 2018 9:08:52
*
*/
package com.jh.common.security.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import com.jh.common.jpa.AbstractIdEntity;
/**
* The Class AppRole.
*
* @author Jan Hrabal
*/
@Entity
@Table(name = "APP_ROLE")
public class AppRole extends AbstractIdEntity {
private static final long serialVersionUID = 1L;
@Column(name = "NAME")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/*
* Created on 27. 7. 2017 15:46:57
*
*/
package com.jh.common.security.model;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Version;
import com.jh.common.jpa.AbstractIdEntity;
/**
* The Class AppUser.
*
* @author Jan Hrabal
*/
@Entity
@Table(name = "APP_USER")
public class AppUser extends AbstractIdEntity {
private static final long serialVersionUID = 1L;
/** The version. */
@Version
@Column(name = "VERSION")
private Long version;
/** The deleted. */
@Column(name = "DELETED")
private Boolean deleted;
/** The email. */
@Column(name = "EMAIL")
private String email;
/** The password. */
@Column(name = "PASSWORD")
private String password;
/** The password salt. */
@Column(name = "PASSWORD_SALT")
private String passwordSalt;
/** The first name. */
@Column(name = "FIRST_NAME")
private String firstName;
/** The last name. */
@Column(name = "LAST_NAME")
private String lastName;
@ManyToMany
@JoinTable(name="APP_USER_ROLE",
joinColumns=
@JoinColumn(name="USER_ID", referencedColumnName="ID"),
inverseJoinColumns=
@JoinColumn(name="ROLE_ID", referencedColumnName="ID")
)
private Set<AppRole> roles;
/**
* Gets the email.
*
* @return the email
*/
public String getEmail() {
return email;
}
/**
* Sets the email.
*
* @param email the new email
*/
public void setEmail(String email) {
this.email = email;
}
/**
* Gets the version.
*
* @return the version
*/
public Long getVersion() {
return version;
}
/**
* Sets the version.
*
* @param version the new version
*/
public void setVersion(Long version) {
this.version = version;
}
/**
* Gets the deleted.
*
* @return the deleted
*/
public Boolean getDeleted() {
return deleted;
}
/**
* Sets the deleted.
*
* @param deleted the new deleted
*/
public void setDeleted(Boolean deleted) {
this.deleted = deleted;
}
/**
* Gets the password.
*
* @return the password
*/
public String getPassword() {
return password;
}
/**
* Sets the password.
*
* @param password the new password
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Gets the password salt.
*
* @return the password salt
*/
public String getPasswordSalt() {
return passwordSalt;
}
/**
* Sets the password salt.
*
* @param passwordSalt the new password salt
*/
public void setPasswordSalt(String passwordSalt) {
this.passwordSalt = passwordSalt;
}
/**
* Gets the first name.
*
* @return the first name
*/
public String getFirstName() {
return firstName;
}
/**
* Sets the first name.
*
* @param firstName the new first name
*/
public void setFirstName(String firstName) {
this.firstName = firstName;
}
/**
* Gets the last name.
*
* @return the last name
*/
public String getLastName() {
return lastName;
}
/**
* Sets the last name.
*
* @param lastName the new last name
*/
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Set<AppRole> getRoles() {
return roles;
}
public void setRoles(Set<AppRole> roles) {
this.roles = roles;
}
}
package com.jh.common.security.repository;
import javax.persistence.Query;
import org.springframework.util.StringUtils;
import com.jh.common.jpa.AbstractHibernateRepository;
import com.jh.common.security.model.AppUser;
public class AppUserRepository extends AbstractHibernateRepository {
public AppUser findByUsername(String username) {
if (!StringUtils.hasText(username)) {
return null;
}
Query q = entityManager.createQuery("select au from AppUser au where lower(au.email) = :email");
q.setParameter("email", username.trim().toLowerCase());
return singleResult(q);
}
}
package com.jh.common.security.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.transaction.annotation.Transactional;
import com.jh.common.security.AuthError;
import com.jh.common.security.AuthService;
import com.jh.common.security.PasswordUtils;
import com.jh.common.security.ResetPassword;
import com.jh.common.security.Signup;
import com.jh.common.security.model.AppUser;
import com.jh.common.security.repository.AppUserRepository;
public class AppUserAuthService implements AuthService {
private AppUserRepository appUserRepository;
@Override
@Transactional
public Authentication authenticate(String username, String password) throws BadCredentialsException {
AppUser user = appUserRepository.findByUsername(username);
if (user == null) {
throw new BadCredentialsException("User not found");
}
if (!PasswordUtils.checkPassword(password, user.getPassword(), user.getPasswordSalt())) {
throw new BadCredentialsException("Bad password");
}
return new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword());
}
@Override
public List<AuthError> register(Signup signup) {
return null;
}
@Override
public void generateResetToken(String login) {
}
@Override
public List<AuthError> resetPassword(ResetPassword resetPassword) {
return null;
}
@Autowired(required = false)
public void setAppUserRepository(AppUserRepository appUserRepository) {
this.appUserRepository = appUserRepository;
}
}
/*
* Created on 28. 7. 2017 15:26:38
*
*/
package com.jh.common.web;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import java.util.Scanner;
import java.util.function.Supplier;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
/**
* The Class ClientContent.
*
* @author Jan Hrabal
*/
public class ClientContent implements ResourceLoaderAware {
/** The Constant LOG. */
private static final Logger LOG = LoggerFactory.getLogger(ClientContent.class);
/** The response modifiers. */
private ClientContentModifier[] responseModifiers = new ClientContentModifier[0];
/** The use cache. */
private boolean useCache;
/** The cached content. */
private String cachedContent;
/** The path. */
private String path;
/** The resource loader. */
private ResourceLoader resourceLoader;
/** The input stream supplier. */
private Supplier<InputStream> inputStreamSupplier;
/**
* Instantiates a new client content.
*/
public ClientContent() {
super();
}
/**
* Instantiates a new client content.
*
* @param resourceLoader the resource loader
* @param path the path
* @param useCache the use cache
* @param responseModifiers the response modifiers
*/
public ClientContent(ResourceLoader resourceLoader, String path, boolean useCache, ClientContentModifier...responseModifiers) {
super();
this.resourceLoader = resourceLoader;
this.path = path;
this.useCache = useCache;
this.responseModifiers = responseModifiers;
inputStreamSupplier = this::fetchResource;
}
/**
* Instantiates a new client content.
*
* @param inputStreamSupplier the input stream supplier
* @param useCache the use cache
* @param responseModifiers the response modifiers
*/
public ClientContent(Supplier<InputStream> inputStreamSupplier, boolean useCache, ClientContentModifier...responseModifiers) {
super();
this.useCache = useCache;
this.responseModifiers = responseModifiers;
this.inputStreamSupplier = inputStreamSupplier;
if (responseModifiers != null) {
this.setResponseModifiers(responseModifiers);
}
}
/**
* Inits the.
*/
@PostConstruct
public void init() {
inputStreamSupplier = this::fetchResource;
}
/**
* Gets the content.
*
* @param request the request
* @return the content
*/
public String getContent(HttpServletRequest request) {
String content = useCache && cachedContent != null ? cachedContent : (cachedContent = readContent());
return evaluateIndexContent(content, request);
}
/**
* Reads HTML content from provided path (classpath).
*
* @return the string
*/
protected String readContent() {
Objects.requireNonNull(inputStreamSupplier, "Input stream loader is not initialized");
try (InputStream is = inputStreamSupplier.get()) {
@SuppressWarnings("resource")
Scanner s = new Scanner(is).useDelimiter("\\A");
String content = s.hasNext() ? s.next() : null;
s.close();
return content;
} catch (Exception e) {
throw new IllegalStateException(String.format("Cannot read index file '%s'.", path), e);
}
}
/**
* Calls configured {@link ClientContentModifier}s.
*
* @param content the content
* @param request the request
* @return the string
*/
protected String evaluateIndexContent(String content, HttpServletRequest request) {
for (ClientContentModifier cirm : responseModifiers) {
content = cirm.modifyResponse(content, request);
}
return content;
}
/**
* Sets the response modifiers.
*
* @param responseModifiers the new response modifiers
*/
public void setResponseModifiers(ClientContentModifier[] responseModifiers) {
this.responseModifiers = responseModifiers;
}
/**
* Sets the use cache.
*
* @param useCache the new use cache
*/
public void setUseCache(boolean useCache) {
this.useCache = useCache;
}
/**
* Sets the path.
*
* @param path the new path
*/
public void setPath(String path) {
this.path = path;
}
/**
* Sets the resource loader.
*
* @param arg0 the new resource loader
* @see org.springframework.context.ResourceLoaderAware#setResourceLoader(org.springframework.core.io.ResourceLoader)
*/
@Override
@Autowired(required = false)
public void setResourceLoader(ResourceLoader arg0) {
resourceLoader = arg0;
}
/**
* Fetch resource.
*
* @return the input stream
*/
protected InputStream fetchResource() {
if (resourceLoader == null) {
LOG.warn("There is no resource loader defined. TODO");
return null;
}
Resource resource = resourceLoader.getResource(path);
if (!resource.exists()) {
LOG.error(String.format("Cannot find resource '%s'", path));
return null;
}
try {
return resource.getInputStream();
} catch (IOException e) {
throw new RuntimeException(String.format("Cannot find resource '%s'", path), e);
}
};
/**
* Render.
*
* @param content the content
* @param response the response
*/
public static void render(String content, HttpServletResponse response) {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
try {
response.getWriter().print(content);
} catch (IOException e) {
throw new RuntimeException("Cannot render response", e);
}
response.setStatus(HttpServletResponse.SC_OK);
}
}
/*
* Created on 25. 7. 2017 9:53:03
*
*/
package com.jh.common.web;
import javax.servlet.http.HttpServletRequest;
/**
* Instances of this interface modify client response generated by {@link ClientContentFilter}.
*
* @author Jan Hrabal
*/
public interface ClientContentModifier {
/**
* Modifies response based on provided request .
*
* @param response current response
* @param request http request
* @return modified response
*/
public String modifyResponse(String response, HttpServletRequest request);
}
......@@ -23,12 +23,8 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.jh.common.web.list.PagingArgumentResolver;
import com.jh.common.web.list.SortingArgumentResolver;
/**
* The Class ReactApplicationContext.
*/
public class ReactApplicationContext implements WebMvcConfigurer {
public class JhWebConfig implements WebMvcConfigurer {
/**
* @param converters
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment