Commit ebcf4e56 authored by Matija Obreza's avatar Matija Obreza

Merge branch 'graylog' into 'master'

GELF

See merge request genesys-pgr/genesys-server!509
parents 04d1405d 32656bf3
......@@ -141,7 +141,13 @@
<!-- Embedded in jcl-over-slf4j -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.graylog2</groupId>
<artifactId>gelfj</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
......
/*
* 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.api.v2;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import org.genesys.catalog.service.ShortFilterService;
import org.genesys2.server.service.MappingService;
import org.genesys2.server.service.filter.AccessionFilter;
import org.genesys2.util.ColorUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* This is an API controller, it's not accessible to anonymous users.
*
* @author Matija Obreza
*/
@Controller("tileControllerApi2")
@RequestMapping(TileController.CONTROLLER_URL)
public class TileController {
/** The Constant CONTROLLER_URL. */
public static final String CONTROLLER_URL = ApiBaseController.APIv2_BASE + "/tile";
@Autowired
private MappingService mappingService;
@Autowired
private ShortFilterService shortFilterService;
@RequestMapping(value = "/{zoom:\\d+}/{x:\\d+}/{y:\\d+}", produces = MediaType.IMAGE_PNG_VALUE)
public void tile(@RequestParam(value = "f", required = false, defaultValue="") String filterCode,
@RequestParam(value = "color", required = false) String color,
@PathVariable("x") int x, @PathVariable("y") int y, @PathVariable("zoom") int zoom,
HttpServletResponse response) throws IOException {
AccessionFilter appliedFilters = shortFilterService.filterByCode(filterCode, AccessionFilter.class);
byte[] image = mappingService.getTile(appliedFilters, zoom, x, y);
image = ColorUtil.changeColor(color, image);
response.setContentType(MediaType.IMAGE_PNG_VALUE);
response.setContentLength(image.length);
// HTTP maxAge
int maxAge;
if (zoom < 4) {
maxAge = 60 * 60 * 24; // 24 hrs
} else if (zoom < 6) {
maxAge = 60 * 60 * 2; // 2 hrs
} else if (zoom < 8) {
maxAge = 60 * 60; // 1 hr
} else {
maxAge = 60 * 5; // 5 min
}
response.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=" + maxAge + ", s-maxage=" + maxAge + ", public, no-transform");
response.getOutputStream().write(image, 0, image.length);
response.getOutputStream().flush();
}
}
/*
* 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.servlet.filter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Level;
import org.graylog2.GelfMessage;
import org.graylog2.GelfSender;
import org.json.simple.JSONValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.web.filter.OncePerRequestFilter;
/**
* Log API request details using GELF.
*
* @author Matija Obreza
*/
public class ApiAccessLoggerFilter extends OncePerRequestFilter {
@Autowired(required = false)
private GelfSender gelfSender;
@Value("${host.nameAndPort}")
private String originHost;
@Value("${gelf.additionalFields}")
private String additionalFieldsStr;
@Value("${gelf.logCustomHeaders}")
private boolean logCustomHeaders;
private Map<String, Object> additionalFields = null;
@Override
@SuppressWarnings("unchecked")
public void afterPropertiesSet() throws ServletException {
super.afterPropertiesSet();
System.err.println("GELFSender: " + gelfSender);
if (StringUtils.isNotBlank(this.additionalFieldsStr)) {
this.additionalFields = (Map<String, Object>) JSONValue.parse(this.additionalFieldsStr.replaceAll("'", "\""));
}
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (gelfSender == null) {
filterChain.doFilter(request, response);
return;
}
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
OAuth2Authentication oauthAuth = null;
if (authentication instanceof OAuth2Authentication) {
oauthAuth = (OAuth2Authentication) authentication;
}
long startTime = System.currentTimeMillis();
filterChain.doFilter(request, response);
long processingTime = System.currentTimeMillis() - startTime;
try {
String requestUrl = request.getRequestURI();
StringBuilder shortMessage = new StringBuilder();
shortMessage.append(request.getMethod()).append(" ").append(requestUrl);
StringBuilder fullMessage = new StringBuilder();
fullMessage.append(request.getMethod()).append(" ").append(requestUrl).append(" ");
if (response.getStatus() == 200) {
fullMessage.append(" OK");
}
fullMessage.append(". ").append(processingTime).append("ms.");
GelfMessage message = new GelfMessage(shortMessage.toString(), null, startTime, Level.INFO.toString());
message.setAdditonalFields(additionalFields);
message.setFacility("API");
message.setHost(originHost);
message.addField("httpMethod", request.getMethod());
message.addField("endpoint", requestUrl);
message.addField("qs", request.getQueryString());
message.addField("contentType", request.getContentType());
message.addField("encoding", request.getCharacterEncoding());
message.addField("origin", request.getHeader(HttpHeaders.ORIGIN));
message.addField("referrer", request.getHeader(HttpHeaders.REFERER));
message.addField("ua", request.getHeader(HttpHeaders.USER_AGENT));
message.addField("lang", request.getHeader(HttpHeaders.ACCEPT_LANGUAGE));
message.addField("timeTakenMs", processingTime);
message.addField("uploaded", request.getContentLength());
message.addField("status", response.getStatus());
message.addField("remoteAddress", request.getRemoteAddr());
if (this.logCustomHeaders) {
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String header = headerNames.nextElement();
if (StringUtils.startsWithIgnoreCase(header, "X-")) {
message.addField(header, request.getHeader(header));
}
}
}
if (oauthAuth != null) {
message.addField("clientId", oauthAuth.getOAuth2Request().getClientId());
fullMessage.append(" ").append(oauthAuth.getOAuth2Request().getClientId());
Object details = oauthAuth.getDetails();
if (details != null) {
OAuth2AuthenticationDetails oauthDetails = (OAuth2AuthenticationDetails) details;
message.addField("remoteAddress", oauthDetails.getRemoteAddress());
}
if (oauthAuth.isClientOnly()) {
} else {
Authentication userAuth = oauthAuth.getUserAuthentication();
if (userAuth != null) {
// Object principal = userAuth.getPrincipal();
// if (principal != null) {
// System.err.println("User principal " + principal.getClass());
// }
message.addField("user", userAuth.getName());
fullMessage.append("/").append(userAuth.getName());
}
}
} else {
message.addField("clientId", "None");
fullMessage.append(" No client.");
}
message.setFullMessage(fullMessage.toString());
gelfSender.sendMessage(message);
} catch (
Throwable e) {
System.err.println(e.getMessage());
}
}
}
......@@ -50,7 +50,7 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
// Scan for services, components and aspects
@ComponentScan(basePackages= { "org.genesys2.server.security", "org.genesys2.server.service", "org.genesys2.server.component", "org.genesys2.brapi.service", "org.genesys.catalog.service" })
@Import({ HazelcastConfig.class, CommonConfig.class, SchedulerConfig.class, DatabaseConfig.class, TemplatingConfig.class, MailConfig.class, OAuth2ServerConfig.class, SecurityConfig.class, CacheConfig.class,
@Import({ GelfConfig.class, HazelcastConfig.class, CommonConfig.class, SchedulerConfig.class, DatabaseConfig.class, TemplatingConfig.class, MailConfig.class, OAuth2ServerConfig.class, SecurityConfig.class, CacheConfig.class,
ElasticsearchConfig.class, FileRepositoryConfig.class, WebSecurityConfig.class, WebConfiguration.class, AuditConfig.class, GLISConfig.class, SwaggerConfig.class, AccessionListenersConfig.class, AmphibianConfig.class })
@EnableAspectJAutoProxy
......
package org.genesys2.spring.config;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.graylog2.GelfSender;
import org.graylog2.log.GelfAppender;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
/**
* Configure GELF logging with UDP packets to target host.
*
* @author Matija Obreza
*/
@Configuration
@Order(-1)
public class GelfConfig implements InitializingBean {
@Value("${gelf.host}")
private String gelfHost;
@Value("${gelf.port}")
private Integer gelfPort;
@Value("${host.nameAndPort}")
private String originHost;
@Value("${gelf.additionalFields}")
private String additionalFields;
private GelfSender gelfSender;
@Override
public void afterPropertiesSet() throws Exception {
if (StringUtils.isNotBlank(gelfHost)) {
System.err.println("Enabled GELF logging to " + gelfHost);
registerLogAppender();
} else {
System.err.println("GELF not enabled.");
}
}
@Bean
public GelfSender gelfSender() {
return this.gelfSender;
}
/**
* Configure Log4j appender.
*
* <pre>
# Define the graylog2 destination
log4j.appender.graylog2=org.graylog2.log.GelfAppender
# to use TCP instead of UDP, prefix with tcp:
log4j.appender.graylog2.graylogHost=127.0.0.1
# log4j.appender.graylog2.graylogPort=12201
log4j.appender.graylog2.originHost=localhost
# log4j.appender.graylog2.facility=genesys
log4j.appender.graylog2.layout=org.apache.log4j.PatternLayout
log4j.appender.graylog2.extractStacktrace=true
log4j.appender.graylog2.addExtendedInformation=true
# call location is costly
log4j.appender.graylog2.includeLocation=false
log4j.appender.graylog2.additionalFields={'environment': 'DEV', 'application': 'Genesys API'}
* </pre>
*/
private void registerLogAppender() {
GelfAppender appender = new GelfAppender();
appender.setName("gelf");
appender.setFacility("genesys");
appender.setGraylogHost(gelfHost);
if (gelfPort != null) {
appender.setGraylogPort(gelfPort);
}
appender.setAddExtendedInformation(true);
appender.setIncludeLocation(false);
appender.setAdditionalFields(additionalFields);
appender.setExtractStacktrace(true);
appender.setOriginHost(originHost);
appender.setLayout(new PatternLayout());
appender.activateOptions();
this.gelfSender = appender.getGelfSender();
Logger rootLogger = LogManager.getRootLogger();
rootLogger.addAppender(appender);
}
}
......@@ -19,6 +19,7 @@ import java.util.Arrays;
import org.genesys.blocks.oauth.service.OAuthServiceImpl;
import org.genesys.blocks.security.component.OAuthClientOriginCheckFilter;
import org.genesys2.server.servlet.filter.ApiAccessLoggerFilter;
import org.genesys2.spring.CachedInMemoryAuthorizationCodeServices;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
......@@ -53,6 +54,7 @@ import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import org.springframework.security.web.authentication.switchuser.SwitchUserFilter;
/**
* The Class OAuth2ServerConfig.
......@@ -118,6 +120,11 @@ public class OAuth2ServerConfig {
return new OAuthClientOriginCheckFilter();
}
@Bean
public ApiAccessLoggerFilter apiAccessLoggerFilter() {
return new ApiAccessLoggerFilter();
}
@Override
public void configure(final ResourceServerSecurityConfigurer resources) {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
......@@ -169,6 +176,7 @@ public class OAuth2ServerConfig {
;
/*@formatter:on*/
http.addFilterAfter(clientOriginCheckFilter(), AbstractPreAuthenticatedProcessingFilter.class);
http.addFilterAfter(apiAccessLoggerFilter(), SwitchUserFilter.class);
}
}
......
......@@ -24,6 +24,11 @@ base.cookie-domain=${host.name}
base.cookie-secure=false
base.cookie-http-only=true
# GELF logging
gelf.host=
gelf.port=
gelf.logCustomHeaders=true
gelf.additionalFields=
# Paginator
paginator.default.maxPageSize=500
......
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