mirror of
https://github.com/fabioformosa/quartz-manager.git
synced 2025-12-31 06:33:16 +09:00
first commit
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>it.fabioformosa</groupId>
|
||||
<artifactId>quartz-manager</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>2.0.1-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<name>quartz-manager</name>
|
||||
@@ -20,13 +20,14 @@
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -38,32 +39,25 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf.extras</groupId>
|
||||
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-velocity</artifactId>
|
||||
<version>1.4.7.RELEASE</version>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.nekohtml</groupId>
|
||||
<artifactId>nekohtml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-messaging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
<scope>provided</scope>
|
||||
@@ -75,23 +69,45 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-messaging</artifactId>
|
||||
</dependency>
|
||||
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>0.9.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>bootstrap</artifactId>
|
||||
<version>3.3.6</version>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>nz.net.ultraq.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf-layout-dialect</artifactId>
|
||||
</dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.nekohtml</groupId>
|
||||
<artifactId>nekohtml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>spring-mock-mvc</artifactId>
|
||||
<version>3.0.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.quartz-scheduler</groupId>
|
||||
<artifactId>quartz</artifactId>
|
||||
@@ -102,11 +118,7 @@
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Reactor -->
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
@@ -0,0 +1,22 @@
|
||||
package it.fabioformosa.quartzmanager.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
|
||||
@Configuration
|
||||
public class MVCConfig extends WebMvcConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController("/login").setViewName("login");
|
||||
registry.addViewController("/").setViewName("redirect:/manager");
|
||||
|
||||
registry.addViewController("/templates/manager/config-form.html").setViewName("manager/config-form");
|
||||
registry.addViewController("/templates/manager/progress-panel.html")
|
||||
.setViewName("manager/progress-panel");
|
||||
registry.addViewController("/templates/manager/logs-panel.html").setViewName("manager/logs-panel");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package it.fabioformosa.quartzmanager.configuration;
|
||||
|
||||
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.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
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.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
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 it.fabioformosa.quartzmanager.security.service.impl.CustomUserDetailsService;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-10-19.
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Value("${jwt.cookie}")
|
||||
private String TOKEN_COOKIE;
|
||||
|
||||
@Autowired
|
||||
private CustomUserDetailsService jwtUserDetailsService;
|
||||
|
||||
@Autowired
|
||||
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
|
||||
|
||||
@Autowired
|
||||
private LogoutSuccess logoutSuccess;
|
||||
|
||||
@Autowired
|
||||
private AuthenticationSuccessHandler authenticationSuccessHandler;
|
||||
|
||||
@Autowired
|
||||
private AuthenticationFailureHandler authenticationFailureHandler;
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder)
|
||||
throws Exception {
|
||||
authenticationManagerBuilder.userDetailsService(jwtUserDetailsService)
|
||||
.passwordEncoder(passwordEncoder());
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TokenAuthenticationFilter jwtAuthenticationTokenFilter() throws Exception {
|
||||
return new TokenAuthenticationFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().ignoringAntMatchers("/api/login", "/api/signup")
|
||||
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
|
||||
.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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,13 +2,9 @@ package it.fabioformosa.quartzmanager.configuration;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
@@ -16,48 +12,50 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import it.fabioformosa.quartzmanager.security.AjaxAuthenticationFilter;
|
||||
import it.fabioformosa.quartzmanager.security.ComboEntryPoint;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
//@Configuration
|
||||
//@EnableWebSecurity
|
||||
public class WebSecurityConfigOld extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Configuration
|
||||
@Order(1)
|
||||
// @Configuration
|
||||
// @Order(1)
|
||||
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().disable() //
|
||||
.antMatcher("/notifications").authorizeRequests().anyRequest().hasAnyRole("ADMIN").and()
|
||||
.httpBasic();
|
||||
.antMatcher("/notifications").authorizeRequests().anyRequest().hasAnyRole("ADMIN").and()
|
||||
.httpBasic();
|
||||
|
||||
// http.antMatcher("/logs/**").authorizeRequests().anyRequest()
|
||||
// .permitAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(2)
|
||||
// @Configuration
|
||||
// @Order(2)
|
||||
public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Resource
|
||||
private ComboEntryPoint comboEntryPoint;
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.exceptionHandling().authenticationEntryPoint(comboEntryPoint).and().csrf().disable()//
|
||||
.authorizeRequests().anyRequest().authenticated().and()//
|
||||
.addFilterBefore(new AjaxAuthenticationFilter(authenticationManager()),
|
||||
UsernamePasswordAuthenticationFilter.class)//
|
||||
.formLogin().loginPage("/login").permitAll().and().logout()
|
||||
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/manager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().ignoringAntMatchers("/api/login", "/api/signup").and() //
|
||||
.exceptionHandling().authenticationEntryPoint(comboEntryPoint).and().csrf().disable()//
|
||||
.authorizeRequests().anyRequest().authenticated().and()//
|
||||
.addFilterBefore(new AjaxAuthenticationFilter(authenticationManager()),
|
||||
UsernamePasswordAuthenticationFilter.class)//
|
||||
.formLogin().loginPage("/api/login").permitAll().and().logout()
|
||||
.logoutRequestMatcher(new AntPathRequestMatcher("/api/logout"))
|
||||
.logoutSuccessUrl("/manager");
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
// @Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package it.fabioformosa.quartzmanager.controllers;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.TokenHelper;
|
||||
import it.fabioformosa.quartzmanager.security.model.UserTokenState;
|
||||
import it.fabioformosa.quartzmanager.security.service.impl.CustomUserDetailsService;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2017-05-10.
|
||||
*/
|
||||
|
||||
@RestController
|
||||
@RequestMapping( value = "/api", produces = MediaType.APPLICATION_JSON_VALUE )
|
||||
public class AuthenticationController {
|
||||
|
||||
static class PasswordChanger {
|
||||
public String oldPassword;
|
||||
public String newPassword;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private CustomUserDetailsService userDetailsService;
|
||||
|
||||
@Autowired
|
||||
TokenHelper tokenHelper;
|
||||
|
||||
@Value("${jwt.expires_in}")
|
||||
private int EXPIRES_IN;
|
||||
|
||||
@Value("${jwt.cookie}")
|
||||
private String TOKEN_COOKIE;
|
||||
|
||||
@RequestMapping(value = "/changePassword", method = RequestMethod.POST)
|
||||
@PreAuthorize("hasRole('USER')")
|
||||
public ResponseEntity<?> changePassword(@RequestBody PasswordChanger passwordChanger) {
|
||||
userDetailsService.changePassword(passwordChanger.oldPassword, passwordChanger.newPassword);
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put( "result", "success" );
|
||||
return ResponseEntity.accepted().body(result);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/refresh", method = RequestMethod.GET)
|
||||
public ResponseEntity<?> refreshAuthenticationToken(HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
String authToken = tokenHelper.getToken( request );
|
||||
if (authToken != null && tokenHelper.canTokenBeRefreshed(authToken)) {
|
||||
// TODO check user password last update
|
||||
String refreshedToken = tokenHelper.refreshToken(authToken);
|
||||
|
||||
Cookie authCookie = new Cookie( TOKEN_COOKIE, refreshedToken );
|
||||
authCookie.setPath( "/" );
|
||||
authCookie.setHttpOnly( true );
|
||||
authCookie.setMaxAge( EXPIRES_IN );
|
||||
// Add cookie to response
|
||||
response.addCookie( authCookie );
|
||||
|
||||
UserTokenState userTokenState = new UserTokenState(refreshedToken, EXPIRES_IN);
|
||||
return ResponseEntity.ok(userTokenState);
|
||||
} else {
|
||||
UserTokenState userTokenState = new UserTokenState();
|
||||
return ResponseEntity.accepted().body(userTokenState);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package it.fabioformosa.quartzmanager.controllers;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2017-05-08.
|
||||
*/
|
||||
|
||||
@RestController
|
||||
@RequestMapping( value = "/api", produces = MediaType.APPLICATION_JSON_VALUE )
|
||||
public class PublicController {
|
||||
|
||||
@RequestMapping( method = GET, value= "/foo")
|
||||
public Map<String, String> getFoo() {
|
||||
Map<String, String> fooObj = new HashMap<>();
|
||||
fooObj.put("foo", "bar");
|
||||
return fooObj;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package it.fabioformosa.quartzmanager.controllers;
|
||||
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import it.fabioformosa.quartzmanager.exceptions.ResourceConflictException;
|
||||
import it.fabioformosa.quartzmanager.security.model.User;
|
||||
import it.fabioformosa.quartzmanager.security.model.UserRequest;
|
||||
import it.fabioformosa.quartzmanager.security.service.UserService;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-10-15.
|
||||
*/
|
||||
|
||||
@RestController
|
||||
@RequestMapping(value = "/api", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public class UserController {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
|
||||
@RequestMapping(method = POST, value = "/signup")
|
||||
public ResponseEntity<?> addUser(@RequestBody UserRequest userRequest,
|
||||
UriComponentsBuilder ucBuilder) {
|
||||
|
||||
User existUser = this.userService.findByUsername(userRequest.getUsername());
|
||||
if (existUser != null)
|
||||
throw new ResourceConflictException(userRequest.getId(), "Username already exists");
|
||||
User user = this.userService.save(userRequest);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setLocation(ucBuilder.path("/api/user/{userId}").buildAndExpand(user.getId()).toUri());
|
||||
return new ResponseEntity<>(user, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
@RequestMapping(method = GET, value = "/user/all")
|
||||
public List<User> loadAll() {
|
||||
return this.userService.findAll();
|
||||
}
|
||||
|
||||
@RequestMapping(method = GET, value = "/user/{userId}")
|
||||
public User loadById(@PathVariable Long userId) {
|
||||
return this.userService.findById(userId);
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping(method = GET, value = "/user/reset-credentials")
|
||||
public ResponseEntity<Map> resetCredentials() {
|
||||
this.userService.resetCredentials();
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("result", "success");
|
||||
return ResponseEntity.accepted().body(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* We are not using userService.findByUsername here(we could), so it is good that we are making
|
||||
* sure that the user has role "ROLE_USER" to access this endpoint.
|
||||
*/
|
||||
@RequestMapping("/whoami")
|
||||
@PreAuthorize("hasRole('USER')")
|
||||
public User user() {
|
||||
return (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package it.fabioformosa.quartzmanager.exceptions;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
|
||||
@ControllerAdvice
|
||||
public class ExceptionHandlingController {
|
||||
|
||||
@ExceptionHandler(ResourceConflictException.class)
|
||||
public ResponseEntity<ExceptionResponse> resourceConflict(ResourceConflictException ex) {
|
||||
ExceptionResponse response = new ExceptionResponse();
|
||||
response.setErrorCode("Conflict");
|
||||
response.setErrorMessage(ex.getMessage());
|
||||
return new ResponseEntity<ExceptionResponse>(response, HttpStatus.CONFLICT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package it.fabioformosa.quartzmanager.exceptions;
|
||||
|
||||
public class ExceptionResponse {
|
||||
|
||||
private String errorCode;
|
||||
private String errorMessage;
|
||||
|
||||
public ExceptionResponse() {}
|
||||
|
||||
public String getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
public void setErrorCode(String errorCode) {
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
public void setErrorMessage(String errorMessage) {
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package it.fabioformosa.quartzmanager.exceptions;
|
||||
|
||||
public class ResourceConflictException extends RuntimeException {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1791564636123821405L;
|
||||
private Long resourceId;
|
||||
|
||||
public ResourceConflictException(Long resourceId, String message) {
|
||||
super(message);
|
||||
this.setResourceId(resourceId);
|
||||
}
|
||||
|
||||
public Long getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public void setResourceId(Long resourceId) {
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package it.fabioformosa.quartzmanager.security;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import org.joda.time.DateTime;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-10-19.
|
||||
*/
|
||||
|
||||
@Component
|
||||
public class TokenHelper {
|
||||
|
||||
@Value("${app.name}")
|
||||
private String APP_NAME;
|
||||
|
||||
@Value("${jwt.secret}")
|
||||
private String SECRET;
|
||||
|
||||
@Value("${jwt.expires_in}")
|
||||
private int EXPIRES_IN;
|
||||
|
||||
@Value("${jwt.header}")
|
||||
private String AUTH_HEADER;
|
||||
|
||||
@Value("${jwt.cookie}")
|
||||
private String AUTH_COOKIE;
|
||||
|
||||
@Autowired
|
||||
UserDetailsService userDetailsService;
|
||||
|
||||
private SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS512;
|
||||
|
||||
public String getUsernameFromToken(String token) {
|
||||
String username;
|
||||
try {
|
||||
final Claims claims = this.getClaimsFromToken(token);
|
||||
username = claims.getSubject();
|
||||
} catch (Exception e) {
|
||||
username = null;
|
||||
}
|
||||
return username;
|
||||
}
|
||||
|
||||
public String generateToken(String username) {
|
||||
return Jwts.builder()
|
||||
.setIssuer( APP_NAME )
|
||||
.setSubject(username)
|
||||
.setIssuedAt(generateCurrentDate())
|
||||
.setExpiration(generateExpirationDate())
|
||||
.signWith( SIGNATURE_ALGORITHM, SECRET )
|
||||
.compact();
|
||||
}
|
||||
|
||||
private Claims getClaimsFromToken(String token) {
|
||||
Claims claims;
|
||||
try {
|
||||
claims = Jwts.parser()
|
||||
.setSigningKey(this.SECRET)
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
} catch (Exception e) {
|
||||
claims = null;
|
||||
}
|
||||
return claims;
|
||||
}
|
||||
|
||||
String generateToken(Map<String, Object> claims) {
|
||||
return Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.setExpiration(generateExpirationDate())
|
||||
.signWith( SIGNATURE_ALGORITHM, SECRET )
|
||||
.compact();
|
||||
}
|
||||
|
||||
public Boolean canTokenBeRefreshed(String token) {
|
||||
try {
|
||||
final Date expirationDate = getClaimsFromToken(token).getExpiration();
|
||||
String username = getUsernameFromToken(token);
|
||||
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||
return expirationDate.compareTo(generateCurrentDate()) > 0;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String refreshToken(String token) {
|
||||
String refreshedToken;
|
||||
try {
|
||||
final Claims claims = getClaimsFromToken(token);
|
||||
claims.setIssuedAt(generateCurrentDate());
|
||||
refreshedToken = generateToken(claims);
|
||||
} catch (Exception e) {
|
||||
refreshedToken = null;
|
||||
}
|
||||
return refreshedToken;
|
||||
}
|
||||
|
||||
private long getCurrentTimeMillis() {
|
||||
return DateTime.now().getMillis();
|
||||
}
|
||||
|
||||
private Date generateCurrentDate() {
|
||||
return new Date(getCurrentTimeMillis());
|
||||
}
|
||||
|
||||
private Date generateExpirationDate() {
|
||||
|
||||
return new Date(getCurrentTimeMillis() + this.EXPIRES_IN * 1000);
|
||||
}
|
||||
|
||||
public String getToken( HttpServletRequest request ) {
|
||||
/**
|
||||
* Getting the token from Cookie store
|
||||
*/
|
||||
Cookie authCookie = getCookieValueByName( request, AUTH_COOKIE );
|
||||
if ( authCookie != null ) {
|
||||
return authCookie.getValue();
|
||||
}
|
||||
/**
|
||||
* Getting the token from Authentication header
|
||||
* e.g Bearer your_token
|
||||
*/
|
||||
String authHeader = request.getHeader(AUTH_HEADER);
|
||||
if ( authHeader != null && authHeader.startsWith("Bearer ")) {
|
||||
return authHeader.substring(7);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a specific HTTP cookie in a request.
|
||||
*
|
||||
* @param request
|
||||
* The HTTP request object.
|
||||
* @param name
|
||||
* The cookie name to look for.
|
||||
* @return The cookie, or <code>null</code> if not found.
|
||||
*/
|
||||
public Cookie getCookieValueByName(HttpServletRequest request, String name) {
|
||||
if (request.getCookies() == null) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < request.getCookies().length; i++) {
|
||||
if (request.getCookies()[i].getName().equals(name)) {
|
||||
return request.getCookies()[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package it.fabioformosa.quartzmanager.security.auth;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2017-04-04.
|
||||
*/
|
||||
|
||||
public class AnonAuthentication extends AbstractAuthenticationToken {
|
||||
|
||||
public AnonAuthentication() {
|
||||
super( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthenticated() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals( Object obj ) {
|
||||
if ( this == obj ) {
|
||||
return true;
|
||||
}
|
||||
if ( obj == null ) {
|
||||
return false;
|
||||
}
|
||||
if ( getClass() != obj.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package it.fabioformosa.quartzmanager.security.auth;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-11-07.
|
||||
*/
|
||||
|
||||
@Component
|
||||
public class AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException exception) throws IOException, ServletException {
|
||||
|
||||
super.onAuthenticationFailure(request, response, exception);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
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.web.authentication.SimpleUrlAuthenticationSuccessHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-11-07.
|
||||
*/
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.TokenHelper;
|
||||
import it.fabioformosa.quartzmanager.security.model.User;
|
||||
import it.fabioformosa.quartzmanager.security.model.UserTokenState;
|
||||
|
||||
@Component
|
||||
public class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
|
||||
|
||||
@Value("${jwt.expires_in}")
|
||||
private int EXPIRES_IN;
|
||||
|
||||
@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() );
|
||||
|
||||
// Create token auth Cookie
|
||||
Cookie authCookie = new Cookie( TOKEN_COOKIE, jws );
|
||||
|
||||
authCookie.setHttpOnly( true );
|
||||
|
||||
authCookie.setMaxAge( EXPIRES_IN );
|
||||
|
||||
authCookie.setPath( "/" );
|
||||
// Add cookie to response
|
||||
response.addCookie( authCookie );
|
||||
|
||||
// JWT is also in the response
|
||||
UserTokenState userTokenState = new UserTokenState(jws, EXPIRES_IN);
|
||||
String jwtResponse = objectMapper.writeValueAsString( userTokenState );
|
||||
response.setContentType("application/json");
|
||||
response.getWriter().write( jwtResponse );
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package it.fabioformosa.quartzmanager.security.auth;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
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 javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2017-05-06.
|
||||
*/
|
||||
@Component
|
||||
public class LogoutSuccess implements LogoutSuccessHandler {
|
||||
|
||||
@Autowired
|
||||
ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse response, Authentication authentication)
|
||||
throws IOException, ServletException {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put( "result", "success" );
|
||||
response.setContentType("application/json");
|
||||
response.getWriter().write( objectMapper.writeValueAsString( result ) );
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package it.fabioformosa.quartzmanager.security.auth;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-11-12.
|
||||
*/
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-11-07.
|
||||
*/
|
||||
@Component
|
||||
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
AuthenticationException authException) throws IOException {
|
||||
// This is invoked when user tries to access a secured REST resource without supplying any credentials
|
||||
// We should just send a 401 Unauthorized response because there is no 'login page' to redirect to
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
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.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-10-19.
|
||||
*/
|
||||
public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
/*
|
||||
* The below paths will get ignored by the filter
|
||||
*/
|
||||
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
|
||||
TokenHelper tokenHelper;
|
||||
@Autowired
|
||||
UserDetailsService userDetailsService;
|
||||
|
||||
private List<String> 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))
|
||||
// get username from token
|
||||
try {
|
||||
String username = tokenHelper.getUsernameFromToken(authToken);
|
||||
// get user
|
||||
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());
|
||||
}
|
||||
else
|
||||
SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication());
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private boolean skipPathRequest(HttpServletRequest request, List<String> pathsToSkip ) {
|
||||
Assert.notNull(pathsToSkip, "path cannot be null.");
|
||||
List<RequestMatcher> m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList());
|
||||
OrRequestMatcher matchers = new OrRequestMatcher(m);
|
||||
return matchers.matches(request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package it.fabioformosa.quartzmanager.security.auth;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-11-11.
|
||||
*/
|
||||
public class TokenBasedAuthentication extends AbstractAuthenticationToken {
|
||||
|
||||
private String token;
|
||||
private final UserDetails principle;
|
||||
|
||||
public TokenBasedAuthentication( UserDetails principle ) {
|
||||
super( principle.getAuthorities() );
|
||||
this.principle = principle;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken( String token ) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthenticated() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails getPrincipal() {
|
||||
return principle;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package it.fabioformosa.quartzmanager.security.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-11-03.
|
||||
*/
|
||||
|
||||
@Entity
|
||||
@Table(name="Authority")
|
||||
public class Authority implements GrantedAuthority {
|
||||
|
||||
@Id
|
||||
@Column(name="id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
Long id;
|
||||
|
||||
@Column(name="name")
|
||||
String name;
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package it.fabioformosa.quartzmanager.security.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.Table;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-10-15.
|
||||
*/
|
||||
|
||||
@Entity
|
||||
@Table(name = "USER")
|
||||
public class User implements UserDetails, Serializable {
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "username")
|
||||
private String username;
|
||||
|
||||
@JsonIgnore
|
||||
@Column(name = "password")
|
||||
private String password;
|
||||
|
||||
@Column(name = "firstname")
|
||||
private String firstname;
|
||||
|
||||
@Column(name = "lastname")
|
||||
private String lastname;
|
||||
|
||||
|
||||
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
@JoinTable(name = "user_authority",
|
||||
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "authority_id", referencedColumnName = "id"))
|
||||
private List<Authority> authorities;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getFirstname() {
|
||||
return firstname;
|
||||
}
|
||||
|
||||
public void setFirstname(String firstname) {
|
||||
this.firstname = firstname;
|
||||
}
|
||||
|
||||
public String getLastname() {
|
||||
return lastname;
|
||||
}
|
||||
|
||||
public void setLastname(String lastname) {
|
||||
|
||||
this.lastname = lastname;
|
||||
}
|
||||
|
||||
public void setAuthorities(List<Authority> authorities) {
|
||||
this.authorities = authorities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return this.authorities;
|
||||
}
|
||||
|
||||
// We can add the below fields in the users table.
|
||||
// For now, they are hardcoded.
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package it.fabioformosa.quartzmanager.security.model;
|
||||
|
||||
|
||||
public class UserRequest {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private String firstname;
|
||||
|
||||
private String lastname;
|
||||
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getFirstname() {
|
||||
return firstname;
|
||||
}
|
||||
|
||||
public void setFirstname(String firstname) {
|
||||
this.firstname = firstname;
|
||||
}
|
||||
|
||||
public String getLastname() {
|
||||
return lastname;
|
||||
}
|
||||
|
||||
public void setLastname(String lastname) {
|
||||
this.lastname = lastname;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package it.fabioformosa.quartzmanager.security.model;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-10-17.
|
||||
*/
|
||||
public class UserTokenState {
|
||||
private String access_token;
|
||||
private Long expires_in;
|
||||
|
||||
public UserTokenState() {
|
||||
this.access_token = null;
|
||||
this.expires_in = null;
|
||||
}
|
||||
|
||||
public UserTokenState(String access_token, long expires_in) {
|
||||
this.access_token = access_token;
|
||||
this.expires_in = expires_in;
|
||||
}
|
||||
|
||||
public String getAccess_token() {
|
||||
return access_token;
|
||||
}
|
||||
|
||||
public void setAccess_token(String access_token) {
|
||||
this.access_token = access_token;
|
||||
}
|
||||
|
||||
public Long getExpires_in() {
|
||||
return expires_in;
|
||||
}
|
||||
|
||||
public void setExpires_in(Long expires_in) {
|
||||
this.expires_in = expires_in;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package it.fabioformosa.quartzmanager.security.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.model.Authority;
|
||||
|
||||
public interface AuthorityRepository extends JpaRepository<Authority, Long> {
|
||||
Authority findByName(String name);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package it.fabioformosa.quartzmanager.security.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.model.User;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-10-15.
|
||||
*/
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
User findByUsername( String username );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package it.fabioformosa.quartzmanager.security.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.model.Authority;
|
||||
|
||||
public interface AuthorityService {
|
||||
List<Authority> findById(Long id);
|
||||
|
||||
List<Authority> findByname(String name);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package it.fabioformosa.quartzmanager.security.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.model.User;
|
||||
import it.fabioformosa.quartzmanager.security.model.UserRequest;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-10-15.
|
||||
*/
|
||||
public interface UserService {
|
||||
List<User> findAll();
|
||||
|
||||
User findById(Long id);
|
||||
|
||||
User findByUsername(String username);
|
||||
|
||||
void resetCredentials();
|
||||
|
||||
User save(UserRequest user);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package it.fabioformosa.quartzmanager.security.service.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.model.Authority;
|
||||
import it.fabioformosa.quartzmanager.security.repository.AuthorityRepository;
|
||||
import it.fabioformosa.quartzmanager.security.service.AuthorityService;
|
||||
|
||||
@Service
|
||||
public class AuthorityServiceImpl implements AuthorityService {
|
||||
|
||||
@Autowired
|
||||
private AuthorityRepository authorityRepository;
|
||||
|
||||
@Override
|
||||
public List<Authority> findById(Long id) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
Authority auth = this.authorityRepository.findOne(id);
|
||||
List<Authority> auths = new ArrayList<>();
|
||||
auths.add(auth);
|
||||
return auths;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Authority> findByname(String name) {
|
||||
// TODO Auto-generated method stub
|
||||
Authority auth = this.authorityRepository.findByName(name);
|
||||
List<Authority> auths = new ArrayList<>();
|
||||
auths.add(auth);
|
||||
return auths;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package it.fabioformosa.quartzmanager.security.service.impl;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
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.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.model.User;
|
||||
import it.fabioformosa.quartzmanager.security.repository.UserRepository;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-10-31.
|
||||
*/
|
||||
|
||||
@Service
|
||||
public class CustomUserDetailsService implements UserDetailsService {
|
||||
|
||||
protected final Log LOGGER = LogFactory.getLog(getClass());
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Autowired
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
public void changePassword(String oldPassword, String newPassword) {
|
||||
|
||||
Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
|
||||
String username = currentUser.getName();
|
||||
|
||||
if (authenticationManager != null) {
|
||||
LOGGER.debug("Re-authenticating user '"+ username + "' for password change request.");
|
||||
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, oldPassword));
|
||||
} else {
|
||||
LOGGER.debug("No authentication manager set. can't change Password!");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.debug("Changing password for user '"+ username + "'");
|
||||
|
||||
User user = (User) loadUserByUsername(username);
|
||||
|
||||
user.setPassword(passwordEncoder.encode(newPassword));
|
||||
userRepository.save(user);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
User user = userRepository.findByUsername(username);
|
||||
if (user == null)
|
||||
throw new UsernameNotFoundException(String.format("No user found with username '%s'.", username));
|
||||
else
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package it.fabioformosa.quartzmanager.security.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.model.Authority;
|
||||
import it.fabioformosa.quartzmanager.security.model.User;
|
||||
import it.fabioformosa.quartzmanager.security.model.UserRequest;
|
||||
import it.fabioformosa.quartzmanager.security.repository.UserRepository;
|
||||
import it.fabioformosa.quartzmanager.security.service.AuthorityService;
|
||||
import it.fabioformosa.quartzmanager.security.service.UserService;
|
||||
|
||||
/**
|
||||
* Created by fan.jin on 2016-10-15.
|
||||
*/
|
||||
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Autowired
|
||||
private AuthorityService authService;
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public List<User> findAll() throws AccessDeniedException {
|
||||
List<User> result = userRepository.findAll();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public User findById(Long id) throws AccessDeniedException {
|
||||
User u = userRepository.findOne(id);
|
||||
return u;
|
||||
}
|
||||
|
||||
@Override
|
||||
// @PreAuthorize("hasRole('USER')")
|
||||
public User findByUsername(String username) throws UsernameNotFoundException {
|
||||
User u = userRepository.findByUsername(username);
|
||||
return u;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetCredentials() {
|
||||
List<User> users = userRepository.findAll();
|
||||
for (User user : users) {
|
||||
user.setPassword(passwordEncoder.encode("123"));
|
||||
userRepository.save(user);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User save(UserRequest userRequest) {
|
||||
User user = new User();
|
||||
user.setUsername(userRequest.getUsername());
|
||||
user.setPassword(passwordEncoder.encode(userRequest.getPassword()));
|
||||
user.setFirstname(userRequest.getFirstname());
|
||||
user.setLastname(userRequest.getLastname());
|
||||
List<Authority> auth = authService.findByname("ROLE_USER");
|
||||
user.setAuthorities(auth);
|
||||
this.userRepository.save(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
server.context-path=/quartz-manager
|
||||
server.port=9000
|
||||
server.port=8080
|
||||
server.session.timeout=28800
|
||||
|
||||
spring.thymeleaf.cache=false
|
||||
@@ -11,4 +11,10 @@ job.frequency=4000
|
||||
job.repeatCount=19
|
||||
|
||||
logging.level.org.springframework.web=WARN
|
||||
logging.level.it.fabioformosa=INFO
|
||||
logging.level.it.fabioformosa=INFO
|
||||
|
||||
app.name: quartz-manager
|
||||
jwt.header: Authorization
|
||||
jwt.expires_in: 600
|
||||
jwt.secret: queenvictoria
|
||||
jwt.cookie: AUTH-TOKEN
|
||||
8
quartz-manager-backend/src/main/resources/banner.txt
Normal file
8
quartz-manager-backend/src/main/resources/banner.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
____ _ __ __
|
||||
/ __ \ | | | \/ |
|
||||
| | | |_ _ __ _ _ __| |_ ____ | \ / | __ _ _ __ __ _ __ _ ___ _ __
|
||||
| | | | | | |/ _` | '__| __|_ / | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__|
|
||||
| |__| | |_| | (_| | | | |_ / / | | | | (_| | | | | (_| | (_| | __/ |
|
||||
\___\_\\__,_|\__,_|_| \__/___| |_| |_|\__,_|_| |_|\__,_|\__, |\___|_|
|
||||
__/ |
|
||||
|___/
|
||||
11
quartz-manager-backend/src/main/resources/import.sql
Normal file
11
quartz-manager-backend/src/main/resources/import.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
-- the password hash is generated by BCrypt Calculator Generator(https://www.dailycred.com/article/bcrypt-calculator)
|
||||
INSERT INTO user (id, username, password, firstname, lastname) VALUES (1, 'user', '$2a$04$Vbug2lwwJGrvUXTj6z7ff.97IzVBkrJ1XfApfGNl.Z695zqcnPYra', 'Fan', 'Jin');
|
||||
INSERT INTO user (id, username, password, firstname, lastname) VALUES (2, 'admin', '$2a$04$Vbug2lwwJGrvUXTj6z7ff.97IzVBkrJ1XfApfGNl.Z695zqcnPYra', 'Jing', 'Xiao');
|
||||
|
||||
INSERT INTO authority (id, name) VALUES (1, 'ROLE_USER');
|
||||
INSERT INTO authority (id, name) VALUES (2, 'ROLE_ADMIN');
|
||||
|
||||
INSERT INTO user_authority (user_id, authority_id) VALUES (1, 1);
|
||||
INSERT INTO user_authority (user_id, authority_id) VALUES (2, 1);
|
||||
INSERT INTO user_authority (user_id, authority_id) VALUES (2, 2);
|
||||
@@ -31,7 +31,7 @@ angular.module('progress')
|
||||
|
||||
var _handleNewMsgFromLogWebsocket = function(receivedMsg){
|
||||
if(receivedMsg.type == 'SUCCESS')
|
||||
_showNewLog(receivedMsg.message);
|
||||
_showNewLo g(receivedMsg.message);
|
||||
else if(receivedMsg.type == 'ERROR')
|
||||
_refreshSession(); //if websocket has been closed for session expiration, try to refresh it
|
||||
};
|
||||
57
quartz-manager-frontend/.angular-cli.json
Normal file
57
quartz-manager-frontend/.angular-cli.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"project": {
|
||||
"name": "angular-spring-starter"
|
||||
},
|
||||
"apps": [
|
||||
{
|
||||
"root": "src",
|
||||
"outDir": "../server/src/main/resources/static",
|
||||
"assets": [
|
||||
"assets",
|
||||
"favicon.ico"
|
||||
],
|
||||
"index": "index.html",
|
||||
"main": "main.ts",
|
||||
"polyfills": "polyfills.ts",
|
||||
"test": "test.ts",
|
||||
"tsconfig": "tsconfig.app.json",
|
||||
"testTsconfig": "tsconfig.spec.json",
|
||||
"prefix": "app",
|
||||
"styles": [
|
||||
"styles.css"
|
||||
],
|
||||
"scripts": [],
|
||||
"environmentSource": "environments/environment.ts",
|
||||
"environments": {
|
||||
"dev": "environments/environment.ts",
|
||||
"prod": "environments/environment.prod.ts"
|
||||
}
|
||||
}
|
||||
],
|
||||
"e2e": {
|
||||
"protractor": {
|
||||
"config": "./protractor.conf.js"
|
||||
}
|
||||
},
|
||||
"lint": [
|
||||
{
|
||||
"project": "src/tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"project": "src/tsconfig.spec.json"
|
||||
},
|
||||
{
|
||||
"project": "e2e/tsconfig.e2e.json"
|
||||
}
|
||||
],
|
||||
"test": {
|
||||
"karma": {
|
||||
"config": "./karma.conf.js"
|
||||
}
|
||||
},
|
||||
"defaults": {
|
||||
"styleExt": "css",
|
||||
"component": {}
|
||||
}
|
||||
}
|
||||
13
quartz-manager-frontend/.editorconfig
Normal file
13
quartz-manager-frontend/.editorconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
# Editor configuration, see http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
45
quartz-manager-frontend/.gitignore
vendored
Normal file
45
quartz-manager-frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# e2e
|
||||
/e2e/*.js
|
||||
/e2e/*.map
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
#package-lock.json
|
||||
package-lock.json
|
||||
14
quartz-manager-frontend/e2e/app.e2e-spec.ts
Normal file
14
quartz-manager-frontend/e2e/app.e2e-spec.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { WebUiPage } from './app.po';
|
||||
|
||||
describe('web-ui App', () => {
|
||||
let page: WebUiPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new WebUiPage();
|
||||
});
|
||||
|
||||
it('should display message saying app works', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getParagraphText()).toContain('ANGULAR-SPRING-JWT-STARTER');
|
||||
});
|
||||
});
|
||||
11
quartz-manager-frontend/e2e/app.po.ts
Normal file
11
quartz-manager-frontend/e2e/app.po.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { browser, element, by } from 'protractor';
|
||||
|
||||
export class WebUiPage {
|
||||
navigateTo() {
|
||||
return browser.get('/');
|
||||
}
|
||||
|
||||
getParagraphText() {
|
||||
return element(by.css('app-root app-header span')).getText();
|
||||
}
|
||||
}
|
||||
12
quartz-manager-frontend/e2e/tsconfig.e2e.json
Normal file
12
quartz-manager-frontend/e2e/tsconfig.e2e.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types":[
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
||||
44
quartz-manager-frontend/karma.conf.js
Normal file
44
quartz-manager-frontend/karma.conf.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/0.13/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular/cli'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular/cli/plugins/karma')
|
||||
],
|
||||
client:{
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
files: [
|
||||
{ pattern: './src/test.ts', watched: false }
|
||||
],
|
||||
preprocessors: {
|
||||
'./src/test.ts': ['@angular/cli']
|
||||
},
|
||||
mime: {
|
||||
'text/x-typescript': ['ts','tsx']
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
reports: [ 'html', 'lcovonly' ],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
angularCli: {
|
||||
environment: 'dev'
|
||||
},
|
||||
reporters: config.angularCli && config.angularCli.codeCoverage
|
||||
? ['progress', 'coverage-istanbul']
|
||||
: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false
|
||||
});
|
||||
};
|
||||
56
quartz-manager-frontend/package.json
Normal file
56
quartz-manager-frontend/package.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "quartz-manager-ui",
|
||||
"version": "0.1.1",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve --proxy-config proxy.conf.json",
|
||||
"build": "ng build",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "5.0.2",
|
||||
"@angular/common": "5.0.2",
|
||||
"@angular/compiler": "5.0.2",
|
||||
"@angular/core": "5.0.2",
|
||||
"@angular/forms": "5.0.2",
|
||||
"@angular/http": "5.0.2",
|
||||
"@angular/material": "5.0.0-rc.1",
|
||||
"@angular/cdk": "5.0.0-rc.1",
|
||||
"@angular/platform-browser": "5.0.2",
|
||||
"@angular/platform-browser-dynamic": "5.0.2",
|
||||
"@angular/platform-server": "5.0.2",
|
||||
"@angular/router": "5.0.2",
|
||||
"@angular/flex-layout": "2.0.0-beta.10-4905443",
|
||||
"core-js": "2.5.1",
|
||||
"hammerjs": "2.0.8",
|
||||
"rxjs": "5.5.2",
|
||||
"zone.js": "0.8.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/core": "^0.2.0",
|
||||
"@angular/cli": "1.5.3",
|
||||
"@angular/compiler-cli": "5.0.2",
|
||||
"@angular/language-service": "5.0.2",
|
||||
"@types/hammerjs": "2.0.34",
|
||||
"@types/jasmine": "2.5.54",
|
||||
"@types/jasminewd2": "2.0.3",
|
||||
"@types/node": "6.0.90",
|
||||
"codelyzer": "3.2.2",
|
||||
"jasmine-core": "2.6.4",
|
||||
"jasmine-spec-reporter": "4.1.1",
|
||||
"karma": "1.7.1",
|
||||
"karma-chrome-launcher": "2.1.1",
|
||||
"karma-cli": "1.0.1",
|
||||
"karma-coverage-istanbul-reporter": "1.3.0",
|
||||
"karma-jasmine": "1.1.0",
|
||||
"karma-jasmine-html-reporter": "0.2.2",
|
||||
"protractor": "5.1.2",
|
||||
"ts-node": "3.0.6",
|
||||
"tslint": "5.7.0",
|
||||
"typescript": "2.4.2"
|
||||
}
|
||||
}
|
||||
30
quartz-manager-frontend/protractor.conf.js
Normal file
30
quartz-manager-frontend/protractor.conf.js
Normal file
@@ -0,0 +1,30 @@
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: [
|
||||
'./e2e/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
print: function() {}
|
||||
},
|
||||
beforeLaunch: function() {
|
||||
require('ts-node').register({
|
||||
project: 'e2e/tsconfig.e2e.json'
|
||||
});
|
||||
},
|
||||
onPrepare() {
|
||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
}
|
||||
};
|
||||
6
quartz-manager-frontend/proxy.conf.json
Normal file
6
quartz-manager-frontend/proxy.conf.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"/quartz-manager": {
|
||||
"target": "http://localhost:8080",
|
||||
"secure": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<p>
|
||||
This is admin page!
|
||||
</p>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user