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

com.sap.cloud.sdk.cloudplatform.auditlog.ScpNeoAuditLog Maven / Gradle / Ivy

/*
 * Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved.
 */

package com.sap.cloud.sdk.cloudplatform.auditlog;

import java.util.Map;
import java.util.Optional;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.slf4j.Logger;

import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.sap.cloud.auditlog.AuditedDataSubject;
import com.sap.cloud.auditlog.AuditedObject;
import com.sap.cloud.auditlog.ConfigurationChangeAuditMessage;
import com.sap.cloud.auditlog.ReadAccessAuditMessage;
import com.sap.cloud.auditlog.exception.AuditLogWriteException;
import com.sap.cloud.auditlog.extension.AuditLogMessageExtension;
import com.sap.cloud.auditlog.extension.AuditLogMessageExtensionFactory;
import com.sap.cloud.auditlog.extension.ConfigurationChangeAuditMessageExtension;
import com.sap.cloud.auditlog.extension.DataModificationAuditMessageExtension;
import com.sap.cloud.auditlog.extension.ReadAccessAuditMessageExtension;
import com.sap.cloud.auditlog.extension.SecurityEventAuditMessageExtension;
import com.sap.cloud.sdk.cloudplatform.auditlog.exception.AuditLogAccessException;
import com.sap.cloud.sdk.cloudplatform.logging.CloudLoggerFactory;
import com.sap.cloud.sdk.cloudplatform.servlet.Property;
import com.sap.cloud.sdk.cloudplatform.servlet.RequestContext;
import com.sap.cloud.sdk.cloudplatform.servlet.RequestContextAccessor;
import com.sap.cloud.sdk.cloudplatform.servlet.RequestContextExecutor;
import com.sap.cloud.sdk.cloudplatform.servlet.RequestContextServletFilter;
import com.sap.cloud.sdk.cloudplatform.servlet.exception.RequestContextPropertyException;

