001package org.apache.archiva.audit; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.archiva.metadata.model.facets.AuditEvent; 023import org.apache.archiva.metadata.repository.MetadataRepository; 024import org.apache.archiva.metadata.repository.MetadataRepositoryException; 025import org.apache.archiva.metadata.repository.RepositorySession; 026import org.apache.archiva.metadata.repository.RepositorySessionFactory; 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029import org.springframework.stereotype.Service; 030 031import javax.inject.Inject; 032import java.text.ParseException; 033import java.text.SimpleDateFormat; 034import java.util.ArrayList; 035import java.util.Collection; 036import java.util.Collections; 037import java.util.Comparator; 038import java.util.Date; 039import java.util.List; 040import java.util.TimeZone; 041 042/** 043 * 044 */ 045@Service("auditManager#default") 046public class DefaultAuditManager 047 implements AuditManager 048{ 049 private static final int NUM_RECENT_EVENTS = 10; 050 051 private static final Logger log = LoggerFactory.getLogger( DefaultAuditManager.class ); 052 053 private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone( "UTC" ); 054 055 @Inject 056 RepositorySessionFactory repositorySessionFactory; 057 058 @Override 059 public List<AuditEvent> getMostRecentAuditEvents( MetadataRepository metadataRepository, 060 List<String> repositoryIds ) 061 throws MetadataRepositoryException 062 { 063 try(RepositorySession session = repositorySessionFactory.createSession()) { 064 // TODO: consider a more efficient implementation that directly gets the last ten from the content repository 065 List<AuditRecord> records = new ArrayList<>(); 066 for (String repositoryId : repositoryIds) { 067 List<String> names = metadataRepository.getMetadataFacets(session, repositoryId, AuditEvent.FACET_ID); 068 for (String name : names) { 069 records.add(new AuditRecord(repositoryId, name)); 070 } 071 } 072 Collections.sort(records); 073 records = records.subList(0, records.size() < NUM_RECENT_EVENTS ? records.size() : NUM_RECENT_EVENTS); 074 075 List<AuditEvent> events = new ArrayList<>(records.size()); 076 for (AuditRecord record : records) { 077 AuditEvent auditEvent = (AuditEvent) metadataRepository.getMetadataFacet(session, 078 record.repositoryId, 079 AuditEvent.FACET_ID, record.name); 080 events.add(auditEvent); 081 } 082 return events; 083 } 084 } 085 086 @Override 087 public void addAuditEvent( MetadataRepository repository, AuditEvent event ) 088 throws MetadataRepositoryException 089 { 090 try(RepositorySession session = repositorySessionFactory.createSession()) { 091 // ignore those with no repository - they will still be logged to the textual audit log 092 if (event.getRepositoryId() != null) { 093 repository.addMetadataFacet(session, event.getRepositoryId(), event); 094 } 095 } 096 } 097 098 @Override 099 public void deleteAuditEvents( MetadataRepository metadataRepository, String repositoryId ) 100 throws MetadataRepositoryException 101 { 102 try(RepositorySession session = repositorySessionFactory.createSession()) { 103 metadataRepository.removeMetadataFacets(session, repositoryId, AuditEvent.FACET_ID); 104 } 105 } 106 107 @Override 108 public List<AuditEvent> getAuditEventsInRange( MetadataRepository metadataRepository, 109 Collection<String> repositoryIds, Date startTime, Date endTime ) 110 throws MetadataRepositoryException 111 { 112 return getAuditEventsInRange( metadataRepository, repositoryIds, null, startTime, endTime ); 113 } 114 115 @Override 116 public List<AuditEvent> getAuditEventsInRange( MetadataRepository metadataRepository, 117 Collection<String> repositoryIds, String resource, Date startTime, 118 Date endTime ) 119 throws MetadataRepositoryException 120 { 121 try(RepositorySession session = repositorySessionFactory.createSession()) { 122 List<AuditEvent> results = new ArrayList<>(); 123 for (String repositoryId : repositoryIds) { 124 List<String> list = metadataRepository.getMetadataFacets(session, repositoryId, AuditEvent.FACET_ID); 125 for (String name : list) { 126 try { 127 Date date = createNameFormat().parse(name); 128 if ((startTime == null || !date.before(startTime)) && (endTime == null || !date.after( 129 endTime))) { 130 AuditEvent event = (AuditEvent) metadataRepository.getMetadataFacet(session, 131 repositoryId, 132 AuditEvent.FACET_ID, name); 133 134 if (resource == null || event.getResource().startsWith(resource)) { 135 results.add(event); 136 } 137 } 138 } catch (ParseException e) { 139 log.error("Invalid audit event found in the metadata repository: {}", e.getMessage()); 140 // continue and ignore this one 141 } 142 } 143 } 144 Collections.sort(results, new Comparator<AuditEvent>() { 145 @Override 146 public int compare(AuditEvent o1, AuditEvent o2) { 147 return o2.getTimestamp().compareTo(o1.getTimestamp()); 148 } 149 }); 150 return results; 151 } 152 } 153 154 private static SimpleDateFormat createNameFormat() 155 { 156 SimpleDateFormat fmt = new SimpleDateFormat( AuditEvent.TIMESTAMP_FORMAT ); 157 fmt.setTimeZone( UTC_TIME_ZONE ); 158 return fmt; 159 } 160 161 private static final class AuditRecord 162 implements Comparable<AuditRecord> 163 { 164 private String repositoryId; 165 166 private String name; 167 168 public AuditRecord( String repositoryId, String name ) 169 { 170 this.repositoryId = repositoryId; 171 this.name = name; 172 } 173 174 @Override 175 public int compareTo( AuditRecord other ) 176 { 177 // reverse ordering 178 return other.name.compareTo( name ); 179 } 180 } 181 182 public RepositorySessionFactory getRepositorySessionFactory( ) 183 { 184 return repositorySessionFactory; 185 } 186 187 public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory ) 188 { 189 this.repositorySessionFactory = repositorySessionFactory; 190 } 191}