mirror of
https://github.com/fabioformosa/quartz-manager.git
synced 2025-12-31 22:53:16 +09:00
Handled session expiration
This commit is contained in:
@@ -40,6 +40,10 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf.extras</groupId>
|
||||
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-velocity</artifactId>
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.ViewResolver;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect;
|
||||
import org.thymeleaf.spring4.SpringTemplateEngine;
|
||||
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
|
||||
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
|
||||
@@ -19,12 +20,10 @@ public class MVCConfig extends WebMvcConfigurerAdapter {
|
||||
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/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");
|
||||
registry.addViewController("/templates/manager/logs-panel.html").setViewName("manager/logs-panel");
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +32,7 @@ public class MVCConfig extends WebMvcConfigurerAdapter {
|
||||
final SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
|
||||
springTemplateEngine.addTemplateResolver(templateResolver());
|
||||
springTemplateEngine.addDialect(new LayoutDialect());
|
||||
springTemplateEngine.addDialect(new SpringSecurityDialect());
|
||||
return springTemplateEngine;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
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;
|
||||
@@ -8,21 +10,24 @@ 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;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.AjaxAuthenticationFilter;
|
||||
import it.fabioformosa.quartzmanager.security.ComboEntryPoint;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Configuration
|
||||
@Order(1)
|
||||
public static class ApiWebSecurityConfig
|
||||
extends WebSecurityConfigurerAdapter {
|
||||
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();
|
||||
@@ -31,30 +36,30 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Configuration
|
||||
@Order(2)
|
||||
public static class FormWebSecurityConfig
|
||||
extends WebSecurityConfigurerAdapter {
|
||||
public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**",
|
||||
"/lib/**");
|
||||
}
|
||||
@Resource
|
||||
private ComboEntryPoint comboEntryPoint;
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().disable().authorizeRequests().anyRequest()
|
||||
.authenticated().and().formLogin().loginPage("/login")
|
||||
.permitAll().and().logout()
|
||||
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
|
||||
.logoutSuccessUrl("/manager");
|
||||
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/**");
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth.inMemoryAuthentication().withUser("admin").password("admin")
|
||||
.roles("ADMIN");
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package it.fabioformosa.quartzmanager.controllers;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/session")
|
||||
public class SessionController {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(SessionController.class);
|
||||
|
||||
@RequestMapping("/invalidate")
|
||||
@PreAuthorize("hasAuthority('ADMIN')")
|
||||
public String invalidateSession(HttpSession session) {
|
||||
session.invalidate();
|
||||
log.info("Invalidated current session!");
|
||||
return "redirect:/manager";
|
||||
}
|
||||
|
||||
@RequestMapping("/refresh")
|
||||
@PreAuthorize("hasAuthority('ADMIN')")
|
||||
public HttpEntity<Void> refreshSession(HttpSession session) {
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package it.fabioformosa.quartzmanager.security;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.WebAttributes;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
public class AjaxAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
|
||||
|
||||
public class AjaxLoginAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler
|
||||
implements AuthenticationSuccessHandler {
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication authentication) throws IOException, ServletException {
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
clearAuthenticationAttributes(request);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AjaxAuthenticationFilter(AuthenticationManager authenticationManager) {
|
||||
setAuthenticationManager(authenticationManager);
|
||||
setUsernameParameter("ajaxUsername");
|
||||
setPasswordParameter("ajaxPassword");
|
||||
setPostOnly(true);
|
||||
setFilterProcessesUrl("/ajaxLogin");
|
||||
|
||||
setAuthenticationSuccessHandler(new AjaxLoginAuthSuccessHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes temporary authentication-related data which may have been stored
|
||||
* in the session during the authentication process.
|
||||
*/
|
||||
protected final void clearAuthenticationAttributes(HttpServletRequest request) {
|
||||
HttpSession session = request.getSession(false);
|
||||
|
||||
if (session == null)
|
||||
return;
|
||||
|
||||
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package it.fabioformosa.quartzmanager.security;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ComboEntryPoint extends LoginUrlAuthenticationEntryPoint {
|
||||
|
||||
private static final String LOGIN_FORM_URL = "/login";
|
||||
|
||||
public ComboEntryPoint() {
|
||||
super(LOGIN_FORM_URL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException authException) throws IOException, ServletException {
|
||||
|
||||
if (RESTRequestMatcher.isRestRequest(request)
|
||||
|| WebsocketRequestMatcher.isWebsocketConnectionRequest(request))
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
|
||||
else
|
||||
super.commence(request, response, authException);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package it.fabioformosa.quartzmanager.security;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.web.util.matcher.ELRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
public class RESTRequestMatcher {
|
||||
|
||||
static private final Logger log = LoggerFactory.getLogger(RESTRequestMatcher.class);
|
||||
|
||||
static public RequestMatcher matcherRequestedWith = new ELRequestMatcher(
|
||||
"hasHeader('X-Requested-With','XMLHttpRequest')");
|
||||
static public RequestMatcher matcherAccept = new ELRequestMatcher(
|
||||
"hasHeader('accept','application/json, text/plain, */*')");
|
||||
|
||||
static public boolean isRestRequest(HttpServletRequest request) {
|
||||
log.trace("Detecting if it's an AJAX Request: " + request.getRequestURL() + " accept: "
|
||||
+ request.getHeader("accept") + " " + " X-Requested-With: "
|
||||
+ request.getHeader("X-Requested-With"));
|
||||
return matcherRequestedWith.matches(request) || matcherAccept.matches(request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package it.fabioformosa.quartzmanager.security;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class WebsocketRequestMatcher {
|
||||
|
||||
static private final Logger log = LoggerFactory.getLogger(WebsocketRequestMatcher.class);
|
||||
|
||||
static public boolean isWebsocketConnectionRequest(HttpServletRequest request) {
|
||||
log.trace("Detecting if it's a Websocket Connection Request: " + request.getRequestURL());
|
||||
return request.getServletPath().equals("/progress/info")
|
||||
|| request.getServletPath().equals("/logs/info");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
server.context-path=/quartz-manager
|
||||
server.port=9000
|
||||
server.session.timeout=28800
|
||||
|
||||
spring.thymeleaf.cache=false
|
||||
spring.thymeleaf.mode=LEGACYHTML5
|
||||
|
||||
@@ -1 +1 @@
|
||||
var schedulerApp = angular.module('schedulerApp', ['starter', 'configurator', 'ff-websocket', 'progress']);
|
||||
var schedulerApp = angular.module('schedulerApp', ['starter', 'configurator', 'ff-websocket', 'progress', 'http-auth-interceptor', 'authenticationComp']);
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
'use strict';
|
||||
var authenticationComp = angular.module('authenticationComp', ['http-auth-interceptor']);
|
||||
|
||||
|
||||
/**********************************************************************************/
|
||||
//SESSION REFRESH
|
||||
authenticationComp.directive('sessionRefresh',
|
||||
['authService','$rootScope', '$http', '$location',
|
||||
'LoginService', function(authService, $rootScope, $http, $location, LoginService) {
|
||||
var openedLoginDialog = false;
|
||||
return{
|
||||
restrict: 'AC',
|
||||
link: function(scope, elem, attrs) {
|
||||
|
||||
var lastRefreshTimeForAnonymous;
|
||||
|
||||
$rootScope.$on('event:auth-loginRequired', function(e, arg) {
|
||||
//show login dialog
|
||||
arg.ajaxLoginError = arg.ajaxLoginError || "";
|
||||
|
||||
attrs.title = attrs.title || "Session Expired";
|
||||
attrs.commonErrorLabel = attrs.commonErrorLabel || "Authenticaion failed:";
|
||||
attrs.wrongPasswordMsg = attrs.wrongPasswordMsg || "Wrong Password! Please, re-try.";
|
||||
attrs.serverConnectionFailedMsg = attrs.serverConnectionFailedMsg || "Server connection failed, please try later";
|
||||
|
||||
var username = attrs.username || "admin";
|
||||
|
||||
if(openedLoginDialog)
|
||||
return;
|
||||
|
||||
openedLoginDialog = true;
|
||||
bootbox.dialog({
|
||||
message: "<div id=\"refreshSessionDialog\">" +
|
||||
"<h4 id=\"refreshSessionHeader\">Login</h4>"+
|
||||
"<span id=\"refreshSessionError\">" + arg.ajaxLoginError + " </span>"+
|
||||
"<table id=\"refreshSessionTable\">" +
|
||||
"<tr>" +
|
||||
"<label for=\"username\" class=\"block clearfix\">" +
|
||||
"<span class=\"block input-icon input-icon-right\">" +
|
||||
"<input id=\"ajaxLoginUsername\" name=\"ajaxLoginUsername\" class=\"form-control\" type=\"text\" value=\"" + username + "\" readonly=\"true\" size=\"35\">" +
|
||||
"</span>" +
|
||||
"</label>" +
|
||||
"</tr>" +
|
||||
"<tr>" +
|
||||
"<label for=\"username\" class=\"block clearfix\">" +
|
||||
"<span class=\"block input-icon input-icon-right\">" +
|
||||
"<input id=\"ajaxLoginPassword\" name=\"ajaxLoginPassword\" class=\"form-control\" type=\"password\" placeholder=\"password\" size=\"35\">" +
|
||||
"</span>" +
|
||||
"</label>"+
|
||||
"</tr>" +
|
||||
"</table>" +
|
||||
"</div>",
|
||||
title: attrs.title,
|
||||
onEscape: function() {
|
||||
openedLoginDialog = false;
|
||||
},
|
||||
buttons:{
|
||||
main:{
|
||||
label: "Ok",
|
||||
className: "btn-primary",
|
||||
callback: function() {
|
||||
openedLoginDialog = false;
|
||||
var postData = {};
|
||||
postData.ajaxUsername = $('#ajaxLoginUsername').val();
|
||||
postData.ajaxPassword = $('#ajaxLoginPassword').val();
|
||||
|
||||
var loginCompleted = LoginService.doLogin(postData.ajaxUsername, postData.ajaxPassword, true);
|
||||
|
||||
loginCompleted
|
||||
.then(function(){
|
||||
authService.loginConfirmed();
|
||||
},function(msgError){
|
||||
$rootScope.$emit('event:auth-loginRequired', {ajaxLoginError: msgError});
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
$('#refreshSessionDialog').closest('.modal').css('z-index', '9001');
|
||||
});
|
||||
|
||||
$rootScope.$on('event:auth-loginConfirmed', function() {
|
||||
//nothing
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
/**********************************************************************************/
|
||||
//LOGIN SERVICE
|
||||
authenticationComp.service('LoginService', ['$q', '$http', '$window', '$log', function($q, $http, $window, $log){
|
||||
|
||||
this.doLogin = function(username, password, ignoreAuthModule){
|
||||
ignoreAuthModule = ignoreAuthModule || false;
|
||||
|
||||
var ajaxLoginDone = $q.defer();
|
||||
|
||||
var postData = {};
|
||||
postData.ajaxUsername = username;
|
||||
postData.ajaxPassword = password;
|
||||
|
||||
$http({
|
||||
method: 'POST',
|
||||
url: 'ajaxLogin',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
transformRequest: function(obj) {
|
||||
var str = [];
|
||||
for(var p in obj)
|
||||
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
|
||||
return str.join("&");
|
||||
},
|
||||
data: postData,
|
||||
ignoreAuthModule : ignoreAuthModule
|
||||
})
|
||||
.then(function (res) {
|
||||
if(res.status == 200){
|
||||
for(var i in res.cookies)
|
||||
if(res.cookies[i] != null)
|
||||
document.cookie = res.cookies[i];
|
||||
ajaxLoginDone.resolve(res.loggedUser);
|
||||
}
|
||||
else
|
||||
ajaxLoginDone.reject(res.msgError);
|
||||
}, function(data, status){
|
||||
ajaxLoginDone.reject();
|
||||
});
|
||||
|
||||
return ajaxLoginDone.promise;
|
||||
};
|
||||
|
||||
|
||||
}]);
|
||||
@@ -1,23 +1,15 @@
|
||||
angular.module('progress')
|
||||
.directive('logsPanel', ['LogService', function(LogService){
|
||||
|
||||
var appendLog = function(log){
|
||||
var logTable = document.getElementById('logTable');
|
||||
var row = document.createElement('tr');
|
||||
row.style.wordWrap = 'break-word';
|
||||
row.appendChild(document.createTextNode(log));
|
||||
logTable.appendChild(row);
|
||||
}
|
||||
|
||||
var MAX_LOGS = 10;
|
||||
|
||||
return{
|
||||
restrict: 'E',
|
||||
controller : ['$scope', function($scope){
|
||||
controller : ['$scope', '$rootScope', '$http', function($scope, $rootScope, $http){
|
||||
|
||||
$scope.logs = new Array();
|
||||
|
||||
LogService.receive().then(null, null, function(logRecord){
|
||||
var _showNewLog = function(logRecord){
|
||||
if($scope.logs.length > MAX_LOGS)
|
||||
$scope.logs.pop();
|
||||
|
||||
@@ -28,7 +20,31 @@ angular.module('progress')
|
||||
logItem.threadName = logRecord.threadName;
|
||||
|
||||
$scope.logs.unshift(logItem);
|
||||
})
|
||||
};
|
||||
|
||||
var _refreshSession = function(){
|
||||
$http({
|
||||
method : 'GET',
|
||||
url : 'session/refresh'
|
||||
});
|
||||
};
|
||||
|
||||
var _handleNewMsgFromLogWebsocket = function(receivedMsg){
|
||||
if(receivedMsg.type == 'SUCCESS')
|
||||
_showNewLog(receivedMsg.message);
|
||||
else if(receivedMsg.type == 'ERROR')
|
||||
_refreshSession(); //if websocket has been closed for session expiration, try to refresh it
|
||||
};
|
||||
|
||||
|
||||
LogService.receive().then(null, null, function(receivedMsg){
|
||||
_handleNewMsgFromLogWebsocket(receivedMsg);
|
||||
});
|
||||
|
||||
$rootScope.$on('event:auth-loginConfirmed', function() {
|
||||
//REST API login succeeded, now open websocket connection again
|
||||
LogService.reconnectNow();
|
||||
});
|
||||
}],
|
||||
templateUrl : 'templates/manager/logs-panel.html'
|
||||
};
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
angular.module('progress')
|
||||
.directive('progressPanel',['ProgressService', function(ProgressService){
|
||||
|
||||
return{
|
||||
restrict: 'E',
|
||||
controller : ['$scope', function($scope){
|
||||
.directive('progressPanel', [ 'ProgressService', function(ProgressService) {
|
||||
|
||||
return {
|
||||
restrict : 'E',
|
||||
controller : [ '$scope', '$rootScope', function($scope, $rootScope) {
|
||||
|
||||
ProgressService.receive().then(null, null, function(receivedMsg) {
|
||||
if (receivedMsg.type == 'SUCCESS') {
|
||||
var newStatus = receivedMsg.message;
|
||||
$scope.progress = newStatus;
|
||||
$scope.percentageStr = $scope.progress.percentage + '%';
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on('event:auth-loginConfirmed', function() {
|
||||
//re-open websocket connection after REST API login
|
||||
ProgressService.reconnectNow();
|
||||
});
|
||||
|
||||
}],
|
||||
|
||||
ProgressService.receive().then(null, null, function(newStatus){
|
||||
$scope.progress = newStatus;
|
||||
$scope.percentageStr = $scope.progress.percentage + '%';
|
||||
});
|
||||
|
||||
}],
|
||||
templateUrl : 'templates/manager/progress-panel.html'
|
||||
};
|
||||
}]);
|
||||
templateUrl : 'templates/manager/progress-panel.html'
|
||||
};
|
||||
} ]);
|
||||
@@ -29,6 +29,7 @@ angular.module('ff-websocket')
|
||||
|
||||
var getMessage = function(data) {
|
||||
var out = {};
|
||||
out.type = 'SUCCESS';
|
||||
out.message = JSON.parse(data.body);
|
||||
out.headers = {};
|
||||
out.headers.messageId = data.headers["message-id"];
|
||||
@@ -40,24 +41,42 @@ angular.module('ff-websocket')
|
||||
return out;
|
||||
};
|
||||
|
||||
that.reconnect = function() {
|
||||
$timeout(function() {
|
||||
initialize();
|
||||
that.reconnectionPromise = null;
|
||||
|
||||
that.scheduleReconnection = function() {
|
||||
that.reconnectionPromise = $timeout(function() {
|
||||
console.log("Socket reconnecting... (if it fails, next attempt in " + that.options.RECONNECT_TIMEOUT + " msec)");
|
||||
_initialize();
|
||||
}, that.options.RECONNECT_TIMEOUT);
|
||||
};
|
||||
|
||||
that.reconnectNow = function(){
|
||||
_socket.stomp.disconnect();
|
||||
if(that.reconnectionPromise && that.reconnectionPromise.cancel)
|
||||
that.reconnectionPromise.cancel();
|
||||
_initialize();
|
||||
};
|
||||
|
||||
var _errorCallback = function(errorMsg){
|
||||
var out = {};
|
||||
out.type = 'ERROR';
|
||||
out.message = errorMsg;
|
||||
_deferred.notify(out);
|
||||
that.scheduleReconnection();
|
||||
};
|
||||
|
||||
var _startListener = function(frame){
|
||||
console.log('Connected: ' + frame);
|
||||
_socket.stomp.subscribe(that.options.TOPIC_NAME, function(data){
|
||||
_deferred.notify(getMessage(data).message);
|
||||
_deferred.notify(getMessage(data));
|
||||
});
|
||||
};
|
||||
|
||||
var _initialize = function(){
|
||||
_socket.client = new SockJS(that.options.SOCKET_URL);
|
||||
_socket.stomp = Stomp.over(_socket.client);
|
||||
_socket.stomp.connect({}, _startListener);
|
||||
_socket.stomp.onclose = that.reconnect;
|
||||
_socket.stomp.connect({}, _startListener, _errorCallback);
|
||||
_socket.stomp.onclose = that.scheduleReconnection;
|
||||
};
|
||||
|
||||
that.receive = function(){
|
||||
|
||||
6
quartz-manager/src/main/resources/static/js/lib/bootbox.min.js
vendored
Normal file
6
quartz-manager/src/main/resources/static/js/lib/bootbox.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,141 @@
|
||||
/*global angular:true, browser:true */
|
||||
|
||||
/**
|
||||
* @license HTTP Auth Interceptor Module for AngularJS
|
||||
* (c) 2012 Witold Szczerba
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular.module('http-auth-interceptor', ['http-auth-interceptor-buffer'])
|
||||
|
||||
.factory('authService', ['$rootScope','httpBuffer', function($rootScope, httpBuffer) {
|
||||
return {
|
||||
/**
|
||||
* Call this function to indicate that authentication was successful and trigger a
|
||||
* retry of all deferred requests.
|
||||
* @param data an optional argument to pass on to $broadcast which may be useful for
|
||||
* example if you need to pass through details of the user that was logged in
|
||||
* @param configUpdater an optional transformation function that can modify the
|
||||
* requests that are retried after having logged in. This can be used for example
|
||||
* to add an authentication token. It must return the request.
|
||||
*/
|
||||
loginConfirmed: function(data, configUpdater) {
|
||||
var updater = configUpdater || function(config) {return config;};
|
||||
$rootScope.$broadcast('event:auth-loginConfirmed', data);
|
||||
httpBuffer.retryAll(updater);
|
||||
},
|
||||
|
||||
/**
|
||||
* Call this function to indicate that authentication should not proceed.
|
||||
* All deferred requests will be abandoned or rejected (if reason is provided).
|
||||
* @param data an optional argument to pass on to $broadcast.
|
||||
* @param reason if provided, the requests are rejected; abandoned otherwise.
|
||||
*/
|
||||
loginCancelled: function(data, reason) {
|
||||
httpBuffer.rejectAll(reason);
|
||||
$rootScope.$broadcast('event:auth-loginCancelled', data);
|
||||
}
|
||||
};
|
||||
}])
|
||||
|
||||
/**
|
||||
* $http interceptor.
|
||||
* On 401 response (without 'ignoreAuthModule' option) stores the request
|
||||
* and broadcasts 'event:auth-loginRequired'.
|
||||
* On 403 response (without 'ignoreAuthModule' option) discards the request
|
||||
* and broadcasts 'event:auth-forbidden'.
|
||||
*/
|
||||
.config(['$httpProvider', function($httpProvider) {
|
||||
$httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer', function($rootScope, $q, httpBuffer) {
|
||||
return {
|
||||
responseError: function(rejection) {
|
||||
var config = rejection.config || {};
|
||||
if (!config.ignoreAuthModule) {
|
||||
switch (rejection.status) {
|
||||
case 401:
|
||||
var deferred = $q.defer();
|
||||
var bufferLength = httpBuffer.append(config, deferred);
|
||||
if (bufferLength === 1)
|
||||
$rootScope.$broadcast('event:auth-loginRequired', rejection);
|
||||
return deferred.promise;
|
||||
case 403:
|
||||
$rootScope.$broadcast('event:auth-forbidden', rejection);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// otherwise, default behaviour
|
||||
return $q.reject(rejection);
|
||||
}
|
||||
};
|
||||
}]);
|
||||
}]);
|
||||
|
||||
/**
|
||||
* Private module, a utility, required internally by 'http-auth-interceptor'.
|
||||
*/
|
||||
angular.module('http-auth-interceptor-buffer', [])
|
||||
|
||||
.factory('httpBuffer', ['$injector', function($injector) {
|
||||
/** Holds all the requests, so they can be re-requested in future. */
|
||||
var buffer = [];
|
||||
|
||||
/** Service initialized later because of circular dependency problem. */
|
||||
var $http;
|
||||
|
||||
function retryHttpRequest(config, deferred) {
|
||||
function successCallback(response) {
|
||||
deferred.resolve(response);
|
||||
}
|
||||
function errorCallback(response) {
|
||||
deferred.reject(response);
|
||||
}
|
||||
$http = $http || $injector.get('$http');
|
||||
$http(config).then(successCallback, errorCallback);
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* Appends HTTP request configuration object with deferred response attached to buffer.
|
||||
* @return {Number} The new length of the buffer.
|
||||
*/
|
||||
append: function(config, deferred) {
|
||||
return buffer.push({
|
||||
config: config,
|
||||
deferred: deferred
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Abandon or reject (if reason provided) all the buffered requests.
|
||||
*/
|
||||
rejectAll: function(reason) {
|
||||
if (reason) {
|
||||
for (var i = 0; i < buffer.length; ++i) {
|
||||
buffer[i].deferred.reject(reason);
|
||||
}
|
||||
}
|
||||
buffer = [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Retries all the buffered requests clears the buffer.
|
||||
*/
|
||||
retryAll: function(updater) {
|
||||
for (var i = 0; i < buffer.length; ++i) {
|
||||
var _cfg = updater(buffer[i].config);
|
||||
if (_cfg !== false)
|
||||
retryHttpRequest(_cfg, buffer[i].deferred);
|
||||
}
|
||||
buffer = [];
|
||||
}
|
||||
};
|
||||
}]);
|
||||
})();
|
||||
|
||||
/* commonjs package manager support (eg componentjs) */
|
||||
if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
|
||||
module.exports = 'http-auth-interceptor';
|
||||
}
|
||||
1
quartz-manager/src/main/resources/static/js/lib/http-auth-interceptor.min.js
vendored
Normal file
1
quartz-manager/src/main/resources/static/js/lib/http-auth-interceptor.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!function(){"use strict";angular.module("http-auth-interceptor",["http-auth-interceptor-buffer"]).factory("authService",["$rootScope","httpBuffer",function($rootScope,httpBuffer){return{loginConfirmed:function(data,configUpdater){var updater=configUpdater||function(config){return config};$rootScope.$broadcast("event:auth-loginConfirmed",data),httpBuffer.retryAll(updater)},loginCancelled:function(data,reason){httpBuffer.rejectAll(reason),$rootScope.$broadcast("event:auth-loginCancelled",data)}}}]).config(["$httpProvider",function($httpProvider){$httpProvider.interceptors.push(["$rootScope","$q","httpBuffer",function($rootScope,$q,httpBuffer){return{responseError:function(rejection){var config=rejection.config||{};if(!config.ignoreAuthModule)switch(rejection.status){case 401:var deferred=$q.defer(),bufferLength=httpBuffer.append(config,deferred);return 1===bufferLength&&$rootScope.$broadcast("event:auth-loginRequired",rejection),deferred.promise;case 403:$rootScope.$broadcast("event:auth-forbidden",rejection)}return $q.reject(rejection)}}}])}]),angular.module("http-auth-interceptor-buffer",[]).factory("httpBuffer",["$injector",function($injector){function retryHttpRequest(config,deferred){function successCallback(response){deferred.resolve(response)}function errorCallback(response){deferred.reject(response)}$http=$http||$injector.get("$http"),$http(config).then(successCallback,errorCallback)}var $http,buffer=[];return{append:function(config,deferred){return buffer.push({config:config,deferred:deferred})},rejectAll:function(reason){if(reason)for(var i=0;i<buffer.length;++i)buffer[i].deferred.reject(reason);buffer=[]},retryAll:function(updater){for(var i=0;i<buffer.length;++i){var _cfg=updater(buffer[i].config);_cfg!==!1&&retryHttpRequest(_cfg,buffer[i].deferred)}buffer=[]}}}])}(),"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="http-auth-interceptor");
|
||||
@@ -4,7 +4,7 @@
|
||||
<head lang="en" th:fragment="head">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
|
||||
<link href="http://cdn.jsdelivr.net/webjars/bootstrap/3.3.4/css/bootstrap.min.css"
|
||||
<link href="http://cdn.jsdelivr.net/webjars/bootstrap/3.3.7/css/bootstrap.min.css"
|
||||
rel="stylesheet" media="screen" />
|
||||
|
||||
<link th:href="@{css/animate.css}" rel="stylesheet" />
|
||||
@@ -15,10 +15,16 @@
|
||||
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"/>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
|
||||
<script src="http://cdn.jsdelivr.net/webjars/bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
||||
|
||||
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||
|
||||
<script th:src="@{/js/lib/bootbox.min.js}"></script>
|
||||
|
||||
<script th:src="@{/js/lib/sockjs-0.3.4.js}"></script>
|
||||
<script th:src="@{/js/lib/stomp.js}"></script>
|
||||
<script th:src="@{/js/lib/http-auth-interceptor.min.js}"></script>
|
||||
|
||||
<script th:src="@{/js/app/components/starter/starter-module.js}"></script>
|
||||
<script th:src="@{/js/app/components/starter/starter-directives.js}"></script>
|
||||
@@ -33,6 +39,9 @@
|
||||
<script th:src="@{/js/app/components/progress/progress-directive.js}"></script>
|
||||
<script th:src="@{/js/app/components/progress/logs-service.js}"></script>
|
||||
<script th:src="@{/js/app/components/progress/logs-directive.js}"></script>
|
||||
|
||||
<script th:src="@{/js/app/components/authentication/authentication.js}"></script>
|
||||
|
||||
<script th:src="@{/js/app/app.js}"></script>
|
||||
|
||||
</head>
|
||||
|
||||
@@ -57,5 +57,21 @@
|
||||
<th:block layout:fragment="script"></th:block>
|
||||
</div><!-- row -->
|
||||
</div> <!-- container-fluid -->
|
||||
|
||||
<!-- REFRESH SESSION DIALOG -->
|
||||
<div id="refreshSessionBox"
|
||||
data-session-refresh="active"
|
||||
data-ajax-login-server='/'
|
||||
th:attr="data-username=${#authentication.name}"
|
||||
data-title="Session Expired"
|
||||
data-common-error-label="authentication failed!"
|
||||
data-wrong-password-msg="Wrong password! Please, re-try."
|
||||
data-server-connection-failed-msg="Unexpected error! Server connection failed. Please, try later."
|
||||
data-relogin-failed-msg="Session refreshing failed! Please, refresh current page to continue"
|
||||
data-relogin-label="Authentication"
|
||||
style="display:none;"
|
||||
>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user