/**
 * Implementation of audit logging that uses the SAP CP Neo library. Makes extensive use of custom attributes to fill in
 * useful auditing information.
 * 

* Important: For performance reasons, consider to only use the audit log for logging of events such as: *

    *
  • security relevant events, *
  • read access to sensitive personal data, *
  • changes to configuration data, *
  • and changes to personal data. *
*/ public class ScpNeoAuditLog implements AuditLog { private static final Logger logger = CloudLoggerFactory.getLogger(ScpNeoAuditLog.class); static final String ACTION_SECURITY_EVENT_BEGIN = "securityEventBegin"; static final String ACTION_SECURITY_EVENT = "securityEvent"; static final String ACTION_SECURITY_EVENT_FAILED = "securityEventFailed"; static final String ATTRIBUTE_CALLER_CHANNEL = "caller_channel"; static final String ATTRIBUTE_LOG_MESSAGE = "message"; static final String ATTRIBUTE_ERROR_MESSAGE = "errorMessage"; static final String ATTRIBUTE_STACK_TRACE = "errorStackTrace"; static final String SUCCESSFUL_OPERATION = "[successfully modified]"; static final String FAILED_OPERATION = "[modification failed]"; private final AuditLogMessageExtensionFactory auditLogExtensionMessageFactory; public ScpNeoAuditLog() throws AuditLogAccessException { auditLogExtensionMessageFactory = getAuditLogMessageExtensionFactory(); } public ScpNeoAuditLog( @Nonnull final AuditLogMessageExtensionFactory auditLogExtensionMessageFactory ) { this.auditLogExtensionMessageFactory = auditLogExtensionMessageFactory; } private AuditLogMessageExtensionFactory getAuditLogMessageExtensionFactory() throws AuditLogAccessException { final Optional requestContext = RequestContextAccessor.getCurrentRequestContext(); if( !requestContext.isPresent() ) { throw new AuditLogAccessException( "Failed to get " + AuditLogMessageExtensionFactory.class.getSimpleName() + ": no " + RequestContext.class.getSimpleName() + " available." + " Have you correctly configured a " + RequestContextServletFilter.class.getSimpleName() + " or have you wrapped your logic in a " + RequestContextExecutor.class.getSimpleName() + " when executing background tasks that are not triggered by a request?"); } try { final Optional> property = requestContext.get().getProperty( ScpNeoAuditLogRequestContextListener.PROPERTY_AUDIT_LOG_MESSAGE_FACTORY); if( !property.isPresent() ) { throw new AuditLogAccessException( "Failed to get " + AuditLogMessageExtensionFactory.class.getSimpleName() + ": " + RequestContext.class.getSimpleName() + " property \"" + ScpNeoAuditLogRequestContextListener.PROPERTY_AUDIT_LOG_MESSAGE_FACTORY + "\" is not present. " + "Please ensure that " + ScpNeoAuditLogRequestContextListener.class.getSimpleName() + " is available on the class path."); } @Nullable final Exception exception = property.get().getException(); if( exception != null ) { throw new AuditLogAccessException( "Failed to get " + AuditLogMessageExtensionFactory.class.getSimpleName() + ".", exception); } return (AuditLogMessageExtensionFactory) property.get().getValue(); } catch( final RequestContextPropertyException e ) { throw new AuditLogAccessException( "Failed to get " + AuditLogMessageExtensionFactory.class.getSimpleName() + ": failed to get " + RequestContext.class.getSimpleName() + " property.", e); } } @Override public void logSecurityEventBeginning( @Nonnull final AccessRequester initiator, @Nullable final String message ) { logSecurityEvent(true, initiator, message, null); } @Override public void logSecurityEvent( @Nonnull final AccessRequester initiator, @Nullable final String message, @Nullable final Throwable throwable ) { logSecurityEvent(false, initiator, message, throwable); } private void logSecurityEvent( final boolean isBeginning, @Nonnull final AccessRequester initiator, @Nullable final String message, @Nullable final Throwable throwable ) { final SecurityEventAuditMessageExtension auditLogEntry = auditLogExtensionMessageFactory.createAuditLogMessageExtension(SecurityEventAuditMessageExtension.class); if( throwable == null ) { auditLogEntry.setAction(isBeginning ? ACTION_SECURITY_EVENT_BEGIN : ACTION_SECURITY_EVENT); } else { auditLogEntry.setAction(ACTION_SECURITY_EVENT_FAILED); } if( !Strings.isNullOrEmpty(message) ) { auditLogEntry.setMessage(message); } fillCommonAttributesAndLog(auditLogEntry, initiator, throwable); } @Override public void logConfigChangeBeginning( @Nonnull final AccessRequester initiator, @Nonnull final AuditedDataObject object, @Nullable final Iterable attributesAffected ) { logConfigChange(true, initiator, object, attributesAffected, null); } @Override public void logConfigChange( @Nonnull final AccessRequester initiator, @Nonnull final AuditedDataObject object, @Nullable final Iterable attributesAffected, @Nullable final Throwable error ) { logConfigChange(false, initiator, object, attributesAffected, error); } private void logConfigChange( final boolean isBeginning, @Nonnull final AccessRequester initiator, @Nonnull final AuditedDataObject object, @Nullable final Iterable attributesAffected, @Nullable final Throwable error ) { final ConfigurationChangeAuditMessageExtension auditLogEntry = auditLogExtensionMessageFactory .createAuditLogMessageExtension(ConfigurationChangeAuditMessageExtension.class); if( error == null ) { auditLogEntry.setAction( isBeginning ? ConfigurationChangeAuditMessage.ACTION_ABOUT_TO_UPDATE : ConfigurationChangeAuditMessage.ACTION_UPDATE); } else { auditLogEntry.setAction(ConfigurationChangeAuditMessage.ACTION_UPDATE_FAILED); } auditLogEntry.setObject(convertAuditedObject(object)); if( attributesAffected != null ) { for( final AccessedAttribute attribute : attributesAffected ) { auditLogEntry.addChangedValues( attribute.getIdentifier(), String.valueOf(attribute.getOldValue()), String.valueOf(attribute.getNewValue())); } } fillCommonAttributesAndLog(auditLogEntry, initiator, error); } @Override public void logDataReadAttempt( @Nonnull final AccessRequester initiator, @Nonnull final AuditedDataObject object, @Nonnull final com.sap.cloud.sdk.cloudplatform.auditlog.AuditedDataSubject subject, @Nullable final Iterable attributesAffected ) { logDataRead(true, initiator, object, subject, attributesAffected, null); } @Override public void logDataRead( @Nonnull final AccessRequester initiator, @Nonnull final AuditedDataObject object, @Nonnull final com.sap.cloud.sdk.cloudplatform.auditlog.AuditedDataSubject subject, @Nullable final Iterable attributesAffected, @Nullable final Throwable error ) { logDataRead(false, initiator, object, subject, attributesAffected, error); } private void logDataRead( final boolean isAttempting, @Nonnull final AccessRequester initiator, @Nonnull final AuditedDataObject object, @Nonnull final com.sap.cloud.sdk.cloudplatform.auditlog.AuditedDataSubject subject, @Nullable final Iterable attributesAffected, @Nullable final Throwable error ) { final ReadAccessAuditMessageExtension auditLogEntry = auditLogExtensionMessageFactory.createAuditLogMessageExtension(ReadAccessAuditMessageExtension.class); if( error == null ) { auditLogEntry.setAction( isAttempting ? ReadAccessAuditMessage.ACTION_ABOUT_TO_READ : ReadAccessAuditMessage.ACTION_READ); } else { auditLogEntry.setAction(ReadAccessAuditMessage.ACTION_READ_ATTEMPT); } auditLogEntry.setObject(convertAuditedObject(object)); auditLogEntry.setDataSubject(convertAuditedSubject(subject)); if( attributesAffected != null ) { for( final AccessedAttribute attribute : attributesAffected ) { auditLogEntry.addObjectAttribute(attribute.getIdentifier(), String.valueOf(attribute.getOldValue())); } } fillCommonAttributesAndLog(auditLogEntry, initiator, error); } @Override public void logDataWriteAttempt( @Nonnull final AccessRequester initiator, @Nonnull final AuditedDataObject object, @Nonnull final com.sap.cloud.sdk.cloudplatform.auditlog.AuditedDataSubject subject, @Nullable final Iterable attributesAffected ) { logDataWrite(true, initiator, object, subject, attributesAffected, null); } @Override public void logDataWrite( @Nonnull final AccessRequester initiator, @Nonnull final AuditedDataObject object, @Nonnull final com.sap.cloud.sdk.cloudplatform.auditlog.AuditedDataSubject subject, @Nullable final Iterable attributesAffected, @Nullable final Throwable error ) { logDataWrite(false, initiator, object, subject, attributesAffected, error); } private void logDataWrite( final boolean isAttempting, @Nonnull final AccessRequester initiator, @Nonnull final AuditedDataObject object, @Nonnull final com.sap.cloud.sdk.cloudplatform.auditlog.AuditedDataSubject subject, @Nullable final Iterable attributesAffected, @Nullable final Throwable error ) { final DataModificationAuditMessageExtension auditLogEntry = auditLogExtensionMessageFactory.createAuditLogMessageExtension(DataModificationAuditMessageExtension.class); if( error == null ) { auditLogEntry.setAction( isAttempting ? DataModificationAuditMessageExtension.ACTION_ABOUT_TO_UPDATE : DataModificationAuditMessageExtension.ACTION_UPDATE); } else { auditLogEntry.setAction(DataModificationAuditMessageExtension.ACTION_UPDATE_FAILED); } auditLogEntry.setObject(convertAuditedObject(object)); auditLogEntry.setDataSubject(convertAuditedSubject(subject)); if( attributesAffected != null ) { for( final AccessedAttribute attribute : attributesAffected ) { auditLogEntry.addModifiedValues( attribute.getIdentifier(), String.valueOf(attribute.getOldValue()), String.valueOf(attribute.getNewValue())); } } fillCommonAttributesAndLog(auditLogEntry, initiator, error); } @SuppressWarnings( "deprecation" ) private void fillCommonAttributesAndLog( @Nonnull final AuditLogMessageExtension auditLogEntry, @Nonnull final AccessRequester initiator, @Nullable final Throwable throwable ) { auditLogEntry.setCaller( initiator.getIpAddress().orElse(null), null, initiator.getUserId().orElse(null), null, null, null, initiator.getTenantId().orElse(null)); auditLogEntry.addCustomAttribute(ATTRIBUTE_CALLER_CHANNEL, initiator.getChannel().orElse(null)); if( throwable != null ) { auditLogEntry.addCustomAttribute(ATTRIBUTE_ERROR_MESSAGE, throwable.getMessage()); auditLogEntry.addCustomAttribute(ATTRIBUTE_STACK_TRACE, Throwables.getStackTraceAsString(throwable)); } try { auditLogEntry.log(getClass()); } catch( final AuditLogWriteException e ) { logger.error("Unable to write audit log entry. Entry contents: [" + auditLogEntry + "]", e); } } private AuditedObject convertAuditedObject( @Nonnull AuditedDataObject sdkObject ) { AuditedObject converted = auditLogExtensionMessageFactory.createAuditedObject(); converted.setType(sdkObject.getType()); for( Map.Entry identifier : sdkObject.getAllIdentifiers().entrySet() ) { converted.addIdentifier(identifier.getKey(), identifier.getValue()); } return converted; } private AuditedDataSubject convertAuditedSubject( @Nonnull com.sap.cloud.sdk.cloudplatform.auditlog.AuditedDataSubject sdkSubject ) { AuditedDataSubject converted = auditLogExtensionMessageFactory.createAuditedDataSubject(); converted.setType(sdkSubject.getType()); converted.setRole(sdkSubject.getRole()); for( Map.Entry identifier : sdkSubject.getAllIdentifiers().entrySet() ) { converted.addIdentifier(identifier.getKey(), identifier.getValue()); } return converted; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy