mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-07-04 09:28:06 +02:00
Add initial audit log API
Introduce audit log API which logs all creations, modifications and deletions of annotated entities and everything which is stored inside a ConfigurationStore. Without the related Audit Log Plugin installed this API does nothing.
This commit is contained in:
committed by
SCM-Manager
parent
e74225e168
commit
56265be9a2
@@ -21,12 +21,16 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm;
|
||||
|
||||
import com.github.sdorra.ssp.PermissionCheck;
|
||||
import sonia.scm.auditlog.AuditEntry;
|
||||
import sonia.scm.auditlog.Auditor;
|
||||
import sonia.scm.auditlog.EntryCreationContext;
|
||||
import sonia.scm.util.AssertUtil;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
@@ -34,9 +38,11 @@ import java.util.function.Supplier;
|
||||
public class ManagerDaoAdapter<T extends ModelObject> {
|
||||
|
||||
private final GenericDAO<T> dao;
|
||||
private final Set<Auditor> auditors;
|
||||
|
||||
public ManagerDaoAdapter(GenericDAO<T> dao) {
|
||||
public ManagerDaoAdapter(GenericDAO<T> dao, Set<Auditor> auditors) {
|
||||
this.dao = dao;
|
||||
this.auditors = auditors;
|
||||
}
|
||||
|
||||
public void modify(T object, Function<T, PermissionCheck> permissionCheck, AroundHandler<T> beforeUpdate, AroundHandler<T> afterUpdate) {
|
||||
@@ -51,6 +57,8 @@ public class ManagerDaoAdapter<T extends ModelObject> {
|
||||
object.setLastModified(System.currentTimeMillis());
|
||||
object.setCreationDate(notModified.getCreationDate());
|
||||
|
||||
callAuditors(notModified, object);
|
||||
|
||||
dao.modify(object);
|
||||
|
||||
afterUpdate.handle(notModified);
|
||||
@@ -73,6 +81,7 @@ public class ManagerDaoAdapter<T extends ModelObject> {
|
||||
existsCheck.accept(newObject);
|
||||
newObject.setCreationDate(System.currentTimeMillis());
|
||||
beforeCreate.handle(newObject);
|
||||
callAuditors(null, newObject);
|
||||
dao.add(newObject);
|
||||
afterCreate.handle(newObject);
|
||||
return newObject;
|
||||
@@ -82,6 +91,7 @@ public class ManagerDaoAdapter<T extends ModelObject> {
|
||||
permissionCheck.get().check();
|
||||
if (dao.contains(toDelete)) {
|
||||
beforeDelete.handle(toDelete);
|
||||
callAuditors(toDelete, null);
|
||||
dao.delete(toDelete);
|
||||
afterDelete.handle(toDelete);
|
||||
} else {
|
||||
@@ -89,6 +99,12 @@ public class ManagerDaoAdapter<T extends ModelObject> {
|
||||
}
|
||||
}
|
||||
|
||||
private void callAuditors(T notModified, T newObject) {
|
||||
if ((newObject == null? notModified: newObject).getClass().isAnnotationPresent(AuditEntry.class)) {
|
||||
auditors.forEach(s -> s.createEntry(new EntryCreationContext<>(newObject, notModified)));
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface AroundHandler<T extends ModelObject> {
|
||||
void handle(T notModified);
|
||||
|
||||
@@ -37,6 +37,7 @@ import sonia.scm.HandlerEventType;
|
||||
import sonia.scm.ManagerDaoAdapter;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.auditlog.Auditor;
|
||||
import sonia.scm.search.SearchRequest;
|
||||
import sonia.scm.search.SearchUtil;
|
||||
import sonia.scm.util.CollectionAppender;
|
||||
@@ -77,10 +78,10 @@ public class DefaultGroupManager extends AbstractGroupManager
|
||||
* @param groupDAO
|
||||
*/
|
||||
@Inject
|
||||
public DefaultGroupManager(GroupDAO groupDAO)
|
||||
public DefaultGroupManager(GroupDAO groupDAO, Set<Auditor> auditors)
|
||||
{
|
||||
this.groupDAO = groupDAO;
|
||||
this.managerDaoAdapter = new ManagerDaoAdapter<>(groupDAO);
|
||||
this.managerDaoAdapter = new ManagerDaoAdapter<>(groupDAO, auditors);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
@@ -285,16 +286,11 @@ public class DefaultGroupManager extends AbstractGroupManager
|
||||
final PermissionActionCheck<Group> check = GroupPermissions.read();
|
||||
|
||||
return Util.createSubCollection(groupDAO.getAll(), comparator,
|
||||
new CollectionAppender<Group>()
|
||||
{
|
||||
@Override
|
||||
public void append(Collection<Group> collection, Group group)
|
||||
{
|
||||
(collection, group) -> {
|
||||
if (check.isPermitted(group)) {
|
||||
collection.add(group.clone());
|
||||
}
|
||||
}
|
||||
}, start, limit);
|
||||
}, start, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,7 @@ package sonia.scm.lifecycle.modules;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.multibindings.Multibinder;
|
||||
import com.google.inject.throwingproviders.ThrowingProviderBinder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -49,6 +50,8 @@ import sonia.scm.security.DefaultKeyGenerator;
|
||||
import sonia.scm.security.KeyGenerator;
|
||||
import sonia.scm.store.BlobStoreFactory;
|
||||
import sonia.scm.store.ConfigurationEntryStoreFactory;
|
||||
import sonia.scm.store.ConfigurationStore;
|
||||
import sonia.scm.store.ConfigurationStoreDecoratorFactory;
|
||||
import sonia.scm.store.ConfigurationStoreFactory;
|
||||
import sonia.scm.store.DataStoreFactory;
|
||||
import sonia.scm.store.DefaultBlobDirectoryAccess;
|
||||
@@ -100,6 +103,10 @@ public class BootstrapModule extends AbstractModule {
|
||||
// note CipherUtil uses an other generator
|
||||
bind(CipherHandler.class).toInstance(CipherUtil.getInstance().getCipherHandler());
|
||||
|
||||
// Bind empty set in the bootstrap module
|
||||
Multibinder.newSetBinder(binder(), ConfigurationStoreDecoratorFactory.class).addBinding()
|
||||
.to(NoOpConfigurationStoreDecoratorFactory.class);
|
||||
|
||||
// bind core
|
||||
bind(RepositoryArchivedCheck.class, EventDrivenRepositoryArchiveCheck.class);
|
||||
bind(RepositoryExportingCheck.class, DefaultRepositoryExportingCheck.class);
|
||||
@@ -137,4 +144,11 @@ public class BootstrapModule extends AbstractModule {
|
||||
|
||||
return implementation;
|
||||
}
|
||||
|
||||
private static class NoOpConfigurationStoreDecoratorFactory implements ConfigurationStoreDecoratorFactory {
|
||||
@Override
|
||||
public <T> ConfigurationStore<T> createDecorator(ConfigurationStore<T> object, Context context) {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import sonia.scm.api.v2.resources.BranchLinkProvider;
|
||||
import sonia.scm.api.v2.resources.DefaultBranchLinkProvider;
|
||||
import sonia.scm.api.v2.resources.DefaultRepositoryLinkProvider;
|
||||
import sonia.scm.api.v2.resources.RepositoryLinkProvider;
|
||||
import sonia.scm.auditlog.AuditLogConfigurationStoreDecoratorFactory;
|
||||
import sonia.scm.cache.CacheManager;
|
||||
import sonia.scm.cache.GuavaCacheManager;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
@@ -98,6 +99,7 @@ import sonia.scm.security.DefaultSecuritySystem;
|
||||
import sonia.scm.security.LoginAttemptHandler;
|
||||
import sonia.scm.security.RepositoryPermissionProvider;
|
||||
import sonia.scm.security.SecuritySystem;
|
||||
import sonia.scm.store.ConfigurationStoreDecoratorFactory;
|
||||
import sonia.scm.store.FileStoreExporter;
|
||||
import sonia.scm.store.StoreExporter;
|
||||
import sonia.scm.template.MustacheTemplateEngine;
|
||||
@@ -154,6 +156,10 @@ class ScmServletModule extends ServletModule {
|
||||
|
||||
bind(NamespaceStrategy.class).toProvider(NamespaceStrategyProvider.class);
|
||||
|
||||
// bind store decorators
|
||||
Multibinder<ConfigurationStoreDecoratorFactory> storeDecoratorMultiBinder = Multibinder.newSetBinder(binder(), ConfigurationStoreDecoratorFactory.class);
|
||||
storeDecoratorMultiBinder.addBinding().to(AuditLogConfigurationStoreDecoratorFactory.class);
|
||||
|
||||
// bind repository provider
|
||||
ThrowingProviderBinder.create(binder())
|
||||
.bind(RepositoryProvider.class, Repository.class)
|
||||
|
||||
@@ -37,6 +37,7 @@ import sonia.scm.NoChangesMadeException;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.Type;
|
||||
import sonia.scm.auditlog.Auditor;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.security.AuthorizationChangedEvent;
|
||||
import sonia.scm.security.KeyGenerator;
|
||||
@@ -112,9 +113,13 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
|
||||
private final RepositoryPostProcessor repositoryPostProcessor;
|
||||
|
||||
@Inject
|
||||
public DefaultRepositoryManager(SCMContextProvider contextProvider, KeyGenerator keyGenerator,
|
||||
RepositoryDAO repositoryDAO, Set<RepositoryHandler> handlerSet,
|
||||
Provider<NamespaceStrategy> namespaceStrategyProvider, RepositoryPostProcessor repositoryPostProcessor) {
|
||||
public DefaultRepositoryManager(SCMContextProvider contextProvider,
|
||||
KeyGenerator keyGenerator,
|
||||
RepositoryDAO repositoryDAO,
|
||||
Set<RepositoryHandler> handlerSet,
|
||||
Provider<NamespaceStrategy> namespaceStrategyProvider,
|
||||
RepositoryPostProcessor repositoryPostProcessor,
|
||||
Set<Auditor> auditors) {
|
||||
this.keyGenerator = keyGenerator;
|
||||
this.repositoryDAO = repositoryDAO;
|
||||
this.namespaceStrategyProvider = namespaceStrategyProvider;
|
||||
@@ -126,7 +131,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
|
||||
for (RepositoryHandler handler : handlerSet) {
|
||||
addHandler(contextProvider, handler);
|
||||
}
|
||||
managerDaoAdapter = new ManagerDaoAdapter<>(repositoryDAO);
|
||||
managerDaoAdapter = new ManagerDaoAdapter<>(repositoryDAO, auditors);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
@@ -34,31 +34,35 @@ import sonia.scm.HandlerEventType;
|
||||
import sonia.scm.ManagerDaoAdapter;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.auditlog.Auditor;
|
||||
import sonia.scm.security.RepositoryPermissionProvider;
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Singleton @EagerSingleton
|
||||
public class DefaultRepositoryRoleManager extends AbstractRepositoryRoleManager
|
||||
{
|
||||
@Singleton
|
||||
@EagerSingleton
|
||||
public class DefaultRepositoryRoleManager extends AbstractRepositoryRoleManager {
|
||||
|
||||
/** the logger for XmlRepositoryRoleManager */
|
||||
/**
|
||||
* the logger for XmlRepositoryRoleManager
|
||||
*/
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(DefaultRepositoryRoleManager.class);
|
||||
|
||||
@Inject
|
||||
public DefaultRepositoryRoleManager(RepositoryRoleDAO repositoryRoleDAO, RepositoryPermissionProvider repositoryPermissionProvider)
|
||||
{
|
||||
public DefaultRepositoryRoleManager(RepositoryRoleDAO repositoryRoleDAO,
|
||||
RepositoryPermissionProvider repositoryPermissionProvider,
|
||||
Set<Auditor> auditors) {
|
||||
this.repositoryRoleDAO = repositoryRoleDAO;
|
||||
this.managerDaoAdapter = new ManagerDaoAdapter<>(repositoryRoleDAO);
|
||||
this.managerDaoAdapter = new ManagerDaoAdapter<>(repositoryRoleDAO, auditors);
|
||||
this.repositoryPermissionProvider = repositoryPermissionProvider;
|
||||
}
|
||||
|
||||
@@ -99,6 +103,7 @@ public class DefaultRepositoryRoleManager extends AbstractRepositoryRoleManager
|
||||
|
||||
@Override
|
||||
public void init(SCMContextProvider context) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -179,20 +184,16 @@ public class DefaultRepositoryRoleManager extends AbstractRepositoryRoleManager
|
||||
@Override
|
||||
public Collection<RepositoryRole> getAll(Comparator<RepositoryRole> comaparator, int start, int limit) {
|
||||
return Util.createSubCollection(getAll(), comaparator,
|
||||
(collection, item) -> {
|
||||
collection.add(item.clone());
|
||||
}, start, limit);
|
||||
(collection, item) -> collection.add(item.clone()), start, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RepositoryRole> getAll(int start, int limit)
|
||||
{
|
||||
public Collection<RepositoryRole> getAll(int start, int limit) {
|
||||
return getAll(null, start, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLastModified()
|
||||
{
|
||||
public Long getLastModified() {
|
||||
return repositoryRoleDAO.getLastModified();
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import sonia.scm.HandlerEventType;
|
||||
import sonia.scm.ManagerDaoAdapter;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.auditlog.Auditor;
|
||||
import sonia.scm.search.SearchRequest;
|
||||
import sonia.scm.search.SearchUtil;
|
||||
import sonia.scm.security.Authentications;
|
||||
@@ -48,6 +49,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
@@ -76,11 +78,11 @@ public class DefaultUserManager extends AbstractUserManager
|
||||
* @param userDAO
|
||||
*/
|
||||
@Inject
|
||||
public DefaultUserManager(PasswordService passwordService, UserDAO userDAO)
|
||||
public DefaultUserManager(PasswordService passwordService, UserDAO userDAO, Set<Auditor> auditors)
|
||||
{
|
||||
this.passwordService = passwordService;
|
||||
this.userDAO = userDAO;
|
||||
this.managerDaoAdapter = new ManagerDaoAdapter<>(userDAO);
|
||||
this.managerDaoAdapter = new ManagerDaoAdapter<>(userDAO, auditors);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user