first commit

This commit is contained in:
fabio.formosa
2018-02-13 00:10:07 +01:00
parent 6b4506aed3
commit 0b4dc4a1de
203 changed files with 4572 additions and 126 deletions

View File

@@ -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>

View File

@@ -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");
}
}

View File

@@ -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);
}
}

View File

@@ -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");
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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 );
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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 );
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -0,0 +1,8 @@
____ _ __ __
/ __ \ | | | \/ |
| | | |_ _ __ _ _ __| |_ ____ | \ / | __ _ _ __ __ _ __ _ ___ _ __
| | | | | | |/ _` | '__| __|_ / | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__|
| |__| | |_| | (_| | | | |_ / / | | | | (_| | | | | (_| | (_| | __/ |
\___\_\\__,_|\__,_|_| \__/___| |_| |_|\__,_|_| |_|\__,_|\__, |\___|_|
__/ |
|___/

View 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);

View File

@@ -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
};

View 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": {}
}
}

View 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
View 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

View 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');
});
});

View 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();
}
}

View File

@@ -0,0 +1,12 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es5",
"types":[
"jasmine",
"node"
]
}
}

View 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
});
};

View 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"
}
}

View 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 } }));
}
};

View File

@@ -0,0 +1,6 @@
{
"/quartz-manager": {
"target": "http://localhost:8080",
"secure": false
}
}

View File

@@ -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