All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sap.cloud.yaas.servicesdk.auditbase.event.SecurityAuditEvent Maven / Gradle / Ivy

The newest version!
/*
 * © 2017 SAP SE or an SAP affiliate company.
 * All rights reserved.
 * Please see http://www.sap.com/corporate-en/legal/copyright/index.epx for additional trademark information and
 * notices.
 */
package com.sap.cloud.yaas.servicesdk.auditbase.event;

import javax.xml.bind.annotation.XmlRootElement;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.sap.cloud.yaas.servicesdk.auditbase.validation.CustomValidated;
import com.sap.cloud.yaas.servicesdk.auditbase.validation.Mandatory;
import com.sap.cloud.yaas.servicesdk.auditbase.validation.Validated;
import com.sap.cloud.yaas.servicesdk.auditbase.validation.ValidationViolation;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.validator.routines.InetAddressValidator;
import org.slf4j.MDC;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.function.Function;


/**
 * A security relevant event at the tenant, organization or account level. Security relevant events comprise all those
 * events which may impact the confidentiality, the integrity, and / or the availability (CIA) of the system. Examples
 * are: failed logins or failed authorization checks.
 */
@XmlRootElement
@JsonAutoDetect(isGetterVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, //
		setterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE, //
		fieldVisibility = JsonAutoDetect.Visibility.NONE)
@Validated
public final class SecurityAuditEvent extends AuditEvent
{
	/**
	 * MDC key for the "X-Forwarded-For" HTTP header containing list of IP addresses of the client chain.
	 */
	static final String MDC_KEY_X_FORWARDED_FOR = "xForwardedFor";

	/**
	 * The type of the event as used in Audit Ingestion Service.
	 */
	private static final String TYPE = "security-events";

	@JsonProperty(value = "clientIp")
	@CustomValidated(validator = IPAddressValidator.class)
	@Mandatory
	private final String clientIp;

	@JsonProperty(value = "data")
	@JsonSerialize(using = SecurityMessageSerializer.class)
	@Mandatory
	private final String message;

	// CHECKSTYLE IGNORE ParameterNumberCheck NEXT 10
	@SuppressWarnings("PMD.ExcessiveParameterList")
	private SecurityAuditEvent(final String serviceBasePath,
			final String serviceRegion,
			final String source,
			final SourceType sourceType,
			final String userId,
			final Date timestamp,
			final String clientIp,
			final String message)
	{
		super(serviceBasePath, serviceRegion, source, sourceType, userId, timestamp);
		this.clientIp = clientIp;
		this.message = message;
	}

	/**
	 * Creates a builder for security-relevant audit event, see {@link AuditEventBuilderFactory#securityEvent()}.
	 *
	 * @return a new instance of {@link SecurityAuditEventBuilder}
	 */
	public static SecurityAuditEventBuilder builder()
	{
		return new SecurityAuditEventBuilder();
	}

	@Override
	public String getType()
	{
		return TYPE;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString()
	{
		return String.format("%s, message: '%s'", super.toString(), message);
	}

	/**
	 * See {@link SecurityAuditEventBuilder#clientIp(String)}.
	 *
	 * @return the client IP
	 */
	public String getClientIp()
	{
		return clientIp;
	}

	/**
	 * See {@link SecurityAuditEventBuilder#message(String)}. Should not contain personal data.
	 *
	 * @return the security event message, without personal data
	 */
	public String getMessage()
	{
		return message;
	}

	/**
	 * Builder for {@link SecurityAuditEvent}.
	 */
	@SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName")
	public static class SecurityAuditEventBuilder extends AbstractAuditEventBuilder
	{
		private String clientIp;
		private String message;

		/**
		 * IP of the original client.
		 *
		 * @param newClientIp the IP address
		 * @return {@code this}, for chaining
		 */
		public SecurityAuditEventBuilder clientIp(final String newClientIp)
		{
			if (newClientIp != null)
			{
				this.clientIp = newClientIp;
			}
			return this;
		}

		/**
		 * Sets or updates the message describing the security audit event. The message should not contain personal data.
		 *
		 * @param newMessage the message
		 * @return {@code this}, for chaining
		 */
		public SecurityAuditEventBuilder message(final String newMessage)
		{
			if (newMessage != null)
			{
				this.message = newMessage;
			}
			return this;
		}

		@Override
		public SecurityAuditEvent build()
		{
			return new SecurityAuditEvent(
					resolveServiceBasePath(),
					resolveServiceRegion(),
					resolveSource(),
					resolveSourceType(),
					resolveUserId(),
					resolveTimestamp(),
					resolveIpAddressInformation(),
					message);
		}

		private String resolveIpAddressInformation()
		{
			if (StringUtils.isNotEmpty(clientIp))
			{
				return clientIp;
			}
			else
			{
				return parseFirstIpFromXForwardedForHeader();
			}
		}

		private String parseFirstIpFromXForwardedForHeader()
		{
			final String xForwardedFor = MDC.get(MDC_KEY_X_FORWARDED_FOR);
			if (StringUtils.isNotEmpty(xForwardedFor))
			{
				final String[] forwardIps = xForwardedFor.split(",");
				if (forwardIps != null && forwardIps.length > 0)
				{
					return forwardIps[0];
				}
			}
			return null;
		}
	}

	static class SecurityMessageSerializer extends JsonSerializer
	{
		@Override
		public void serialize(final String value,
				final JsonGenerator gen,
				final SerializerProvider serializers) throws IOException,
				JsonProcessingException
		{
			final Map messageData = Collections.singletonMap("message", value);
			gen.writeObject(messageData);
		}
	}

	/**
	 * Validator that validates an IP address.
	 */
	public static class IPAddressValidator implements Function>
	{
		@Override
		public Collection apply(final Object clientIp)
		{
			final Collection validationViolations = new ArrayList<>();
			if (!InetAddressValidator.getInstance().isValid(String.valueOf(clientIp)))
			{
				validationViolations.add(new ValidationViolation("clientIp", "The property must be a valid IP address"));
			}
			return validationViolations;
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy