You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@archiva.apache.org by ma...@apache.org on 2021/12/16 23:10:23 UTC

[archiva] branch archiva-2.x updated: [MRM-2026] Improving audit log

This is an automated email from the ASF dual-hosted git repository.

martin_s pushed a commit to branch archiva-2.x
in repository https://gitbox.apache.org/repos/asf/archiva.git


The following commit(s) were added to refs/heads/archiva-2.x by this push:
     new 8ed54fe  [MRM-2026] Improving audit log
8ed54fe is described below

commit 8ed54feb5885857c714ecc80d466b56eaeb25818
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Fri Dec 17 00:10:02 2021 +0100

    [MRM-2026] Improving audit log
---
 .../default-repository/.indexer/_0.fdt             | Bin 0 -> 33 bytes
 .../default-repository/.indexer/_0.fdx             | Bin 0 -> 12 bytes
 .../default-repository/.indexer/_0.fnm             |   2 +
 .../default-repository/.indexer/_0.frq             |   1 +
 .../default-repository/.indexer/_0.nrm             |   1 +
 .../default-repository/.indexer/_0.prx             | Bin 0 -> 1 bytes
 .../default-repository/.indexer/_0.tii             | Bin 0 -> 35 bytes
 .../default-repository/.indexer/_0.tis             | Bin 0 -> 40 bytes
 .../default-repository/.indexer/segments.gen       | Bin 0 -> 20 bytes
 .../default-repository/.indexer/segments_2         | Bin 0 -> 246 bytes
 .../default-repository/.indexer/write.lock         |   0
 .../services/interceptors/AuditInfoFilter.java     | 161 +++++++++++++++++++++
 .../src/main/resources/META-INF/spring-context.xml |   1 +
 .../security/ArchivaUserManagerAuthenticator.java  |  49 ++++++-
 .../archiva-webapp/src/main/resources/log4j2.xml   |  17 +--
 .../java/org/apache/archiva/audit/AuditLog.java    |   1 -
 16 files changed, 210 insertions(+), 23 deletions(-)

diff --git a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.fdt b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.fdt
new file mode 100644
index 0000000..bdbf9e8
Binary files /dev/null and b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.fdt differ
diff --git a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.fdx b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.fdx
new file mode 100644
index 0000000..b8ee809
Binary files /dev/null and b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.fdx differ
diff --git a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.fnm b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.fnm
new file mode 100644
index 0000000..54c9c66
--- /dev/null
+++ b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.fnm
@@ -0,0 +1,2 @@
+����
+DESCRIPTORIDXINFO
\ No newline at end of file
diff --git a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.frq b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.frq
new file mode 100644
index 0000000..6b2aaa7
--- /dev/null
+++ b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.frq
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.nrm b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.nrm
new file mode 100644
index 0000000..98f7c5a
--- /dev/null
+++ b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.nrm
@@ -0,0 +1 @@
+NRM�|
\ No newline at end of file
diff --git a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.prx b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.prx
new file mode 100644
index 0000000..f76dd23
Binary files /dev/null and b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.prx differ
diff --git a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.tii b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.tii
new file mode 100644
index 0000000..509d02d
Binary files /dev/null and b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.tii differ
diff --git a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.tis b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.tis
new file mode 100644
index 0000000..67da529
Binary files /dev/null and b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/_0.tis differ
diff --git a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/segments.gen b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/segments.gen
new file mode 100644
index 0000000..225a55b
Binary files /dev/null and b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/segments.gen differ
diff --git a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/segments_2 b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/segments_2
new file mode 100644
index 0000000..e1bc4ee
Binary files /dev/null and b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/segments_2 differ
diff --git a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/write.lock b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/repositories/default-repository/.indexer/write.lock
new file mode 100644
index 0000000..e69de29
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/interceptors/AuditInfoFilter.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/interceptors/AuditInfoFilter.java
new file mode 100644
index 0000000..a0cdb4b
--- /dev/null
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/interceptors/AuditInfoFilter.java
@@ -0,0 +1,161 @@
+package org.apache.archiva.rest.services.interceptors;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.ext.Provider;
+import java.io.IOException;
+
+/**
+ * @since
+ */
+@Service("auditInfoFilter#rest")
+@Provider
+public class AuditInfoFilter implements ContainerRequestFilter
+{
+
+    private static final Logger log = LoggerFactory.getLogger( AuditInfoFilter.class );
+
+    @Context
+    private HttpServletRequest servletRequest;
+
+    private static final AuditInfoThreadLocal auditInfoThreadLocal = new AuditInfoThreadLocal();
+
+    public AuditInfoFilter() {
+
+    }
+
+    public static class AuditInfoThreadLocal extends ThreadLocal<AuditInfo> {
+
+        public AuditInfoThreadLocal() {
+
+        }
+
+        @Override
+        protected AuditInfo initialValue( )
+        {
+            return new AuditInfo();
+        }
+    }
+
+    public static class AuditInfo {
+
+        private String remoteAddress = "0.0.0.0";
+        private String localAddress = "0.0.0.0";
+        private String remoteHost = "0.0.0.0";
+        private String protocol = "";
+        private int remotePort = 0;
+        private String method = "";
+
+        public AuditInfo() {
+
+        }
+
+        public String getRemoteAddress( )
+        {
+            return remoteAddress;
+        }
+
+        public void setRemoteAddress( String remoteAddress )
+        {
+            this.remoteAddress = remoteAddress;
+        }
+
+        public String getLocalAddress( )
+        {
+            return localAddress;
+        }
+
+        public void setLocalAddress( String localAddress )
+        {
+            this.localAddress = localAddress;
+        }
+
+        public String getRemoteHost( )
+        {
+            return remoteHost;
+        }
+
+        public void setRemoteHost( String remoteHost )
+        {
+            this.remoteHost = remoteHost;
+        }
+
+        public int getRemotePort( )
+        {
+            return remotePort;
+        }
+
+        public void setRemotePort( int remotePort )
+        {
+            this.remotePort = remotePort;
+        }
+
+        public String getMethod( )
+        {
+            return method;
+        }
+
+        public void setMethod( String method )
+        {
+            this.method = method;
+        }
+
+        public String getProtocol( )
+        {
+            return protocol;
+        }
+
+        public void setProtocol( String protocol )
+        {
+            this.protocol = protocol;
+        }
+    }
+
+
+
+    @Override
+    public void filter( ContainerRequestContext containerRequestContext ) throws IOException
+    {
+        if (log.isDebugEnabled())
+        {
+            log.debug( "Filter {}, {}", servletRequest.getRemoteAddr( ), servletRequest.getRemoteHost( ) );
+        }
+        AuditInfo auditInfo = auditInfoThreadLocal.get( );
+        auditInfo.setRemoteAddress( servletRequest.getRemoteAddr( ) );
+        auditInfo.setLocalAddress( servletRequest.getLocalAddr( ) );
+        auditInfo.setProtocol( servletRequest.getProtocol( ) );
+        auditInfo.setRemoteHost( servletRequest.getRemoteHost( ) );
+        auditInfo.setRemotePort( servletRequest.getRemotePort( ) );
+        auditInfo.setMethod( containerRequestContext.getMethod( ) );
+    }
+
+    public static AuditInfo getAuditInfo() {
+        return auditInfoThreadLocal.get( );
+    }
+}
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/resources/META-INF/spring-context.xml b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/resources/META-INF/spring-context.xml
index c9e677d..f51795d 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/resources/META-INF/spring-context.xml
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/resources/META-INF/spring-context.xml
@@ -50,6 +50,7 @@
 
     <jaxrs:providers>
       <ref bean="jsonProvider"/>
+      <ref bean="auditInfoFilter#rest"/>
       <ref bean="authenticationInterceptor#rest"/>
       <ref bean="permissionInterceptor#rest"/>
       <ref bean="requestValidationInterceptor#rest" />
diff --git a/archiva-modules/archiva-web/archiva-web-common/src/main/java/org/apache/archiva/web/security/ArchivaUserManagerAuthenticator.java b/archiva-modules/archiva-web/archiva-web-common/src/main/java/org/apache/archiva/web/security/ArchivaUserManagerAuthenticator.java
index 0a74e39..95b6d4d 100644
--- a/archiva-modules/archiva-web/archiva-web-common/src/main/java/org/apache/archiva/web/security/ArchivaUserManagerAuthenticator.java
+++ b/archiva-modules/archiva-web/archiva-web-common/src/main/java/org/apache/archiva/web/security/ArchivaUserManagerAuthenticator.java
@@ -20,6 +20,7 @@ package org.apache.archiva.web.security;
 
 import org.apache.archiva.admin.model.RepositoryAdminException;
 import org.apache.archiva.admin.model.runtime.RedbackRuntimeConfigurationAdmin;
+import org.apache.archiva.metadata.model.facets.AuditEvent;
 import org.apache.archiva.redback.authentication.AbstractAuthenticator;
 import org.apache.archiva.redback.authentication.AuthenticationConstants;
 import org.apache.archiva.redback.authentication.AuthenticationDataSource;
@@ -35,6 +36,8 @@ import org.apache.archiva.redback.policy.UserSecurityPolicy;
 import org.apache.archiva.redback.users.User;
 import org.apache.archiva.redback.users.UserManager;
 import org.apache.archiva.redback.users.UserNotFoundException;
+import org.apache.archiva.repository.events.AuditListener;
+import org.apache.archiva.rest.services.interceptors.AuditInfoFilter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.ApplicationContext;
@@ -65,6 +68,9 @@ public class ArchivaUserManagerAuthenticator
     @Inject
     private RedbackRuntimeConfigurationAdmin redbackRuntimeConfigurationAdmin;
 
+    @Inject
+    private List<AuditListener> auditListeners = new ArrayList<>();
+
     private List<UserManager> userManagers;
 
     private boolean valid = false;
@@ -94,6 +100,27 @@ public class ArchivaUserManagerAuthenticator
         }
     }
 
+    protected AuditInfoFilter.AuditInfo getAuditInformation()
+    {
+        return AuditInfoFilter.getAuditInfo( );
+    }
+
+    public List<AuditListener> getAuditListeners()
+    {
+        return auditListeners;
+    }
+
+    protected void triggerAuditEvent( String repositoryId, String filePath, String action, String user )
+    {
+        AuditEvent auditEvent = new AuditEvent( repositoryId, user, filePath, action );
+        AuditInfoFilter.AuditInfo auditInformation = getAuditInformation();
+        auditEvent.setUserId( user );
+        auditEvent.setRemoteIP( auditInformation.getRemoteHost() + ":" + auditInformation.getRemotePort() );
+        for ( AuditListener auditListener : getAuditListeners() )
+        {
+            auditListener.auditEvent( auditEvent );
+        }
+    }
 
     @Override
     public AuthenticationResult authenticate( AuthenticationDataSource ds )
@@ -104,21 +131,23 @@ public class ArchivaUserManagerAuthenticator
         Exception resultException = null;
         PasswordBasedAuthenticationDataSource source = (PasswordBasedAuthenticationDataSource) ds;
         List<AuthenticationFailureCause> authnResultErrors = new ArrayList<>();
+        final String loginUserId = source.getUsername( );
 
         for ( UserManager userManager : userManagers )
         {
             try
             {
                 log.debug( "Authenticate: {} with userManager: {}", source, userManager.getId() );
-                User user = userManager.findUser( source.getUsername() );
+                User user = userManager.findUser( loginUserId );
                 username = user.getUsername();
 
                 if ( user.isLocked() )
                 {
                     //throw new AccountLockedException( "Account " + source.getUsername() + " is locked.", user );
                     AccountLockedException e =
-                        new AccountLockedException( "Account " + source.getUsername() + " is locked.", user );
+                        new AccountLockedException( "Account " + loginUserId + " is locked.", user );
                     log.warn( "{}", e.getMessage() );
+                    triggerAuditEvent( "", "", "login-account-locked", loginUserId );
                     resultException = e;
                     authnResultErrors.add(
                         new AuthenticationFailureCause( AuthenticationConstants.AUTHN_LOCKED_USER_EXCEPTION,
@@ -131,6 +160,7 @@ public class ArchivaUserManagerAuthenticator
                     MustChangePasswordException e = new MustChangePasswordException( "Password expired.", user );
                     log.warn( "{}", e.getMessage() );
                     resultException = e;
+                    triggerAuditEvent( "", "", "login-password-change-required", loginUserId );
                     authnResultErrors.add(
                         new AuthenticationFailureCause( AuthenticationConstants.AUTHN_MUST_CHANGE_PASSWORD_EXCEPTION,
                                                         e.getMessage() ) );
@@ -142,13 +172,15 @@ public class ArchivaUserManagerAuthenticator
                 boolean isPasswordValid = encoder.isPasswordValid( user.getEncodedPassword(), source.getPassword() );
                 if ( isPasswordValid )
                 {
-                    log.debug( "User {} provided a valid password", source.getUsername() );
+                    log.debug( "User {} provided a valid password", loginUserId );
 
                     try
                     {
                         securityPolicy.extensionPasswordExpiration( user );
 
                         authenticationSuccess = true;
+                        triggerAuditEvent( "", "", "login-success", loginUserId );
+
 
                         //REDBACK-151 do not make unnessesary updates to the user object
                         if ( user.getCountFailedLoginAttempts() > 0 )
@@ -160,11 +192,12 @@ public class ArchivaUserManagerAuthenticator
                             }
                         }
 
-                        return new AuthenticationResult( true, source.getUsername(), null );
+                        return new AuthenticationResult( true, loginUserId, null );
                     }
                     catch ( MustChangePasswordException e )
                     {
                         user.setPasswordChangeRequired( true );
+                        triggerAuditEvent( "", "", "login-password-change-required", loginUserId );
                         //throw e;
                         resultException = e;
                         authnResultErrors.add( new AuthenticationFailureCause(
@@ -175,6 +208,8 @@ public class ArchivaUserManagerAuthenticator
                 {
                     log.warn( "Password is Invalid for user {} and userManager '{}'.", source.getUsername(),
                               userManager.getId() );
+                    triggerAuditEvent( "", "", "login-authentication-failed", loginUserId );
+
                     authnResultErrors.add( new AuthenticationFailureCause( AuthenticationConstants.AUTHN_NO_SUCH_USER,
                                                                            "Password is Invalid for user "
                                                                                + source.getUsername() + "." ).user( user ) );
@@ -198,18 +233,20 @@ public class ArchivaUserManagerAuthenticator
             }
             catch ( UserNotFoundException e )
             {
-                log.warn( "Login for user {} and userManager {} failed. user not found.", source.getUsername(),
+                log.warn( "Login for user {} and userManager {} failed. user not found.", loginUserId,
                           userManager.getId() );
                 resultException = e;
+                triggerAuditEvent( "", "", "login-user-unknown", loginUserId );
                 authnResultErrors.add( new AuthenticationFailureCause( AuthenticationConstants.AUTHN_NO_SUCH_USER,
                                                                        "Login for user " + source.getUsername()
                                                                            + " failed. user not found." ) );
             }
             catch ( Exception e )
             {
-                log.warn( "Login for user {} and userManager {} failed, message: {}", source.getUsername(),
+                log.warn( "Login for user {} and userManager {} failed, message: {}", loginUserId,
                           userManager.getId(), e.getMessage() );
                 resultException = e;
+                triggerAuditEvent( "", "", "login-error", loginUserId );
                 authnResultErrors.add( new AuthenticationFailureCause( AuthenticationConstants.AUTHN_RUNTIME_EXCEPTION,
                                                                        "Login for user " + source.getUsername()
                                                                            + " failed, message: " + e.getMessage() ) );
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/resources/log4j2.xml b/archiva-modules/archiva-web/archiva-webapp/src/main/resources/log4j2.xml
index f22ee06..462ebdd 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/resources/log4j2.xml
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/resources/log4j2.xml
@@ -40,7 +40,7 @@
 
     <RollingRandomAccessFile name="auditlog" fileName="${logsDirectory}/archiva-audit.log"
                  filePattern="${logsDirectory}/archiva-audit-%d{yyyyMMdd}.log"
-                 immediateFlush="false" append="true">
+                 immediateFlush="true" append="true">
       <PatternLayout>
         <pattern>%d{yyyy-MM-dd HH:mm:ss} %m%n</pattern>
       </PatternLayout>
@@ -49,24 +49,9 @@
       </Policies>
     </RollingRandomAccessFile>
 
-    <RollingRandomAccessFile name="redbackAuditLog" fileName="${logsDirectory}/archiva-security-audit.log"
-                 filePattern="${logsDirectory}/archiva-security-audit.log-%d{yyyyMMdd}.log"
-                 immediateFlush="false" append="true">
-      <PatternLayout>
-        <pattern>%d{yyyy-MM-dd HH:mm:ss} - %X{redback.currentUser} - %m%n</pattern>
-      </PatternLayout>
-      <Policies>
-        <TimeBasedTriggeringPolicy />
-      </Policies>
-    </RollingRandomAccessFile>
-
   </appenders>
   <loggers>
 
-    <logger name="org.apache.archiva.redback.struts2.action.AuditEvent" additivity="false" level="info">
-      <appender-ref ref="redbackAuditLog" />
-    </logger>
-
     <logger name="org.apache.archiva.AuditLog" additivity="false" level="info">
       <appender-ref ref="auditlog" />
     </logger>
diff --git a/archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/AuditLog.java b/archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/AuditLog.java
index 37b5a52..bd6887d 100644
--- a/archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/AuditLog.java
+++ b/archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/AuditLog.java
@@ -54,7 +54,6 @@ public class AuditLog
         msg.append( checkNull( event.getRemoteIP() ) ).append( DELIM );
         msg.append( '\"' ).append( checkNull( event.getResource() ) ).append( '\"' ).append( DELIM );
         msg.append( '\"' ).append( event.getAction() ).append( '\"' );
-
         logger.info( msg.toString() );
     }