diff --git a/quartz-manager-api/.gitignore b/quartz-manager-api/.gitignore
index 233a70f..b889765 100644
--- a/quartz-manager-api/.gitignore
+++ b/quartz-manager-api/.gitignore
@@ -7,3 +7,5 @@
/mvnw
/mvnw.cmd
/.classpath
+/.idea/
+/quartz-manager.iml
diff --git a/quartz-manager-api/pom.xml b/quartz-manager-api/pom.xml
index 72afb43..caccc3c 100644
--- a/quartz-manager-api/pom.xml
+++ b/quartz-manager-api/pom.xml
@@ -26,6 +26,7 @@
+
org.springframework.boot
spring-boot-starter-web
@@ -58,6 +59,11 @@
org.springframework
spring-tx
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
org.springframework.boot
spring-boot-starter-tomcat
@@ -69,6 +75,7 @@
test
+
io.jsonwebtoken
jjwt
@@ -91,23 +98,31 @@
h2
runtime
-
org.codehaus.groovy
groovy
-
net.sourceforge.nekohtml
nekohtml
-
io.rest-assured
spring-mock-mvc
test
+
+ org.projectlombok
+ lombok
+ provided
+
+
+ org.apache.commons
+ commons-lang3
+ 3.10
+
+
org.quartz-scheduler
quartz
@@ -158,13 +173,6 @@
${springfox.version}
-
- org.projectlombok
- lombok
- provided
-
-
-
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/WebSecurityConfig.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/WebSecurityConfig.java
deleted file mode 100644
index b0b9f6f..0000000
--- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/WebSecurityConfig.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package it.fabioformosa.quartzmanager.configuration;
-
-import javax.annotation.Resource;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.core.annotation.Order;
-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.builders.WebSecurity;
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-import org.springframework.security.crypto.factory.PasswordEncoderFactories;
-import org.springframework.security.crypto.password.PasswordEncoder;
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
-
-import it.fabioformosa.quartzmanager.security.ComboEntryPoint;
-import it.fabioformosa.quartzmanager.security.auth.AuthenticationFailureHandler;
-import it.fabioformosa.quartzmanager.security.auth.AuthenticationSuccessHandler;
-
-//@Configuration
-//@EnableWebSecurity
-//@EnableGlobalMethodSecurity(prePostEnabled = true)
-public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
-
- // @Configuration
- @Order(1)
- public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter {
- @Override
- public void configure(WebSecurity web) throws Exception {
- web.ignoring().antMatchers("/","/v2/api-docs",
- "/swagger-resources/**",
- "/swagger-ui.html",
- "/webjars/**",
- "/csrf");
- }
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.csrf().disable() //
- .antMatcher("/notifications").authorizeRequests().anyRequest().hasAnyRole("ADMIN").and()
- .httpBasic();
- }
- }
-
- // @Configuration
- @Order(2)
- public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter {
-
- @Resource
- private ComboEntryPoint comboEntryPoint;
-
- @Autowired
- private AuthenticationSuccessHandler authenticationSuccessHandler;
-
- @Autowired
- private AuthenticationFailureHandler authenticationFailureHandler;
-
- @Override
- public void configure(WebSecurity web) throws Exception {
- web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**", "/webjars/**");
- }
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- // http.csrf().ignoringAntMatchers("/api/login", "/api/signup").and() //
- http.cors().and().csrf().disable().exceptionHandling().authenticationEntryPoint(comboEntryPoint)
- .and()//
- .authorizeRequests().anyRequest().authenticated().and()//
- .formLogin().loginPage("/api/login").successHandler(authenticationSuccessHandler)
- .failureHandler(authenticationFailureHandler).and().logout()
- .logoutRequestMatcher(new AntPathRequestMatcher("/api/logout"))
- .logoutSuccessUrl("/manager");
- }
- }
-
- @Value("${quartz-manager.account.user}")
- private String adminUser;
-
- @Value("${quartz-manager.account.pwd}")
- private String adminPwd;
-
- @Autowired
- public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
- PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
- auth.inMemoryAuthentication().withUser(adminUser).password(encoder.encode(adminPwd)).roles("ADMIN");
- }
-
-}
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/WebSecurityConfigJWT.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/WebSecurityConfigJWT.java
index 77bb44f..d264134 100644
--- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/WebSecurityConfigJWT.java
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/WebSecurityConfigJWT.java
@@ -1,10 +1,14 @@
package it.fabioformosa.quartzmanager.configuration;
+import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
@@ -14,114 +18,175 @@ import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
-import it.fabioformosa.quartzmanager.security.auth.AuthenticationFailureHandler;
-import it.fabioformosa.quartzmanager.security.auth.AuthenticationSuccessHandler;
-import it.fabioformosa.quartzmanager.security.auth.LogoutSuccess;
-import it.fabioformosa.quartzmanager.security.auth.RestAuthenticationEntryPoint;
-import it.fabioformosa.quartzmanager.security.auth.TokenAuthenticationFilter;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import it.fabioformosa.quartzmanager.configuration.properties.InMemoryAccountProperties;
+import it.fabioformosa.quartzmanager.configuration.properties.JwtSecurityProperties;
+import it.fabioformosa.quartzmanager.security.helpers.LoginConfigurer;
+import it.fabioformosa.quartzmanager.security.helpers.impl.AuthenticationFailureHandler;
+import it.fabioformosa.quartzmanager.security.helpers.impl.AuthenticationSuccessHandler;
+import it.fabioformosa.quartzmanager.security.helpers.impl.FormLoginConfig;
+import it.fabioformosa.quartzmanager.security.helpers.impl.JwtAuthenticationSuccessHandler;
+import it.fabioformosa.quartzmanager.security.helpers.impl.JwtAuthenticationSuccessHandlerImpl;
+import it.fabioformosa.quartzmanager.security.helpers.impl.JwtTokenAuthenticationFilter;
+import it.fabioformosa.quartzmanager.security.helpers.impl.JwtTokenHelper;
+import it.fabioformosa.quartzmanager.security.helpers.impl.QuartzManagerHttpSecurity;
+import it.fabioformosa.quartzmanager.security.helpers.impl.JwtUsernamePasswordFiterLoginConfig;
+import it.fabioformosa.quartzmanager.security.helpers.impl.LogoutSuccess;
/**
*
* @author Fabio.Formosa
*
*/
-
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter {
- @Value("${jwt.cookie}")
- private String TOKEN_COOKIE;
+ private static final String[] PATTERNS_SWAGGER_UI = {"/swagger-ui.html", "/v2/api-docs", "/swagger-resources/**", "/webjars/**"};
- // @Autowired
- // private CustomUserDetailsService jwtUserDetailsService;
+ private static final String LOGIN_PATH = "/api/login";
+ private static final String LOGOUT_PATH = "/api/logout";
- @Autowired
- private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
+ @Value("${server.servlet.context-path}")
+ private String contextPath;
- @Autowired
- private LogoutSuccess logoutSuccess;
+ @Value("${app.name}")
+ private String APP_NAME;
- @Autowired
- private AuthenticationSuccessHandler authenticationSuccessHandler;
+ @Value("${quartz-manager.security.login-model.form-login-enabled}")
+ private Boolean formLoginEnabled;
+ @Value("${quartz-manager.security.login-model.userpwd-filter-enabled}")
+ private Boolean userpwdFilterEnabled;
- @Autowired
- private AuthenticationFailureHandler authenticationFailureHandler;
+ @Autowired
+ private JwtSecurityProperties jwtSecurityProps;
- @Value("${quartz-manager.account.user}")
- private String adminUser;
+ @Autowired
+ private ObjectMapper objectMapper;
- @Value("${quartz-manager.account.pwd}")
- private String adminPwd;
+ @Autowired
+ private UserDetailsService userDetailsService;
- // @Bean
- // @Override
- // public AuthenticationManager authenticationManagerBean() throws Exception {
- // return super.authenticationManagerBean();
- // }
+ @Autowired
+ private InMemoryAccountProperties inMemoryAccountProps;
- @Override
- public void configure(WebSecurity web) throws Exception {
- web.ignoring().antMatchers("/css/**", //
- "/js/**", //
- "/img/**", //
- "/lib/**", //
- "/swagger-resources/**", "/swagger-ui.html","/v2/api-docs", //
- "/webjars/**");
- }
- @Autowired
- public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder)
- throws Exception {
- // authenticationManagerBuilder.userDetailsService(jwtUserDetailsService)
- // .passwordEncoder(passwordEncoder());
- PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
- authenticationManagerBuilder.inMemoryAuthentication().withUser(adminUser).password(encoder.encode(adminPwd)).roles("ADMIN");
- }
+ @Override
+ public void configure(AuthenticationManagerBuilder authenticationManagerBuilder)throws Exception {
+ configureInMemoryAuthentication(authenticationManagerBuilder);
+ }
- @Bean
- public TokenAuthenticationFilter jwtAuthenticationTokenFilter() throws Exception {
- return new TokenAuthenticationFilter();
- }
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.csrf().disable() //
+ .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() //
+ .exceptionHandling().authenticationEntryPoint(restAuthEntryPoint()).and() //
+ .addFilterBefore(jwtAuthenticationTokenFilter(), BasicAuthenticationFilter.class) //
+ .authorizeRequests().anyRequest().authenticated();
- @Bean
- @Override
- public UserDetailsService userDetailsServiceBean() throws Exception {
- return super.userDetailsServiceBean();
- }
+ QuartzManagerHttpSecurity.from(http).withLoginConfigurer(loginConfigurer(), logoutConfigurer()) //
+ .login(LOGIN_PATH, authenticationManager()).logout(LOGOUT_PATH);
- // @Bean
- // public PasswordEncoder passwordEncoder() {
- // return new BCryptPasswordEncoder();
- // }
+ // temporary disabled csfr
+ // http.csrf().ignoringAntMatchers("/api/login", "/api/signup") //
+ // .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) //
+ }
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- // http.csrf().ignoringAntMatchers("/api/login", "/api/signup") //
- // .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) //
- http.csrf().disable() //
- .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() //
- .exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint).and()
- .addFilterBefore(jwtAuthenticationTokenFilter(), BasicAuthenticationFilter.class)
- .authorizeRequests().anyRequest().authenticated().and().formLogin().loginPage("/api/login")
- .successHandler(authenticationSuccessHandler).failureHandler(authenticationFailureHandler)
- .and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/api/logout"))
- .logoutSuccessHandler(logoutSuccess).deleteCookies(TOKEN_COOKIE);
+ @Override
+ public void configure(WebSecurity web) throws Exception {
+ web.ignoring()//
+ .antMatchers(HttpMethod.GET, PATTERNS_SWAGGER_UI) //
+ .antMatchers(HttpMethod.GET,"/css/**", "/js/**", "/img/**", "/lib/**");
+ }
- }
+ private void configureInMemoryAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
+ PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
+ if(inMemoryAccountProps.isEnabled() && inMemoryAccountProps.getUsers() != null && !inMemoryAccountProps.getUsers().isEmpty()) {
+ InMemoryUserDetailsManagerConfigurer inMemoryAuth = authenticationManagerBuilder.inMemoryAuthentication();
+ inMemoryAccountProps.getUsers()
+ .forEach(u -> inMemoryAuth
+ .withUser(u.getName())
+ .password(encoder.encode(u.getPassword()))
+ .roles(u.getRoles().toArray(new String[0])));
+ }
+ }
- @Bean
- CorsConfigurationSource corsConfigurationSource() {
- UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
- source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
- return source;
- }
+ @Bean
+ CorsConfigurationSource corsConfigurationSource() {
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
+ return source;
+ }
+
+ @Bean
+ public LoginConfigurer formLoginConfigurer() {
+ JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler();
+ AuthenticationSuccessHandler authenticationSuccessHandler = new AuthenticationSuccessHandler(jwtAuthenticationSuccessHandler);
+ AuthenticationFailureHandler authenticationFailureHandler = new AuthenticationFailureHandler();
+ LoginConfigurer loginConfigurer = new FormLoginConfig(authenticationSuccessHandler, authenticationFailureHandler);
+ return loginConfigurer;
+ }
+
+ @Bean
+ public JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler() {
+ JwtTokenHelper jwtTokenHelper = jwtTokenHelper();
+ JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler = new JwtAuthenticationSuccessHandlerImpl(contextPath, jwtSecurityProps, jwtTokenHelper, objectMapper);
+ return jwtAuthenticationSuccessHandler;
+ }
+
+ @Bean
+ public JwtTokenAuthenticationFilter jwtAuthenticationTokenFilter() throws Exception {
+ return new JwtTokenAuthenticationFilter(jwtTokenHelper(), userDetailsService);
+ }
+
+ @Bean
+ public JwtTokenHelper jwtTokenHelper() {
+ return new JwtTokenHelper(APP_NAME, jwtSecurityProps);
+ }
+
+ @Bean
+ public LoginConfigurer loginConfigurer() {
+ if(BooleanUtils.isTrue(userpwdFilterEnabled))
+ return userpwdFilterLoginConfigurer();
+ if(BooleanUtils.isNotFalse(formLoginEnabled))
+ return formLoginConfigurer();
+ throw new RuntimeException("No login configurer enabled!");
+ }
+
+ @Bean
+ public LogoutSuccess logoutConfigurer() {
+ return new LogoutSuccess(objectMapper);
+ }
+
+ @Bean
+ public AuthenticationEntryPoint restAuthEntryPoint() {
+ return new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED);
+ }
+
+ @Bean
+ @Override
+ public UserDetailsService userDetailsServiceBean() throws Exception {
+ return super.userDetailsServiceBean();
+ }
+
+ @Bean
+ public LoginConfigurer userpwdFilterLoginConfigurer() {
+ LoginConfigurer loginConfigurer = new JwtUsernamePasswordFiterLoginConfig(jwtAuthenticationSuccessHandler());
+ return loginConfigurer;
+ }
+
+ // @Bean
+ // public PasswordEncoder passwordEncoder() {
+ // return new BCryptPasswordEncoder();
+ // }
}
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/properties/InMemoryAccountProperties.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/properties/InMemoryAccountProperties.java
new file mode 100644
index 0000000..a6905ba
--- /dev/null
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/properties/InMemoryAccountProperties.java
@@ -0,0 +1,24 @@
+package it.fabioformosa.quartzmanager.configuration.properties;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Configuration
+@ConfigurationProperties(prefix = "quartz-manager.accounts.in-memory")
+@Getter @Setter
+public class InMemoryAccountProperties {
+ private boolean enabled;
+ private List users;
+
+ @Getter @Setter
+ public static class User {
+ private String name;
+ private String password;
+ private List roles = new ArrayList<>();
+ }
+}
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/properties/JwtSecurityProperties.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/properties/JwtSecurityProperties.java
new file mode 100644
index 0000000..e33baba
--- /dev/null
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/properties/JwtSecurityProperties.java
@@ -0,0 +1,33 @@
+package it.fabioformosa.quartzmanager.configuration.properties;
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+
+@Configuration
+@ConfigurationProperties(prefix = "quartz-manager.security.jwt")
+@Getter @Setter
+public class JwtSecurityProperties {
+ private boolean enabled;
+ private String secret;
+ private long expirationInSec;
+
+ private CookieStrategy cookieStrategy;
+ private HeaderStrategy headerStrategy;
+
+ @Data
+ public static class CookieStrategy {
+ private boolean enabled;
+ private String cookie;
+ }
+
+ @Data
+ public static class HeaderStrategy {
+ private boolean enabled;
+ private String header;
+ }
+
+}
\ No newline at end of file
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AuthenticationController.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AuthenticationController.java
index 61f64a4..4b0d704 100644
--- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AuthenticationController.java
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AuthenticationController.java
@@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
-import it.fabioformosa.quartzmanager.security.TokenHelper;
+import it.fabioformosa.quartzmanager.security.helpers.impl.JwtTokenHelper;
import it.fabioformosa.quartzmanager.security.model.UserTokenState;
import it.fabioformosa.quartzmanager.security.service.impl.CustomUserDetailsService;
@@ -39,12 +39,12 @@ public class AuthenticationController {
private CustomUserDetailsService userDetailsService;
@Autowired
- TokenHelper tokenHelper;
+ JwtTokenHelper tokenHelper;
- @Value("${jwt.expires_in_sec}")
+ @Value("${quartz-manager.security.jwt.expiration-in-sec}")
private int EXPIRES_IN_SEC;
- @Value("${jwt.cookie}")
+ @Value("${quartz-manager.security.jwt.cookie-strategy-cookie}")
private String TOKEN_COOKIE;
@RequestMapping(value = "/changePassword", method = RequestMethod.POST)
@@ -59,7 +59,7 @@ public class AuthenticationController {
@RequestMapping(value = "/refresh", method = RequestMethod.GET)
public ResponseEntity> refreshAuthenticationToken(HttpServletRequest request, HttpServletResponse response) {
- String authToken = tokenHelper.getToken( request );
+ String authToken = tokenHelper.retrieveToken( request );
if (authToken != null && tokenHelper.canTokenBeRefreshed(authToken)) {
// TODO check user password last update
String refreshedToken = tokenHelper.refreshToken(authToken);
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AuthenticationSuccessHandler.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AuthenticationSuccessHandler.java
deleted file mode 100644
index 9f3bd77..0000000
--- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AuthenticationSuccessHandler.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package it.fabioformosa.quartzmanager.security.auth;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.userdetails.User;
-import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
-import org.springframework.stereotype.Component;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import it.fabioformosa.quartzmanager.security.TokenHelper;
-import it.fabioformosa.quartzmanager.security.model.UserTokenState;
-
-@Component
-public class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
-
- @Value("${jwt.expires_in_sec}")
- private int EXPIRES_IN_SEC;
-
- @Value("${jwt.cookie}")
- private String TOKEN_COOKIE;
-
- @Autowired
- TokenHelper tokenHelper;
- //
- @Autowired
- ObjectMapper objectMapper;
-
- @Override
- public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
- Authentication authentication ) throws IOException, ServletException {
- clearAuthenticationAttributes(request);
- User user = (User)authentication.getPrincipal();
-
- String jws = tokenHelper.generateToken( user.getUsername() );
-
- Cookie authCookie = new Cookie( TOKEN_COOKIE, jws );
-
- authCookie.setHttpOnly( true );
-
- authCookie.setMaxAge( EXPIRES_IN_SEC );
-
- authCookie.setPath( "/quartz-manager" );
- response.addCookie( authCookie );
-
- // JWT is also in the response
- UserTokenState userTokenState = new UserTokenState(jws, EXPIRES_IN_SEC);
- String jwtResponse = objectMapper.writeValueAsString( userTokenState );
- response.setContentType("application/json");
- response.getWriter().write( jwtResponse );
-
- }
-
- // @Override
- // public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
- // Authentication authentication ) throws IOException, ServletException {
- // // clearAuthenticationAttributes(request);
- // response.setContentType("application/json");
- // response.getWriter().write( objectMapper.writeValueAsString("OK"));
- // }
-}
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/LogoutSuccess.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/LogoutSuccess.java
deleted file mode 100644
index 82d2f17..0000000
--- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/LogoutSuccess.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package it.fabioformosa.quartzmanager.security.auth;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
-import org.springframework.stereotype.Component;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-@Component
-public class LogoutSuccess implements LogoutSuccessHandler {
-
- @Autowired
- ObjectMapper objectMapper;
-
- @Override
- public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse response, Authentication authentication)
- throws IOException, ServletException {
- Map result = new HashMap<>();
- result.put( "result", "success" );
- response.setContentType("application/json");
- response.getWriter().write( objectMapper.writeValueAsString( result ) );
- response.setStatus(HttpServletResponse.SC_OK);
-
- }
-
-}
\ No newline at end of file
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/TokenAuthenticationFilter.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/TokenAuthenticationFilter.java
deleted file mode 100644
index b0856ad..0000000
--- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/TokenAuthenticationFilter.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package it.fabioformosa.quartzmanager.security.auth;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.security.core.userdetails.UserDetailsService;
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
-import org.springframework.security.web.util.matcher.OrRequestMatcher;
-import org.springframework.security.web.util.matcher.RequestMatcher;
-import org.springframework.util.Assert;
-import org.springframework.web.filter.OncePerRequestFilter;
-
-import it.fabioformosa.quartzmanager.security.TokenHelper;
-import lombok.extern.slf4j.Slf4j;
-
-@Slf4j
-public class TokenAuthenticationFilter extends OncePerRequestFilter {
-
- public static final String ROOT_MATCHER = "/";
- public static final String FAVICON_MATCHER = "/favicon.ico";
- public static final String HTML_MATCHER = "/**/*.html";
- public static final String CSS_MATCHER = "/**/*.css";
- public static final String JS_MATCHER = "/**/*.js";
- public static final String IMG_MATCHER = "/images/*";
- public static final String LOGIN_MATCHER = "/auth/login";
- public static final String LOGOUT_MATCHER = "/auth/logout";
-
- // private final Log logger = LogFactory.getLog(this.getClass());
-
- @Autowired
- private TokenHelper tokenHelper;
-
- @Autowired
- private UserDetailsService userDetailsService;
-
- private List pathsToSkip = Arrays.asList(
- ROOT_MATCHER,
- HTML_MATCHER,
- FAVICON_MATCHER,
- CSS_MATCHER,
- JS_MATCHER,
- IMG_MATCHER,
- LOGIN_MATCHER,
- LOGOUT_MATCHER
- );
-
- @Override
- public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
-
- String authToken = tokenHelper.getToken(request);
- if (authToken != null && !skipPathRequest(request, pathsToSkip))
- try {
- String username = tokenHelper.getUsernameFromToken(authToken);
- UserDetails userDetails = userDetailsService.loadUserByUsername(username);
- // create authentication
- TokenBasedAuthentication authentication = new TokenBasedAuthentication(userDetails);
- authentication.setToken(authToken);
- SecurityContextHolder.getContext().setAuthentication(authentication);
- } catch (Exception e) {
- SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication());
- log.error("Switched to Anonimous Authentication, "
- + "because an error occurred setting authentication in security context holder due to " + e.getMessage(), e);
- }
- else
- SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication());
-
- chain.doFilter(request, response);
- }
-
- private boolean skipPathRequest(HttpServletRequest request, List pathsToSkip ) {
- Assert.notNull(pathsToSkip, "path cannot be null.");
- List m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList());
- OrRequestMatcher matchers = new OrRequestMatcher(m);
- return matchers.matches(request);
- }
-
-}
\ No newline at end of file
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/TokenBasedAuthentication.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/TokenBasedAuthentication.java
deleted file mode 100644
index dcf3d18..0000000
--- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/TokenBasedAuthentication.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package it.fabioformosa.quartzmanager.security.auth;
-
-import org.springframework.security.authentication.AbstractAuthenticationToken;
-import org.springframework.security.core.userdetails.UserDetails;
-
-
-public class TokenBasedAuthentication extends AbstractAuthenticationToken {
-
- private static final long serialVersionUID = 1L;
-
- private String token;
- private final UserDetails principle;
-
- public TokenBasedAuthentication( UserDetails principle ) {
- super( principle.getAuthorities() );
- this.principle = principle;
- }
-
- @Override
- public Object getCredentials() {
- return token;
- }
-
- @Override
- public UserDetails getPrincipal() {
- return principle;
- }
-
- public String getToken() {
- return token;
- }
-
- @Override
- public boolean isAuthenticated() {
- return true;
- }
-
- public void setToken( String token ) {
- this.token = token;
- }
-
-}
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/LoginConfigurer.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/LoginConfigurer.java
new file mode 100644
index 0000000..4446087
--- /dev/null
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/LoginConfigurer.java
@@ -0,0 +1,21 @@
+package it.fabioformosa.quartzmanager.security.helpers;
+
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+
+/**
+ * It configures filters to authenticate credentials sent by client or to set authenticationSuccessHandler
+ *
+ * Implement this interface for a login strategy
+ *
+ */
+public interface LoginConfigurer {
+
+ /**
+ * If the authentication is based on cookie, it returns the name of cookie to be erased at the logout
+ */
+ String cookieMustBeDeletedAtLogout();
+
+ HttpSecurity login(String loginPath, HttpSecurity http, AuthenticationManager authenticationManager) throws Exception;
+
+}
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/AjaxAuthenticationFilter.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AjaxAuthenticationFilter.java
similarity index 96%
rename from quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/AjaxAuthenticationFilter.java
rename to quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AjaxAuthenticationFilter.java
index 1cd4bd2..0c4e0b3 100644
--- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/AjaxAuthenticationFilter.java
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AjaxAuthenticationFilter.java
@@ -1,4 +1,4 @@
-package it.fabioformosa.quartzmanager.security;
+package it.fabioformosa.quartzmanager.security.helpers.impl;
import java.io.IOException;
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AnonAuthentication.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AnonAuthentication.java
similarity index 87%
rename from quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AnonAuthentication.java
rename to quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AnonAuthentication.java
index 19796d3..f1c266f 100644
--- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AnonAuthentication.java
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AnonAuthentication.java
@@ -1,4 +1,4 @@
-package it.fabioformosa.quartzmanager.security.auth;
+package it.fabioformosa.quartzmanager.security.helpers.impl;
import org.springframework.security.authentication.AbstractAuthenticationToken;
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AuthenticationFailureHandler.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AuthenticationFailureHandler.java
similarity index 52%
rename from quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AuthenticationFailureHandler.java
rename to quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AuthenticationFailureHandler.java
index a57f6e1..08bb8b4 100644
--- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AuthenticationFailureHandler.java
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AuthenticationFailureHandler.java
@@ -1,4 +1,4 @@
-package it.fabioformosa.quartzmanager.security.auth;
+package it.fabioformosa.quartzmanager.security.helpers.impl;
import java.io.IOException;
@@ -8,15 +8,13 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
-import org.springframework.stereotype.Component;
-@Component
public class AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
- @Override
- public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
- AuthenticationException exception) throws IOException, ServletException {
+ @Override
+ public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
+ AuthenticationException exception) throws IOException, ServletException {
- super.onAuthenticationFailure(request, response, exception);
- }
+ super.onAuthenticationFailure(request, response, exception);
+ }
}
\ No newline at end of file
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AuthenticationSuccessHandler.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AuthenticationSuccessHandler.java
new file mode 100644
index 0000000..6634870
--- /dev/null
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AuthenticationSuccessHandler.java
@@ -0,0 +1,35 @@
+package it.fabioformosa.quartzmanager.security.helpers.impl;
+
+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;
+
+//@Component
+//@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "form-login-enabled", havingValue = "true", matchIfMissing = true)
+public class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
+
+ private final JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler;
+
+ // @Autowired
+ public AuthenticationSuccessHandler(JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) {
+ super();
+ this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler;
+ }
+
+ public String cookieMustBeDeletedAtLogout() {
+ return jwtAuthenticationSuccessHandler.cookieMustBeDeletedAtLogout();
+ }
+
+ @Override
+ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
+ Authentication authentication ) throws IOException, ServletException {
+ clearAuthenticationAttributes(request);
+ jwtAuthenticationSuccessHandler.onLoginSuccess(authentication, response);
+ }
+
+}
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/ComboEntryPoint.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/ComboEntryPoint.java
similarity index 94%
rename from quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/ComboEntryPoint.java
rename to quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/ComboEntryPoint.java
index 2c05117..7ab933d 100644
--- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/ComboEntryPoint.java
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/ComboEntryPoint.java
@@ -1,4 +1,4 @@
-package it.fabioformosa.quartzmanager.security;
+package it.fabioformosa.quartzmanager.security.helpers.impl;
import java.io.IOException;
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/FormLoginConfig.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/FormLoginConfig.java
new file mode 100644
index 0000000..206f969
--- /dev/null
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/FormLoginConfig.java
@@ -0,0 +1,75 @@
+package it.fabioformosa.quartzmanager.security.helpers.impl;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
+
+import it.fabioformosa.quartzmanager.security.helpers.LoginConfigurer;
+
+/**
+ * It delegates form to @FormLoginConfigurer of the httpSecurity.
+ *
+ */
+public class FormLoginConfig implements LoginConfigurer {
+
+ private static final Logger log = LoggerFactory.getLogger(FormLoginConfig.class);
+
+ private final AuthenticationSuccessHandler authenticationSuccessHandler;
+
+ private final AuthenticationFailureHandler authenticationFailureHandler;
+
+
+ public FormLoginConfig() {
+ super();
+ authenticationSuccessHandler = null;
+ authenticationFailureHandler = null;
+ }
+
+ public FormLoginConfig(AuthenticationFailureHandler authenticationFailureHandler) {
+ super();
+ authenticationSuccessHandler = null;
+ this.authenticationFailureHandler = authenticationFailureHandler;
+ }
+
+ public FormLoginConfig(AuthenticationSuccessHandler authenticationSuccessHandler) {
+ super();
+ this.authenticationSuccessHandler = authenticationSuccessHandler;
+ authenticationFailureHandler = null;
+ }
+
+ public FormLoginConfig(AuthenticationSuccessHandler authenticationSuccessHandler,
+ AuthenticationFailureHandler authenticationFailureHandler) {
+ super();
+ this.authenticationSuccessHandler = authenticationSuccessHandler;
+ this.authenticationFailureHandler = authenticationFailureHandler;
+ }
+
+ @Override
+ public String cookieMustBeDeletedAtLogout() {
+ return authenticationSuccessHandler.cookieMustBeDeletedAtLogout();
+ }
+
+ @Override
+ public HttpSecurity login(String loginPath,
+ HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
+ log.debug("Configuring login through FormLoginConfigurer...");
+
+ FormLoginConfigurer login = http.formLogin().loginPage(loginPath);
+
+ if(authenticationSuccessHandler != null) {
+ log.debug("Setting an authenticationSuccessHandler");
+ login = login.successHandler(authenticationSuccessHandler);
+ }
+
+ if(authenticationFailureHandler != null) {
+ log.debug("Setting an authenticationFailureHandler");
+ login = login.failureHandler(authenticationFailureHandler);
+ }
+
+ HttpSecurity httpSecurity = login.and();
+ return httpSecurity;
+ }
+
+}
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationFilter.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationFilter.java
new file mode 100644
index 0000000..c15c0ac
--- /dev/null
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationFilter.java
@@ -0,0 +1,34 @@
+package it.fabioformosa.quartzmanager.security.helpers.impl;
+
+import javax.servlet.FilterChain;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+import lombok.SneakyThrows;
+
+/**
+ * It extends the @UsernamePasswordAuthenticationFilter and it overrides the successfulAuthentication method to put jwtToken in the response
+ *
+ */
+public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
+
+ private JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler;
+
+ public JwtAuthenticationFilter(AuthenticationManager authenticationManager, JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) {
+ this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler;
+ setAuthenticationManager(authenticationManager);
+ }
+
+ @SneakyThrows
+ @Override
+ protected void successfulAuthentication(HttpServletRequest req,
+ HttpServletResponse res,
+ FilterChain chain,
+ Authentication auth) {
+ jwtAuthenticationSuccessHandler.onLoginSuccess(auth, res);
+ }
+}
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationSuccessHandler.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationSuccessHandler.java
new file mode 100644
index 0000000..6a956c9
--- /dev/null
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationSuccessHandler.java
@@ -0,0 +1,14 @@
+package it.fabioformosa.quartzmanager.security.helpers.impl;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.core.Authentication;
+
+public interface JwtAuthenticationSuccessHandler {
+
+ String cookieMustBeDeletedAtLogout();
+
+ void onLoginSuccess(Authentication authentication, HttpServletResponse response) throws IOException;
+}
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationSuccessHandlerImpl.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationSuccessHandlerImpl.java
new file mode 100644
index 0000000..fee0ede
--- /dev/null
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationSuccessHandlerImpl.java
@@ -0,0 +1,78 @@
+package it.fabioformosa.quartzmanager.security.helpers.impl;
+
+import java.io.IOException;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.userdetails.User;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import it.fabioformosa.quartzmanager.configuration.properties.JwtSecurityProperties;
+import it.fabioformosa.quartzmanager.security.model.UserTokenState;
+
+/**
+ * It depends on @JwtTokenHelper to generate the jwtToken.
+ * On login success, it generates the jwtToken and it returns it to the login according to possible strategies: cookie, response header.
+ * You can choice the strategy through @JwtSecurityProperties
+ *
+ */
+public class JwtAuthenticationSuccessHandlerImpl implements JwtAuthenticationSuccessHandler {
+
+ private static final Logger log = LoggerFactory.getLogger(JwtAuthenticationSuccessHandlerImpl.class);
+
+ private final JwtSecurityProperties jwtSecurityProps;
+
+ private final JwtTokenHelper jwtTokenHelper;
+
+ private final ObjectMapper objectMapper;
+
+ private final String contextPath;
+
+ @Autowired
+ public JwtAuthenticationSuccessHandlerImpl(String contextPath, JwtSecurityProperties jwtSecurityProps, JwtTokenHelper jwtTokenHelper, ObjectMapper objectMapper) {
+ this.contextPath = contextPath;
+ this.jwtSecurityProps = jwtSecurityProps;
+ this.jwtTokenHelper = jwtTokenHelper;
+ this.objectMapper = objectMapper;
+ }
+
+ @Override
+ public String cookieMustBeDeletedAtLogout() {
+ if(!jwtSecurityProps.getCookieStrategy().isEnabled())
+ return null;
+ return jwtSecurityProps.getCookieStrategy().getCookie();
+ }
+
+ @Override
+ public void onLoginSuccess(Authentication authentication, HttpServletResponse response) throws IOException {
+ log.debug("Login successed, generating jwtToken...");
+
+ User user = (User) authentication.getPrincipal();
+ String jwtToken = jwtTokenHelper.generateToken(user.getUsername());
+
+ if(jwtSecurityProps.getCookieStrategy().isEnabled()) {
+ Cookie authCookie = new Cookie(jwtSecurityProps.getCookieStrategy().getCookie(), jwtToken);
+ authCookie.setHttpOnly(true);
+ authCookie.setMaxAge((int) jwtSecurityProps.getExpirationInSec());
+ authCookie.setPath(contextPath);
+ response.addCookie(authCookie);
+ log.debug("Set jwtToken into the cookie {}", jwtSecurityProps.getCookieStrategy().getCookie());
+ }
+
+ if(jwtSecurityProps.getHeaderStrategy().isEnabled()) {
+ jwtTokenHelper.setHeader(response, jwtToken);
+ log.debug("Set jwtToken into the response header {}", jwtSecurityProps.getHeaderStrategy().getHeader());
+ }
+
+ UserTokenState userTokenState = new UserTokenState(jwtToken, jwtSecurityProps.getExpirationInSec());
+ String jwtResponse = objectMapper.writeValueAsString(userTokenState);
+ response.setContentType("application/json");
+ response.getWriter().write(jwtResponse);
+ }
+}
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenAuthenticationFilter.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenAuthenticationFilter.java
new file mode 100644
index 0000000..346bb2f
--- /dev/null
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenAuthenticationFilter.java
@@ -0,0 +1,105 @@
+package it.fabioformosa.quartzmanager.security.helpers.impl;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.OrRequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+
+/**
+ * It finds the jwtToken into the request, it validates it and sets an @Authentication into the @SecurityContextHolder.
+ * If the request has a path included into the paths that must be skipped, it sets an anonymous authentication
+ *
+ * It delegates the jwtToken retrieve to the @JwtTokenHelper that applies several strategies.
+ *
+ */
+public class JwtTokenAuthenticationFilter extends OncePerRequestFilter {
+
+ private static final Logger log = LoggerFactory.getLogger(JwtTokenAuthenticationFilter.class);
+
+ private static final String ROOT_MATCHER = "/";
+ private static final String FAVICON_MATCHER = "/favicon.ico";
+ private static final String HTML_MATCHER = "/**/*.html";
+ private static final String CSS_MATCHER = "/**/*.css";
+ private static final String JS_MATCHER = "/**/*.js";
+ private static final String IMG_MATCHER = "/images/*";
+ private static final String LOGIN_MATCHER = "/api/login";
+ private static final String LOGOUT_MATCHER = "/api/logout";
+
+ private static List PATH_TO_SKIP = Arrays.asList(
+ ROOT_MATCHER,
+ HTML_MATCHER,
+ FAVICON_MATCHER,
+ CSS_MATCHER,
+ JS_MATCHER,
+ IMG_MATCHER,
+ LOGIN_MATCHER,
+ LOGOUT_MATCHER
+ );
+
+ private final JwtTokenHelper jwtTokenHelper;
+ private final UserDetailsService userDetailsService;
+
+
+ public JwtTokenAuthenticationFilter(JwtTokenHelper jwtTokenHelper, UserDetailsService userDetailsService) {
+ super();
+ this.jwtTokenHelper = jwtTokenHelper;
+ this.userDetailsService = userDetailsService;
+ }
+
+ @Override
+ public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
+
+ String jwtToken = jwtTokenHelper.retrieveToken(request);
+ if (jwtToken != null) {
+ log.debug("Found a jwtToken into the request {}", request.getPathInfo());
+ try {
+ String username = jwtTokenHelper.getUsernameFromToken(jwtToken);
+ UserDetails userDetails = userDetailsService.loadUserByUsername(username);
+
+ JwtTokenBasedAuthentication authentication = new JwtTokenBasedAuthentication(userDetails);
+ authentication.setToken(jwtToken);
+
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ } catch (Exception e) {
+ log.error("Authentication failed! an expected error occurred authenticating the request {}", request.getRequestURL());
+ // SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication());
+ // log.error("Switched to Anonymous Authentication, "
+ // + "because an error occurred setting authentication in security context holder due to " + e.getMessage(), e);
+ }
+ }
+ else if(skipPathRequest(request, PATH_TO_SKIP)) {
+ log.debug("Detected a path to be skipped from authentication, so activated anonymous auth for {}", request.getRequestURL());
+ SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication());
+ }
+ else
+ log.debug("Not found any jwtToken and the request hasn't a path to be skipped from auth. Path: {}", request.getRequestURL());
+
+ chain.doFilter(request, response);
+ }
+
+ private boolean skipPathRequest(HttpServletRequest request, List pathsToSkip ) {
+ if(pathsToSkip == null)
+ pathsToSkip = new ArrayList();
+ List matchers = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList());
+ OrRequestMatcher compositeMatchers = new OrRequestMatcher(matchers);
+ return compositeMatchers.matches(request);
+ }
+
+}
\ No newline at end of file
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenBasedAuthentication.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenBasedAuthentication.java
new file mode 100644
index 0000000..904ed07
--- /dev/null
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenBasedAuthentication.java
@@ -0,0 +1,42 @@
+package it.fabioformosa.quartzmanager.security.helpers.impl;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.userdetails.UserDetails;
+
+
+public class JwtTokenBasedAuthentication extends AbstractAuthenticationToken {
+
+ private static final long serialVersionUID = 1L;
+
+ private String token;
+ private final UserDetails principle;
+
+ public JwtTokenBasedAuthentication(UserDetails principle) {
+ super(principle.getAuthorities());
+ this.principle = principle;
+ }
+
+ @Override
+ public Object getCredentials() {
+ return token;
+ }
+
+ @Override
+ public UserDetails getPrincipal() {
+ return principle;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ @Override
+ public boolean isAuthenticated() {
+ return true;
+ }
+
+ public void setToken( String token ) {
+ this.token = token;
+ }
+
+}
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/TokenHelper.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenHelper.java
similarity index 50%
rename from quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/TokenHelper.java
rename to quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenHelper.java
index 6576838..b7f24d7 100644
--- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/TokenHelper.java
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenHelper.java
@@ -1,51 +1,52 @@
-package it.fabioformosa.quartzmanager.security;
+package it.fabioformosa.quartzmanager.security.helpers.impl;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import org.joda.time.DateTime;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
-import lombok.extern.slf4j.Slf4j;
+import it.fabioformosa.quartzmanager.configuration.properties.JwtSecurityProperties;
/**
- * JWT Temporary disabled
*
* @author Fabio.Formosa
*
*/
-@Slf4j
-@Component
-public class TokenHelper {
+public class JwtTokenHelper {
- @Value("${app.name}")
- private String APP_NAME;
+ private static final Logger log = LoggerFactory.getLogger(JwtTokenHelper.class);
- @Value("${jwt.secret}")
- private String SECRET;
+ private static String base64EncodeSecretKey(String secretKey) {
+ return Base64.getEncoder().encodeToString(secretKey.getBytes(StandardCharsets.UTF_8));
+ }
- @Value("${jwt.expires_in_sec}")
- private int EXPIRES_IN_SEC;
+ // @Value("${app.name}")
+ private final String appName;
- @Value("${jwt.header}")
- private String AUTH_HEADER;
-
- @Value("${jwt.cookie}")
- private String AUTH_COOKIE;
-
- // @Autowired
- // UserDetailsService userDetailsService;
+ // @Autowired
+ private final JwtSecurityProperties jwtSecurityProps;
private SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS512;
+ // @Autowired
+ public JwtTokenHelper(String appName, JwtSecurityProperties jwtSecurityProps) {
+ super();
+ this.appName = appName;
+ this.jwtSecurityProps = jwtSecurityProps;
+ }
+
public Boolean canTokenBeRefreshed(String token) {
try {
final Date expirationDate = getClaimsFromToken(token).getExpiration();
@@ -62,34 +63,25 @@ public class TokenHelper {
}
private Date generateExpirationDate() {
- return new Date(getCurrentTimeMillis() + EXPIRES_IN_SEC * 1000);
+ return new Date(getCurrentTimeMillis() + jwtSecurityProps.getExpirationInSec() * 1000);
}
- String generateToken(Map claims) {
- return Jwts.builder()
- .setClaims(claims)
- .setExpiration(generateExpirationDate())
- .signWith( SIGNATURE_ALGORITHM, SECRET )
- .compact();
+ private String generateToken(Map claims) {
+ return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate())
+ .signWith(SIGNATURE_ALGORITHM, base64EncodeSecretKey(jwtSecurityProps.getSecret())).compact();
}
public String generateToken(String username) {
- return Jwts.builder()
- .setIssuer(APP_NAME)
- .setSubject(username)
- .setIssuedAt(generateCurrentDate())
+ return Jwts.builder().setIssuer(appName).setSubject(username).setIssuedAt(generateCurrentDate())
.setExpiration(generateExpirationDate())
- .signWith(SIGNATURE_ALGORITHM, SECRET)
- .compact();
+ .signWith(SIGNATURE_ALGORITHM, base64EncodeSecretKey(jwtSecurityProps.getSecret())).compact();
}
private Claims getClaimsFromToken(String token) {
Claims claims;
try {
- claims = Jwts.parser()
- .setSigningKey(SECRET)
- .parseClaimsJws(token)
- .getBody();
+ claims = Jwts.parser().setSigningKey(base64EncodeSecretKey(jwtSecurityProps.getSecret()))
+ .parseClaimsJws(token).getBody();
} catch (Exception e) {
claims = null;
log.error("Error getting claims from jwt token due to " + e.getMessage(), e);
@@ -101,9 +93,9 @@ public class TokenHelper {
* Find a specific HTTP cookie in a request.
*
* @param request
- * The HTTP request object.
+ * The HTTP request object.
* @param name
- * The cookie name to look for.
+ * The cookie name to look for.
* @return The cookie, or null if not found.
*/
public Cookie getCookieValueByName(HttpServletRequest request, String name) {
@@ -119,18 +111,6 @@ public class TokenHelper {
return DateTime.now().getMillis();
}
- public String getToken( HttpServletRequest request ) {
- Cookie authCookie = getCookieValueByName( request, AUTH_COOKIE );
- if ( authCookie != null )
- return authCookie.getValue();
-
- String authHeader = request.getHeader(AUTH_HEADER);
- if ( authHeader != null && authHeader.startsWith("Bearer "))
- return authHeader.substring(7);
-
- return null;
- }
-
public String getUsernameFromToken(String token) {
String username;
try {
@@ -139,6 +119,7 @@ public class TokenHelper {
} catch (Exception e) {
username = null;
log.error("Error getting claims from jwt token due to " + e.getMessage(), e);
+ throw e;
}
return username;
}
@@ -155,4 +136,27 @@ public class TokenHelper {
}
return refreshedToken;
}
+
+ public String retrieveToken(HttpServletRequest request) {
+ if (jwtSecurityProps.getCookieStrategy().isEnabled() == true) {
+ Cookie authCookie = getCookieValueByName(request, jwtSecurityProps.getCookieStrategy().getCookie());
+ if (authCookie != null)
+ return authCookie.getValue();
+ }
+
+ if (jwtSecurityProps.getHeaderStrategy().isEnabled()) {
+ String authHeader = request.getHeader(jwtSecurityProps.getHeaderStrategy().getHeader());
+ if (authHeader != null && authHeader.startsWith("Bearer "))
+ return authHeader.substring(7);
+ }
+
+ if(request.getParameter("access_token") != null)
+ return request.getParameter("access_token");
+
+ return null;
+ }
+
+ public void setHeader(HttpServletResponse response, String token) {
+ response.addHeader(jwtSecurityProps.getHeaderStrategy().getHeader(), "Bearer " + token);
+ }
}
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtUsernamePasswordFiterLoginConfig.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtUsernamePasswordFiterLoginConfig.java
new file mode 100644
index 0000000..6f6991c
--- /dev/null
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtUsernamePasswordFiterLoginConfig.java
@@ -0,0 +1,46 @@
+package it.fabioformosa.quartzmanager.security.helpers.impl;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
+import org.springframework.security.web.util.matcher.RegexRequestMatcher;
+import org.springframework.web.filter.GenericFilterBean;
+
+import it.fabioformosa.quartzmanager.security.helpers.LoginConfigurer;
+
+/**
+ * It adds a new filter @JwtAuthenticationFilter after @AbstractPreAuthenticatedProcessingFilter that match login path
+ *
+ */
+public class JwtUsernamePasswordFiterLoginConfig implements LoginConfigurer {
+
+ private static final Logger log = LoggerFactory.getLogger(JwtUsernamePasswordFiterLoginConfig.class);
+
+ private final JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler;
+
+ public JwtUsernamePasswordFiterLoginConfig(JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) {
+ super();
+ this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler;
+ }
+
+ public GenericFilterBean authenticationProcessingFilter(String loginPath, AuthenticationManager authenticationManager) throws Exception {
+ JwtAuthenticationFilter authenticationProcessingFilter = new JwtAuthenticationFilter(authenticationManager, jwtAuthenticationSuccessHandler);
+ authenticationProcessingFilter.setRequiresAuthenticationRequestMatcher(new RegexRequestMatcher(loginPath, HttpMethod.POST.name(), false));
+ return authenticationProcessingFilter;
+ }
+
+ @Override
+ public String cookieMustBeDeletedAtLogout() {
+ return jwtAuthenticationSuccessHandler.cookieMustBeDeletedAtLogout();
+ }
+
+ @Override
+ public HttpSecurity login(String loginPath, HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
+ log.debug("Configuring login through JwtAuthenticationFilter...");
+ return http.addFilterAfter(authenticationProcessingFilter(loginPath, authenticationManager), AbstractPreAuthenticatedProcessingFilter.class);
+ }
+
+}
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/LogoutSuccess.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/LogoutSuccess.java
new file mode 100644
index 0000000..5f0eaa0
--- /dev/null
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/LogoutSuccess.java
@@ -0,0 +1,36 @@
+package it.fabioformosa.quartzmanager.security.helpers.impl;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+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.logout.LogoutSuccessHandler;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class LogoutSuccess implements LogoutSuccessHandler {
+
+ private final ObjectMapper objectMapper;
+
+ public LogoutSuccess(ObjectMapper objectMapper) {
+ super();
+ this.objectMapper = objectMapper;
+ }
+
+ @Override
+ public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse response, Authentication authentication)
+ throws IOException, ServletException {
+ Map result = new HashMap<>();
+ result.put( "result", "success" );
+ response.setContentType("application/json");
+ response.getWriter().write(objectMapper.writeValueAsString(result));
+ response.setStatus(HttpServletResponse.SC_OK);
+
+ }
+
+}
\ No newline at end of file
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/QuartzManagerHttpSecurity.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/QuartzManagerHttpSecurity.java
new file mode 100644
index 0000000..55b9edb
--- /dev/null
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/QuartzManagerHttpSecurity.java
@@ -0,0 +1,57 @@
+package it.fabioformosa.quartzmanager.security.helpers.impl;
+
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
+import org.springframework.security.web.DefaultSecurityFilterChain;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+
+import it.fabioformosa.quartzmanager.security.helpers.LoginConfigurer;
+
+/**
+ * It wraps the httpSecurity to provide new function as login and logout
+ *
+ */
+public class QuartzManagerHttpSecurity extends SecurityConfigurerAdapter {
+
+ public static QuartzManagerHttpSecurity from(HttpSecurity httpSecurity){
+ QuartzManagerHttpSecurity newInstance = new QuartzManagerHttpSecurity(httpSecurity);
+ newInstance.setBuilder(httpSecurity);
+ return newInstance;
+ }
+
+ private HttpSecurity httpSecurity;
+
+ private LoginConfigurer loginConfigurer;
+
+ private LogoutSuccess logoutSuccess;
+
+ public QuartzManagerHttpSecurity(HttpSecurity httpSecurity) {
+ this.httpSecurity = httpSecurity;
+ // applicationContext = httpSecurity.getSharedObject(ApplicationContext.class);
+ }
+
+ public QuartzManagerHttpSecurity login(String loginPath, AuthenticationManager authenticationManager) throws Exception {
+ if(loginConfigurer == null || logoutSuccess == null)
+ throw new IllegalStateException("QuartzManagerHttpSecurity requires to be set loginConfigurer and logoutSuccess!");
+ httpSecurity = loginConfigurer.login(loginPath, httpSecurity, authenticationManager);
+ return this;
+ }
+
+
+ public LogoutConfigurer logout(String logoutPath) throws Exception {
+ LogoutConfigurer logoutConfigurer = httpSecurity.logout().logoutRequestMatcher(new AntPathRequestMatcher(logoutPath))
+ .logoutSuccessHandler(logoutSuccess);
+ String cookie = loginConfigurer.cookieMustBeDeletedAtLogout();
+ if(cookie != null)
+ logoutConfigurer.deleteCookies(cookie);
+ return logoutConfigurer;
+ }
+
+ public QuartzManagerHttpSecurity withLoginConfigurer(LoginConfigurer loginConfigurer, LogoutSuccess logoutSuccess) {
+ this.loginConfigurer = loginConfigurer;
+ this.logoutSuccess = logoutSuccess;
+ return this;
+ }
+}
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/RESTRequestMatcher.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/RESTRequestMatcher.java
similarity index 94%
rename from quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/RESTRequestMatcher.java
rename to quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/RESTRequestMatcher.java
index 899d5ad..1fb2d2b 100644
--- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/RESTRequestMatcher.java
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/RESTRequestMatcher.java
@@ -1,4 +1,4 @@
-package it.fabioformosa.quartzmanager.security;
+package it.fabioformosa.quartzmanager.security.helpers.impl;
import javax.servlet.http.HttpServletRequest;
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/RestAuthenticationEntryPoint.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/RestAuthenticationEntryPoint.java
similarity index 88%
rename from quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/RestAuthenticationEntryPoint.java
rename to quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/RestAuthenticationEntryPoint.java
index 1d1c4be..3615a2e 100644
--- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/RestAuthenticationEntryPoint.java
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/RestAuthenticationEntryPoint.java
@@ -1,4 +1,4 @@
-package it.fabioformosa.quartzmanager.security.auth;
+package it.fabioformosa.quartzmanager.security.helpers.impl;
import java.io.IOException;
diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/WebsocketRequestMatcher.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/WebsocketRequestMatcher.java
similarity index 89%
rename from quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/WebsocketRequestMatcher.java
rename to quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/WebsocketRequestMatcher.java
index 28f64db..5b42424 100644
--- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/WebsocketRequestMatcher.java
+++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/WebsocketRequestMatcher.java
@@ -1,4 +1,4 @@
-package it.fabioformosa.quartzmanager.security;
+package it.fabioformosa.quartzmanager.security.helpers.impl;
import javax.servlet.http.HttpServletRequest;
diff --git a/quartz-manager-api/src/main/resources/application.yml b/quartz-manager-api/src/main/resources/application.yml
index c4c71ce..f69a691 100644
--- a/quartz-manager-api/src/main/resources/application.yml
+++ b/quartz-manager-api/src/main/resources/application.yml
@@ -11,6 +11,7 @@ spring:
thymeleaf:
cache: false
mode: LEGACYHTML5
+ jpa.open-in-view: false
quartz:
enabled: true
@@ -19,12 +20,6 @@ job:
frequency: 4000
repeatCount: 19
-jwt:
- header: Authorization
- expires_in_sec: 600 # 10 minutes
- secret: queenvictoria
- cookie: AUTH-TOKEN
-
logging:
level:
org.springframework.web: WARN
@@ -33,8 +28,26 @@ logging:
it.fabioformosa: DEBUG
quartz-manager:
+ security:
+ login-model:
+ form-login-enabled: true
+ userpwd-filter-enabled : false
+ jwt:
+ enabled: true
+ secret: "bibidibobidiboo"
+ expiration-in-sec: 28800 # 8 hours
+ header-strategy:
+ enabled: false
+ header: "Authorization"
+ cookie-strategy:
+ enabled: true
+ cookie: AUTH-TOKEN
jobClass: it.fabioformosa.quartzmanager.jobs.myjobs.SampleJob
- account:
- user: admin
- pwd: admin
-
\ No newline at end of file
+ accounts:
+ in-memory:
+ enabled: true
+ users:
+ - name: admin
+ password: admin
+ roles:
+ - ADMIN
diff --git a/quartz-manager-api/src/main/resources/logback.xml b/quartz-manager-api/src/main/resources/logback.xml
index 9977078..90577b1 100644
--- a/quartz-manager-api/src/main/resources/logback.xml
+++ b/quartz-manager-api/src/main/resources/logback.xml
@@ -4,7 +4,7 @@
- %d{yyyy-MM-dd HH:mm:ss.SSS} [%.10thread] %-5level [%-40.40logger{49}:%-3L] --- %m%n
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%.11thread] %-5level [%-40.40logger{49}:%-3L] --- %m%n
diff --git a/quartz-manager-frontend/angular.json b/quartz-manager-frontend/angular.json
index 7cd6f33..563894f 100644
--- a/quartz-manager-frontend/angular.json
+++ b/quartz-manager-frontend/angular.json
@@ -11,6 +11,7 @@
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
+ "aot": true,
"outputPath": "../server/src/main/resources/static",
"index": "src/index.html",
"main": "src/main.ts",
@@ -27,6 +28,12 @@
},
"configurations": {
"production": {
+ "budgets": [
+ {
+ "type": "anyComponentStyle",
+ "maximumWarning": "6kb"
+ }
+ ],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
@@ -119,7 +126,7 @@
"schematics": {
"@schematics/angular:component": {
"prefix": "app",
- "styleext": "css"
+ "style": "css"
},
"@schematics/angular:directive": {
"prefix": "app"
diff --git a/quartz-manager-frontend/browserslist b/quartz-manager-frontend/browserslist
new file mode 100644
index 0000000..8084853
--- /dev/null
+++ b/quartz-manager-frontend/browserslist
@@ -0,0 +1,12 @@
+# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
+# For additional information regarding the format and rule options, please see:
+# https://github.com/browserslist/browserslist#queries
+
+# You can see what browsers were selected by your queries by running:
+# npx browserslist
+
+> 0.5%
+last 2 versions
+Firefox ESR
+not dead
+not IE 9-11 # For IE 9-11 support, remove 'not'.
\ No newline at end of file
diff --git a/quartz-manager-frontend/package.json b/quartz-manager-frontend/package.json
index 47d93ab..d442d69 100644
--- a/quartz-manager-frontend/package.json
+++ b/quartz-manager-frontend/package.json
@@ -12,19 +12,19 @@
},
"private": true,
"dependencies": {
- "@angular/animations": "7.2.13",
- "@angular/cdk": "7.3.7",
- "@angular/common": "7.2.13",
- "@angular/compiler": "7.2.13",
- "@angular/core": "7.2.13",
- "@angular/flex-layout": "7.0.0-beta.24",
- "@angular/forms": "7.2.13",
- "@angular/http": "7.2.13",
- "@angular/material": "7.3.7",
- "@angular/platform-browser": "7.2.13",
- "@angular/platform-browser-dynamic": "7.2.13",
- "@angular/platform-server": "7.2.13",
- "@angular/router": "7.2.13",
+ "@angular/animations": "9.1.4",
+ "@angular/cdk": "9.2.1",
+ "@angular/common": "9.1.4",
+ "@angular/compiler": "9.1.4",
+ "@angular/core": "9.1.4",
+ "@angular/flex-layout": "9.0.0-beta.29",
+ "@angular/forms": "9.1.4",
+ "@angular/material": "9.2.1",
+ "@angular/platform-browser": "9.1.4",
+ "@angular/platform-browser-dynamic": "9.1.4",
+ "@angular/platform-server": "9.1.4",
+ "@angular/router": "9.1.4",
+ "@auth0/angular-jwt": "^4.0.0",
"@fortawesome/fontawesome": "^1.1.4",
"@fortawesome/fontawesome-free-regular": "^5.0.8",
"@fortawesome/fontawesome-free-solid": "^5.0.8",
@@ -32,22 +32,22 @@
"core-js": "2.5.1",
"hammerjs": "2.0.8",
"net": "^1.0.2",
- "rxjs": "6.4.0",
+ "rxjs": "6.5.5",
"stompjs": "^2.3.3",
- "tslib": "^1.9.0",
- "zone.js": "0.8.18"
+ "tslib": "^1.10.0",
+ "zone.js": "~0.10.2"
},
"devDependencies": {
- "@angular-devkit/build-angular": "~0.13.0",
- "@angular-devkit/core": "^0.2.0",
- "@angular/cli": "7.3.7",
- "@angular/compiler-cli": "7.2.13",
- "@angular/language-service": "7.2.13",
+ "@angular-devkit/build-angular": "~0.901.4",
+ "@angular-devkit/core": "^9.1.4",
+ "@angular/cli": "9.1.4",
+ "@angular/compiler-cli": "9.1.4",
+ "@angular/language-service": "9.1.4",
"@types/hammerjs": "2.0.34",
"@types/jasmine": "2.5.54",
"@types/jasminewd2": "2.0.3",
- "@types/node": "6.0.90",
- "codelyzer": "4.2.1",
+ "@types/node": "^12.11.1",
+ "codelyzer": "^5.1.2",
"jasmine-core": "2.6.4",
"jasmine-spec-reporter": "4.1.1",
"karma": "1.7.1",
@@ -59,6 +59,6 @@
"protractor": "5.1.2",
"ts-node": "3.0.6",
"tslint": "5.7.0",
- "typescript": "3.2.4"
+ "typescript": "3.8.3"
}
}
diff --git a/quartz-manager-frontend/proxy.conf.json b/quartz-manager-frontend/proxy.conf.json
index df22791..5a75f53 100644
--- a/quartz-manager-frontend/proxy.conf.json
+++ b/quartz-manager-frontend/proxy.conf.json
@@ -2,6 +2,7 @@
"/quartz-manager": {
"target": "http://localhost:8080",
"secure": false,
+ "cookiePathRewrite": "/",
"ws":true
}
}
diff --git a/quartz-manager-frontend/src/app/app.module.ts b/quartz-manager-frontend/src/app/app.module.ts
index 72f2901..90e0d34 100644
--- a/quartz-manager-frontend/src/app/app.module.ts
+++ b/quartz-manager-frontend/src/app/app.module.ts
@@ -1,22 +1,23 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER} from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
-import { HttpModule } from '@angular/http';
import { HttpClientModule } from '@angular/common/http';
+
+import {JWT_OPTIONS, JwtModule} from "@auth0/angular-jwt";
+
// material
-import {
- MatButtonModule,
- MatMenuModule,
- MatIconModule,
- MatToolbarModule,
- MatTooltipModule,
- MatCardModule,
- MatChipsModule,
- MatInputModule,
- MatIconRegistry,
- MatProgressSpinnerModule,
- MatProgressBarModule,
-} from '@angular/material';
+import {MatIconRegistry} from '@angular/material/icon';
+import {MatInputModule} from '@angular/material/input';
+import {MatChipsModule} from '@angular/material/chips';
+import {MatTooltipModule} from '@angular/material/tooltip';
+import {MatProgressBarModule} from '@angular/material/progress-bar';
+import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
+import {MatMenuModule} from '@angular/material/menu';
+import {MatToolbarModule} from '@angular/material/toolbar';
+import {MatIconModule} from '@angular/material/icon';
+import {MatButtonModule} from '@angular/material/button';
+import {MatCardModule} from '@angular/material/card';
+
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FlexLayoutModule } from '@angular/flex-layout';
import { AppComponent } from './app.component';
@@ -77,6 +78,15 @@ export function initUserFactory(userService: UserService) {
// debug: true
// };
+export function jwtOptionsFactory(apiService: ApiService) {
+ return {
+ tokenGetter: () => {
+ return apiService.getToken();
+ },
+ whitelistedDomains: ['localhost:8080', 'localhost:4200']
+ }
+}
+
@NgModule({
declarations: [
AppComponent,
@@ -99,9 +109,15 @@ export function initUserFactory(userService: UserService) {
BrowserModule,
FormsModule,
ReactiveFormsModule,
- HttpModule,
HttpClientModule,
AppRoutingModule,
+ JwtModule.forRoot({
+ jwtOptionsProvider: {
+ provide: JWT_OPTIONS,
+ useFactory: jwtOptionsFactory,
+ deps: [ApiService]
+ }
+ }),
MatMenuModule,
MatTooltipModule,
MatButtonModule,
diff --git a/quartz-manager-frontend/src/app/components/header/header.component.scss b/quartz-manager-frontend/src/app/components/header/header.component.scss
index 122b0bd..eb2bb4c 100644
--- a/quartz-manager-frontend/src/app/components/header/header.component.scss
+++ b/quartz-manager-frontend/src/app/components/header/header.component.scss
@@ -32,7 +32,7 @@
}
-/deep/ {
+::ng-deep {
.app-header-accountMenu.mat-menu-panel {
border-radius: 3px;
max-width: initial;
diff --git a/quartz-manager-frontend/src/app/model/SocketOption.model.ts b/quartz-manager-frontend/src/app/model/SocketOption.model.ts
index 20d8fe3..529da9d 100644
--- a/quartz-manager-frontend/src/app/model/SocketOption.model.ts
+++ b/quartz-manager-frontend/src/app/model/SocketOption.model.ts
@@ -4,11 +4,14 @@ export class SocketOption{
brokerName : string;
reconnectionTimeout : number = 30000
- constructor(socketUrl : string, topicName : string, brokerName : string = null, reconnectionTimeout : number = 30000){
+ getAccessToken: Function = () => null;
+
+ constructor(socketUrl : string, topicName : string, getAccessToken?: Function, brokerName : string = null, reconnectionTimeout : number = 30000){
this.socketUrl = socketUrl;
this.topicName = topicName;
this.brokerName = brokerName;
this.reconnectionTimeout = reconnectionTimeout;
+ this.getAccessToken = getAccessToken || (() => null);
}
}
\ No newline at end of file
diff --git a/quartz-manager-frontend/src/app/services/api.service.ts b/quartz-manager-frontend/src/app/services/api.service.ts
index 0fc7d7b..bf84c6f 100644
--- a/quartz-manager-frontend/src/app/services/api.service.ts
+++ b/quartz-manager-frontend/src/app/services/api.service.ts
@@ -1,7 +1,7 @@
import { HttpClient, HttpHeaders, HttpResponse, HttpRequest, HttpEventType, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
-import { catchError, map, filter } from 'rxjs/operators'
+import { catchError, map, filter, tap } from 'rxjs/operators'
import { serialize } from 'app/shared/utilities/serialize';
export enum RequestMethod {
@@ -17,22 +17,43 @@ export enum RequestMethod {
@Injectable()
export class ApiService {
+ private static extractTokenFromHttpResponse(res: HttpResponse): string {
+ let authorization: string = null;
+ let headers: HttpHeaders = res.headers;
+ if (headers && headers.has('Authorization')){
+ authorization = headers.get('Authorization');
+ if(authorization.startsWith('Bearer '))
+ authorization = authorization.substring(7);
+ }
+ return authorization;
+ }
+
headers = new HttpHeaders({
'Accept': 'application/json',
'Content-Type': 'application/json'
});
+ private jwtToken: string;
+
constructor( private http: HttpClient) { }
+ setToken(token: string) {
+ this.jwtToken = token;
+ }
+
+ getToken = () => this.jwtToken;
+
get(path: string, args?: any): Observable {
const options = {
headers: this.headers,
withCredentials: true
};
- if (args) {
+ if (args)
options['params'] = serialize(args);
- }
+
+ // if(this.jwtToken)
+ // options.headers = options.headers.set('Authorization', `Bearer ${this.jwtToken}`);
return this.http.get(path, options)
.pipe(catchError(this.checkError.bind(this)));
@@ -51,14 +72,24 @@ export class ApiService {
}
private request(path: string, body: any, method = RequestMethod.Post, customHeaders?: HttpHeaders): Observable {
- const req = new HttpRequest(method, path, body, {
+ const options = {
headers: customHeaders || this.headers,
withCredentials: true
- });
+ }
+
+ // if(this.jwtToken)
+ // options.headers = options.headers.append('Authorization', `Bearer ${this.jwtToken}`);
+
+ const req = new HttpRequest(method, path, body, options);
return this.http.request(req)
.pipe(
filter(response => response instanceof HttpResponse),
+ tap((resp: HttpResponse) => {
+ let jwtToken = ApiService.extractTokenFromHttpResponse(resp);
+ if(jwtToken)
+ this.setToken(jwtToken);
+ }),
map((response: HttpResponse) => response.body),
catchError(error => this.checkError(error))
)
@@ -74,4 +105,6 @@ export class ApiService {
throw error;
}
+
+
}
diff --git a/quartz-manager-frontend/src/app/services/auth.service.ts b/quartz-manager-frontend/src/app/services/auth.service.ts
index 3af4dda..c39a395 100644
--- a/quartz-manager-frontend/src/app/services/auth.service.ts
+++ b/quartz-manager-frontend/src/app/services/auth.service.ts
@@ -1,10 +1,10 @@
import { Injectable } from '@angular/core';
-import { HttpHeaders } from '@angular/common/http';
+import { HttpHeaders, HttpResponse } from '@angular/common/http';
import { ApiService } from './api.service';
import { UserService } from './user.service';
import { ConfigService } from './config.service';
import { Observable } from 'rxjs';
-import { map } from 'rxjs/operators';
+import { map, tap } from 'rxjs/operators';
@Injectable()
export class AuthService {
@@ -21,10 +21,13 @@ export class AuthService {
'Content-Type': 'application/x-www-form-urlencoded'
});
const body = `username=${user.username}&password=${user.password}`;
- return this.apiService.post(this.config.login_url, body, loginHeaders).pipe(map(() => {
- console.log("Login success");
- this.userService.getMyInfo().subscribe();
- }));
+ return this.apiService.post(this.config.login_url, body, loginHeaders)
+ .pipe(
+ map(() => {
+ console.log("Login success");
+ this.userService.getMyInfo().subscribe();
+ })
+ );
}
signup(user){
@@ -40,6 +43,7 @@ export class AuthService {
logout() {
return this.apiService.post(this.config.logout_url, {})
.pipe(map(() => {
+ this.apiService.setToken(null);
this.userService.currentUser = null;
}));
}
@@ -48,4 +52,5 @@ export class AuthService {
return this.apiService.post(this.config.change_password_url, passwordChanger);
}
+
}
diff --git a/quartz-manager-frontend/src/app/services/logs.websocket.service.ts b/quartz-manager-frontend/src/app/services/logs.websocket.service.ts
index 76664cc..fe2cd06 100644
--- a/quartz-manager-frontend/src/app/services/logs.websocket.service.ts
+++ b/quartz-manager-frontend/src/app/services/logs.websocket.service.ts
@@ -1,12 +1,12 @@
import { Injectable, OnInit } from '@angular/core';
-import { WebsocketService } from '.';
+import { WebsocketService, ApiService } from '.';
import { SocketOption } from '../model/SocketOption.model';
-Injectable()
+@Injectable()
export class LogsWebsocketService extends WebsocketService {
- constructor(){
- super(new SocketOption('/quartz-manager/logs', '/topic/logs'))
+ constructor(private apiService: ApiService){
+ super(new SocketOption('/quartz-manager/logs', '/topic/logs', apiService.getToken))
}
}
\ No newline at end of file
diff --git a/quartz-manager-frontend/src/app/services/progress.websocket.service.ts b/quartz-manager-frontend/src/app/services/progress.websocket.service.ts
index 378b7d2..173428b 100644
--- a/quartz-manager-frontend/src/app/services/progress.websocket.service.ts
+++ b/quartz-manager-frontend/src/app/services/progress.websocket.service.ts
@@ -1,12 +1,12 @@
import { Injectable, OnInit } from '@angular/core';
-import { WebsocketService } from '.';
+import { WebsocketService, ApiService } from '.';
import { SocketOption } from '../model/SocketOption.model';
-Injectable()
+@Injectable()
export class ProgressWebsocketService extends WebsocketService {
- constructor(){
- super(new SocketOption('/quartz-manager/progress', '/topic/progress'))
+ constructor(private apiService: ApiService){
+ super(new SocketOption('/quartz-manager/progress', '/topic/progress', apiService.getToken))
}
}
\ No newline at end of file
diff --git a/quartz-manager-frontend/src/app/services/scheduler.service.ts b/quartz-manager-frontend/src/app/services/scheduler.service.ts
index 2da0a1f..f2b351b 100644
--- a/quartz-manager-frontend/src/app/services/scheduler.service.ts
+++ b/quartz-manager-frontend/src/app/services/scheduler.service.ts
@@ -1,5 +1,4 @@
import { Injectable } from '@angular/core';
-import { Headers } from '@angular/http';
import { ApiService } from './api.service';
@Injectable()
diff --git a/quartz-manager-frontend/src/app/services/user.service.ts b/quartz-manager-frontend/src/app/services/user.service.ts
index 310eb49..351653a 100644
--- a/quartz-manager-frontend/src/app/services/user.service.ts
+++ b/quartz-manager-frontend/src/app/services/user.service.ts
@@ -1,5 +1,4 @@
import { Injectable } from '@angular/core';
-import { Headers } from '@angular/http';
import { ApiService } from './api.service';
import { ConfigService } from './config.service';
diff --git a/quartz-manager-frontend/src/app/services/websocket.service.ts b/quartz-manager-frontend/src/app/services/websocket.service.ts
index a9fa640..e1eda64 100644
--- a/quartz-manager-frontend/src/app/services/websocket.service.ts
+++ b/quartz-manager-frontend/src/app/services/websocket.service.ts
@@ -1,10 +1,4 @@
-import { Injectable, OnInit } from '@angular/core';
-import { Headers } from '@angular/http';
-
import { Observable } from 'rxjs';
-
-import { ApiService } from './api.service';
-
import { SocketEndpoint } from '../model/SocketEndpoint.model'
@@ -39,10 +33,7 @@ export class WebsocketService {
this.observableStompConnection = new Observable((observer) => {
const subscriberIndex = this.subscriberIndex++;
this.addToSubscribers({ index: subscriberIndex, observer });
- return () => {
- const index = subscriberIndex;
- this.removeFromSubscribers(index);
- };
+ return () => this.removeFromSubscribers(subscriberIndex);
});
}
@@ -51,13 +42,9 @@ export class WebsocketService {
}
removeFromSubscribers = (index) => {
- let subscribeFromIndex;
- for (let i=0 ; i < this.subscribers.length; i++)
- if(i === index){
- subscribeFromIndex = this.subscribers[i];
- this.subscribers.splice(i, 1);
- break;
- }
+ if(index > this.subscribers.length)
+ throw new Error(`Unexpected error removing subscriber from websocket, because index ${index} is greater than subscriber length ${this.subscribers.length}`);
+ this.subscribers.splice(index, 1);
}
getObservable = () => {
@@ -71,7 +58,7 @@ export class WebsocketService {
out.headers = {};
out.headers.messageId = data.headers["message-id"];
- let messageIdIndex = this._messageIds.indexOf( out.headers.messageId);
+ let messageIdIndex = this._messageIds.indexOf(out.headers.messageId);
if ( messageIdIndex > -1) {
out.self = true;
this._messageIds = this._messageIds.splice(messageIdIndex, 1);
@@ -81,24 +68,20 @@ export class WebsocketService {
_socketListener = (frame) => {
console.log('Connected: ' + frame);
- this._socket.stomp.subscribe(this._options.topicName, (data) => {
- this.subscribers.forEach(subscriber => {
- subscriber.observer.next(this.getMessage(data));
- })
- })
+ this._socket.stomp.subscribe(
+ this._options.topicName,
+ data => this.subscribers.forEach(subscriber => subscriber.observer.next(this.getMessage(data)))
+ );
}
_onSocketError = (errorMsg) => {
let out: any = {};
out.type = 'ERROR';
out.message = errorMsg;
- this.subscribers.forEach(subscriber => {
- subscriber.observer.error(out);
- })
+ this.subscribers.forEach(subscriber => subscriber.observer.error(out));
this.scheduleReconnection();
}
-
scheduleReconnection = () => {
this.reconnectionPromise = setTimeout(() => {
console.log("Socket reconnecting... (if it fails, next attempt in " + this._options.reconnectionTimeout + " msec)");
@@ -126,7 +109,12 @@ export class WebsocketService {
connect = () => {
const headers = {};
- this._socket.client = new SockJS(this._options.socketUrl);
+
+ let socketUrl = this._options.socketUrl;
+ if(this._options.getAccessToken())
+ socketUrl += `?access_token=${this._options.getAccessToken()}`
+
+ this._socket.client = new SockJS(socketUrl);
this._socket.stomp = Stomp.over(this._socket.client);
this._socket.stomp.connect(headers, this._socketListener, this._onSocketError);
this._socket.stomp.onclose = this.scheduleReconnection;
diff --git a/quartz-manager-frontend/src/app/views/login/login.component.ts b/quartz-manager-frontend/src/app/views/login/login.component.ts
index 5962dd3..201b259 100644
--- a/quartz-manager-frontend/src/app/views/login/login.component.ts
+++ b/quartz-manager-frontend/src/app/views/login/login.component.ts
@@ -82,7 +82,6 @@ export class LoginComponent implements OnInit, OnDestroy {
this.authService.login(this.form.value)
.pipe(delay(1000))
.subscribe(data => {
- this.userService.getMyInfo().subscribe();
this.router.navigate([this.returnUrl]);
},
error => {
diff --git a/quartz-manager-frontend/src/tsconfig.app.json b/quartz-manager-frontend/src/tsconfig.app.json
index 5e2507d..26227d8 100644
--- a/quartz-manager-frontend/src/tsconfig.app.json
+++ b/quartz-manager-frontend/src/tsconfig.app.json
@@ -2,12 +2,14 @@
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
- "module": "es2015",
"baseUrl": "",
"types": []
},
- "exclude": [
- "test.ts",
- "**/*.spec.ts"
+ "files": [
+ "main.ts",
+ "polyfills.ts"
+ ],
+ "include": [
+ "src/**/*.d.ts"
]
}
diff --git a/quartz-manager-frontend/src/tsconfig.spec.json b/quartz-manager-frontend/src/tsconfig.spec.json
index 15458ed..9c56a51 100644
--- a/quartz-manager-frontend/src/tsconfig.spec.json
+++ b/quartz-manager-frontend/src/tsconfig.spec.json
@@ -2,8 +2,6 @@
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
- "module": "commonjs",
- "target": "es5",
"baseUrl": "",
"types": [
"jasmine",
diff --git a/quartz-manager-frontend/tsconfig.json b/quartz-manager-frontend/tsconfig.json
index ab228cc..3a709a3 100644
--- a/quartz-manager-frontend/tsconfig.json
+++ b/quartz-manager-frontend/tsconfig.json
@@ -1,6 +1,7 @@
{
"compileOnSave": false,
"compilerOptions": {
+ "downlevelIteration": true,
"importHelpers": true,
"outDir": "./dist/out-tsc",
"baseUrl": "src",
@@ -9,7 +10,7 @@
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
- "target": "es5",
+ "target": "es2015",
"typeRoots": [
"node_modules/@types"
],
@@ -17,6 +18,6 @@
"es2016",
"dom"
],
- "module": "es2015"
+ "module": "esnext"
}
}
\ No newline at end of file
diff --git a/quartz-manager-frontend/tslint.json b/quartz-manager-frontend/tslint.json
index bb84fcf..1701630 100644
--- a/quartz-manager-frontend/tslint.json
+++ b/quartz-manager-frontend/tslint.json
@@ -100,12 +100,12 @@
"directive-selector": [true, "attribute", "app", "camelCase"],
"component-selector": [true, "element", "app", "kebab-case"],
- "use-input-property-decorator": true,
- "use-output-property-decorator": true,
- "use-host-property-decorator": true,
+ "no-inputs-metadata-property": true,
+ "no-outputs-metadata-property": true,
+ "no-host-metadata-property": true,
"no-input-rename": true,
"no-output-rename": true,
- "use-life-cycle-interface": true,
+ "use-lifecycle-interface": true,
"use-pipe-transform-interface": true,
"component-class-suffix": true,
"directive-class-suffix": true,