Commit 9d22d5e7 authored by Matija Obreza's avatar Matija Obreza

Merge branch 'http-sessions' into 'master'

Http sessions

See merge request genesys-pgr/genesys-server!548
parents 5324dcb7 aec5e352
/*
* Copyright 2019 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys2.server.mvc.admin;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* The Class ThreadInfoController.
*/
@Controller
@RequestMapping("/admin")
@PreAuthorize("hasRole('ADMINISTRATOR')")
public class ThreadInfoController {
public static final Logger LOG = LoggerFactory.getLogger(ThreadInfoController.class);
@PostMapping(value = "/action", params = "action=threaddump", produces = { MediaType.TEXT_PLAIN_VALUE })
public void threadDump(HttpServletResponse response) throws IOException {
response.setContentType(MediaType.TEXT_PLAIN_VALUE);
LOG.warn("Dumping thread info");
PrintWriter writer = response.getWriter();
writer.println("Thread dump:\t" + new Date());
writer.println("Hostname:\t" + java.net.InetAddress.getLocalHost().getHostName());
writer.println("CPUs:\t" + Runtime.getRuntime().availableProcessors());
writer.println("Free memory:\t" + Runtime.getRuntime().freeMemory());
writer.println("Max memory:\t" + Runtime.getRuntime().maxMemory());
writer.println("Total memory:\t" + Runtime.getRuntime().totalMemory());
writer.println();
Map<Thread, StackTraceElement[]> threadSet = Thread.getAllStackTraces();
ArrayList<Thread> sortedThreads = new ArrayList<>(threadSet.keySet());
sortedThreads.sort((t1, t2) -> t1.getName().compareTo(t2.getName()));
writer.println("\n\n*** Thread list ***\n");
for (Thread t : sortedThreads) {
writer.println(t.getName());
}
writer.println();
writer.flush();
writer.println("\n\n*** Threads ***\n");
writer.println("ID\tState\tName\tGroup");
for (Thread t : sortedThreads) {
writer.print(t.getId());
writer.print("\t");
writer.print(t.getState());
writer.print("\t");
writer.print(t.getName());
writer.print("\t");
ThreadGroup threadGroup = t.getThreadGroup();
writer.print(threadGroup == null ? "N/A" : threadGroup.getName());
writer.println();
StackTraceElement[] ste = threadSet.get(t);
Arrays.stream(ste).forEach((st) -> {
writer.print(t.getId());
writer.print("\t");
writer.print(st.getClassName());
writer.print(":");
writer.print(st.getMethodName());
writer.print("\t");
writer.print(st.getFileName());
writer.print("\t");
writer.println();
});
writer.println();
writer.flush();
}
ThreadMXBean tmxb = ManagementFactory.getThreadMXBean();
if (tmxb != null && tmxb.isThreadCpuTimeSupported()) {
writer.println("\n\n*** CPU Usage ***\n");
writer.println("ID\tCPU%\tCPU[ms]\tUser[ms]\tState\tName\tGroup");
Map<Long, ThreadCpuUsage> cpuUsage = findCpuUsage(tmxb, sortedThreads);
sortedThreads.sort((t1, t2) -> (int)(cpuUsage.get(t2.getId()).cpuTime - cpuUsage.get(t1.getId()).cpuTime));
for (Thread t : sortedThreads) {
writer.print(t.getId());
writer.print("\t");
ThreadCpuUsage cpu = cpuUsage.get(t.getId());
writer.printf("%.4f", cpu.utilization * 100);
writer.print("\t");
writer.printf("%.2f", cpu.cpuTime / 1000f);
writer.print("\t");
writer.printf("%.2f", cpu.userTime / 1000f);
writer.printf("\t");
writer.print(t.getState());
writer.print("\t");
writer.print(t.getName());
writer.print("\t");
ThreadGroup threadGroup = t.getThreadGroup();
writer.print(threadGroup == null ? "N/A" : threadGroup.getName());
writer.println();
writer.flush();
}
}
writer.flush();
}
/**
* @param tmxb
* @param threads2
* @return
*/
private Map<Long, ThreadCpuUsage> findCpuUsage(ThreadMXBean tmxb, List<? extends Thread> threads) {
Map<Long, ThreadCpuUsage> cpu = new Hashtable<Long, ThreadCpuUsage>();
for (int i = 0; i < threads.size(); i++) {
Thread thread = threads.get(i);
long threadId = thread.getId();
ThreadCpuUsage cpuUsage = new ThreadCpuUsage();
cpuUsage.cpuTime1 = tmxb.getThreadCpuTime(threadId);
cpuUsage.userTime1 = tmxb.getThreadUserTime(threadId);
cpuUsage.time1 = System.currentTimeMillis();
cpu.put(threadId, cpuUsage);
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
LOG.warn("Interrupted from sleep: " + e.getMessage());
}
for (int i = 0; i < threads.size(); i++) {
Thread thread = threads.get(i);
long threadId = thread.getId();
ThreadCpuUsage cpuUsage = cpu.get(threadId);
cpuUsage.cpuTime2 = tmxb.getThreadCpuTime(threadId);
cpuUsage.userTime2 = tmxb.getThreadUserTime(threadId);
cpuUsage.time2 = System.currentTimeMillis();
cpuUsage.cpuTime = cpuUsage.cpuTime2 - cpuUsage.cpuTime1;
cpuUsage.userTime = cpuUsage.userTime2 - cpuUsage.userTime1;
cpuUsage.time = cpuUsage.time2 - cpuUsage.time1;
// if (cpuUsage.cpuTime > 0) {
// cpuUsage.threadInfo = tmxb.getThreadInfo(threadId, 50);
// }
cpuUsage.utilization = (cpuUsage.cpuTime) / ((cpuUsage.time) * 1000000F);
}
return cpu;
}
private class ThreadCpuUsage {
// public ThreadInfo threadInfo;
public long userTime1, userTime2, userTime;
public long cpuTime1, cpuTime2, cpuTime;
public long time1, time2, time;
public double utilization;
}
}
/**
* Copyright 2015 Global Crop Diversity Trust
/*
* Copyright 2019 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -12,36 +12,62 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
*/
package org.genesys2.spring;
import java.util.Arrays;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Enable this {@link WebListener} to figure out what initates the HTTP Session.
*
*
* <ul>
* <li>init.jsp: session="false"</li>
* <li>_csrf.getToken() then creates the session anyway in decorators -- this is
* used for AJAX requests</li>
* </ul>
*
* @author Matija Obreza
*/
// @WebListener
@WebListener
public class WhoMakesTheJSessionId implements HttpSessionListener {
public static final Logger LOG = LoggerFactory.getLogger(WhoMakesTheJSessionId.class);
public WhoMakesTheJSessionId() {
System.err.println("WhoMakesTheJSessionId listener instantiated.");
}
@Override
public void sessionCreated(HttpSessionEvent se) {
throw new UnsupportedOperationException("Sessions are not allowed");
public void sessionCreated(final HttpSessionEvent se) {
LOG.warn("HTTP Session {} created", se.getSession().getId());
if (LOG.isTraceEnabled()) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
StringBuilder sb= new StringBuilder();
Arrays.stream(stackTrace).forEach((st) -> {
sb.append(Thread.currentThread().getName());
sb.append("\t");
sb.append(st.getClassName());
sb.append(":");
sb.append(st.getMethodName());
sb.append("(");
sb.append(st.getFileName());
sb.append(":");
sb.append(st.getLineNumber());
sb.append(")\n");
});
LOG.trace("Session {} created in:\n{}", se.getSession().getId(), sb.toString());
}
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
throw new UnsupportedOperationException("Sessions are not allowed");
public void sessionDestroyed(final HttpSessionEvent se) {
LOG.warn("Session {} destroyed", se.getSession().getId());
}
}
\ No newline at end of file
......@@ -156,11 +156,11 @@ public class OAuth2ServerConfig {
// authorize everything on this path
.authorizeRequests().antMatchers("/**").fullyAuthenticated()
// no sessions
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().requestMatchers().antMatchers("/api/**", "/brapi/**")
// no sessions
.and().sessionManagement().disable()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// no CSRF
.csrf().disable()
......
......@@ -32,8 +32,10 @@ import org.genesys2.server.servlet.filter.SuppressRequestRejectedExceptionFilter
import org.sitemesh.builder.SiteMeshFilterBuilder;
import org.sitemesh.config.ConfigurableSiteMeshFilter;
import org.sitemesh.webapp.contentfilter.BasicSelector;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.CommonsRequestLoggingFilter;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
......@@ -91,7 +93,23 @@ public class WebInitializer extends AbstractAnnotationConfigDispatcherServletIni
servletContext.addListener(new SessionListener());
}
@Bean
public CommonsRequestLoggingFilter requestLoggingFilter() {
CommonsRequestLoggingFilter loggingFilter = new CommonsRequestLoggingFilter();
loggingFilter.setIncludeClientInfo(true);
loggingFilter.setIncludeQueryString(true);
loggingFilter.setIncludePayload(true);
loggingFilter.setMaxPayloadLength(100);
loggingFilter.setBeforeMessagePrefix("START ");
loggingFilter.setAfterMessagePrefix("DONE ");
return loggingFilter;
}
private void registerFilters(final ServletContext servletContext) {
final FilterRegistration.Dynamic loggingFilter = servletContext.addFilter("requestLoggingFilter", requestLoggingFilter());
loggingFilter.addMappingForUrlPatterns(null, false, "/*");
final FilterRegistration.Dynamic customLogFilter = servletContext.addFilter("suppressRequestRejectedExceptionFilter", SuppressRequestRejectedExceptionFilter.class);
customLogFilter.addMappingForUrlPatterns(null, false, "/*");
......
......@@ -25,6 +25,7 @@ import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
......@@ -57,7 +58,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/*@formatter:off*/
http
// No JSESSIONID in URL
.sessionManagement().enableSessionUrlRewriting(false).sessionFixation().migrateSession()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED).enableSessionUrlRewriting(false).sessionFixation().migrateSession()
// Authorizations
.and().authorizeRequests()
......
......@@ -29,6 +29,7 @@ log4j.rootLogger=error, stdout
log4j.category.org.genesys2=warn
log4j.category.org.genesys=warn
log4j.category.org.genesys2.server.api=warn
log4j.category.org.springframework.web.filter.CommonsRequestLoggingFilter=INFO
#log4j.category.org.genesys.custom.elasticsearch=info
#log4j.category.org.genesys2.server.service.impl.ElasticsearchServiceImpl=debug
#log4j.category.org.genesys2.server.service.impl.KPIServiceImpl=trace
......
......@@ -99,7 +99,3 @@
<!--/.nav-collapse -->
</div>
</nav>
<form id="logoutForm" action="/logout" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
......@@ -12,11 +12,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="language" content="${pageContext.response.locale.language}" />
<!-- CSRF protection-->
<meta name="_csrf" content="${_csrf.token}" />
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" content="${_csrf.headerName}" />
<!-- Links -->
<link rel="shortcut icon" href="<c:url value="/html/1/images/genesys.png" />" />
......
......@@ -9,6 +9,14 @@
</head>
<body>
<h3>Toolkit</h3>
<form method="post" action="<c:url value="/admin/action" />">
<input type="submit" class="btn btn-default" name="action" value="threaddump" />
<!-- CSRF protection -->
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
<h3>Updates</h3>
<form method="post" action="<c:url value="/admin/admin-action" />">
<input type="submit" class="btn btn-default" name="accenumbnumb" value="ACCENUMB-NUMB" />
<!-- CSRF protection -->
......
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" language="java" %>
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" language="java" session="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
......
......@@ -8,21 +8,7 @@
</a>
<ul class="dropdown-menu pull-left">
<li>
<form id="loginForm" role="form" method="post" action="<c:url value="/login-attempt" />">
<div class="form-group">
<label class="pull-left" for="username"><spring:message code="login.username" /></label>
<input type="email" class="form-control" id="username" name="username" placeholder="<spring:message code="login.username"/>" />
</div>
<div class="form-group">
<label class="pull-left" for="password"><spring:message code="login.password" /></label>
<input type="password" class="form-control" id="password" name="password" placeholder="<spring:message code="login.password"/>" />
</div>
<div class="checkbox">
<label class="pull-left">
<input type="checkbox" name="_spring_security_remember_me" id="_spring_security_remember_me" />
<spring:message code="login.remember-me" />
</label>
</div>
<form id="loginForm" role="form" method="get" action="<c:url value="/login" />">
<button type="submit" class="btn btn-primary"><spring:message code="login.login-button" /></button>
<span class="or">-</span>
<a href="<c:url value="/google/login" />" class="btn btn-default google-signin">
......@@ -31,9 +17,7 @@
<a href="<c:url value="/registration" />" class="btn btn-default">
<spring:message code="login.register-now" />
</a>
<!-- CSRF protection -->
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</form>
</li>
</ul>
</li>
......@@ -33,5 +33,8 @@
</li>
</security:authorize>
</ul>
</li>
</li>
<form id="logoutForm" action="/logout" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</security:authorize>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment