mirror of
https://github.com/Graylog2/graylog2-server.git
synced 2026-03-13 09:32:21 +08:00
Fixing recent activity for non-admins. (#24080)
* Fixing recent activity for non-admins. * Adding changelog snippet. * Fixing up call. * Adjusting tests. * Also take static permissions (e.g. through roles) into account when checking activity visibility. * Fix links for dashboards. * Fixing up after merge.
This commit is contained in:
5
changelog/unreleased/pr-24080.toml
Normal file
5
changelog/unreleased/pr-24080.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
type = "f"
|
||||
message = "Fixing recent activity for non-admins: Including update events to accessible entities."
|
||||
|
||||
pulls = ["24080"]
|
||||
issues = ["Graylog2/graylog-plugin-enterprise#12427"]
|
||||
@@ -79,7 +79,9 @@ public class DBEventDefinitionService {
|
||||
@Inject
|
||||
public DBEventDefinitionService(MongoCollections mongoCollections,
|
||||
DBEventProcessorStateService stateService,
|
||||
EntityRegistrar entityRegistrar, EntityScopeService entityScopeService, SearchFiltersReFetcher searchFiltersRefetcher) {
|
||||
EntityRegistrar entityRegistrar,
|
||||
EntityScopeService entityScopeService,
|
||||
SearchFiltersReFetcher searchFiltersRefetcher) {
|
||||
this.collection = mongoCollections.collection(COLLECTION_NAME, EventDefinitionDto.class);
|
||||
this.mongoUtils = mongoCollections.utils(collection);
|
||||
this.scopedEntityMongoUtils = mongoCollections.scopedEntityUtils(collection, entityScopeService);
|
||||
|
||||
@@ -16,27 +16,37 @@
|
||||
*/
|
||||
package org.graylog.plugins.views.startpage.recentActivities;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import jakarta.inject.Singleton;
|
||||
import org.graylog2.database.MongoCollection;
|
||||
import com.mongodb.client.model.CreateCollectionOptions;
|
||||
import com.mongodb.client.model.Filters;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import org.graylog.grn.GRN;
|
||||
import org.graylog.grn.GRNRegistry;
|
||||
import org.graylog.grn.GRNType;
|
||||
import org.graylog.grn.GRNTypes;
|
||||
import org.graylog.plugins.views.search.permissions.SearchUser;
|
||||
import org.graylog.security.Capability;
|
||||
import org.graylog.security.CapabilityRegistry;
|
||||
import org.graylog.security.DBGrantService;
|
||||
import org.graylog.security.GrantDTO;
|
||||
import org.graylog.security.PermissionAndRoleResolver;
|
||||
import org.graylog.security.shares.GranteeService;
|
||||
import org.graylog.security.shares.PluggableEntityService;
|
||||
import org.graylog2.database.MongoCollection;
|
||||
import org.graylog2.database.MongoCollections;
|
||||
import org.graylog2.database.MongoConnection;
|
||||
import org.graylog2.database.PaginatedList;
|
||||
import org.graylog2.database.pagination.MongoPaginationHelper;
|
||||
import org.graylog2.plugin.database.users.User;
|
||||
import org.graylog2.plugin.security.Permission;
|
||||
import org.graylog2.rest.models.SortOrder;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Singleton
|
||||
public class RecentActivityService {
|
||||
@@ -48,14 +58,23 @@ public class RecentActivityService {
|
||||
private static final long MAXIMUM_RECENT_ACTIVITIES = 10000;
|
||||
private final MongoCollection<RecentActivityDTO> db;
|
||||
private final MongoPaginationHelper<RecentActivityDTO> pagination;
|
||||
private final DBGrantService grantService;
|
||||
private final GranteeService granteeService;
|
||||
private final PluggableEntityService pluggableEntityService;
|
||||
private CapabilityRegistry capabilityRegistry;
|
||||
|
||||
@Inject
|
||||
public RecentActivityService(final MongoCollections mongoCollections,
|
||||
final MongoConnection mongoConnection,
|
||||
final EventBus eventBus,
|
||||
final GRNRegistry grnRegistry,
|
||||
final PermissionAndRoleResolver permissionAndRoleResolver) {
|
||||
this(mongoCollections, mongoConnection, eventBus, grnRegistry, permissionAndRoleResolver, MAXIMUM_RECENT_ACTIVITIES);
|
||||
final PermissionAndRoleResolver permissionAndRoleResolver,
|
||||
final DBGrantService grantService,
|
||||
final GranteeService granteeService,
|
||||
final PluggableEntityService pluggableEntityService,
|
||||
final CapabilityRegistry capabilityRegistry) {
|
||||
this(mongoCollections, mongoConnection, eventBus, grnRegistry, permissionAndRoleResolver, MAXIMUM_RECENT_ACTIVITIES,
|
||||
grantService, granteeService, pluggableEntityService, capabilityRegistry);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -66,7 +85,15 @@ public class RecentActivityService {
|
||||
final EventBus eventBus,
|
||||
final GRNRegistry grnRegistry,
|
||||
final PermissionAndRoleResolver permissionAndRoleResolver,
|
||||
final long maximum) {
|
||||
final long maximum,
|
||||
final DBGrantService grantService,
|
||||
final GranteeService granteeService,
|
||||
final PluggableEntityService pluggableEntityService,
|
||||
final CapabilityRegistry capabilityRegistry) {
|
||||
this.grantService = grantService;
|
||||
this.granteeService = granteeService;
|
||||
this.pluggableEntityService = pluggableEntityService;
|
||||
this.capabilityRegistry = capabilityRegistry;
|
||||
final var mongodb = mongoConnection.getMongoDatabase();
|
||||
if (!mongodb.listCollectionNames().into(new HashSet<>()).contains(COLLECTION_NAME)) {
|
||||
mongodb.createCollection(COLLECTION_NAME, new CreateCollectionOptions().capped(true).sizeInBytes(maximum * 1024).maxDocuments(maximum));
|
||||
@@ -122,15 +149,33 @@ public class RecentActivityService {
|
||||
|
||||
// filter relevant activities by permissions
|
||||
final var principal = grnRegistry.newGRN(GRNTypes.USER, user.getUser().getId());
|
||||
final var grns = permissionAndRoleResolver.resolveGrantees(principal).stream().map(GRN::toString).toList();
|
||||
var query = Filters.in(RecentActivityDTO.FIELD_GRANTEE, grns);
|
||||
final var grantees = permissionAndRoleResolver.resolveGrantees(principal).stream().map(GRN::toString).toList();
|
||||
final var sharedEntities = getShareGRNsFor(principal);
|
||||
return pagination
|
||||
.perPage(perPage)
|
||||
.sort(sort)
|
||||
.filter(query)
|
||||
.includeGrandTotal(true)
|
||||
.grandTotalFilter(query)
|
||||
.page(page);
|
||||
.page(page, activity -> {
|
||||
final var itemGRN = activity.itemGrn();
|
||||
final var hasAnyPermission = capabilityRegistry.getPermissions(Capability.VIEW, itemGRN.grnType())
|
||||
.stream()
|
||||
.map(Permission::permission)
|
||||
.anyMatch(permission -> user.isPermitted(permission, itemGRN.entity()));
|
||||
return hasAnyPermission || grantees.contains(activity.grantee()) || sharedEntities.contains(itemGRN);
|
||||
});
|
||||
}
|
||||
|
||||
private Set<GRN> getShareGRNsFor(GRN grantee) {
|
||||
// Get all aliases for the grantee to make sure we find all entities the grantee has access to
|
||||
final Set<GRN> granteeAliases = granteeService.getGranteeAliases(grantee);
|
||||
|
||||
final ImmutableSet<GrantDTO> grants = grantService.getForGranteesOrGlobalWithCapability(granteeAliases, Capability.VIEW);
|
||||
|
||||
return grants.stream()
|
||||
.map(GrantDTO::target)
|
||||
.flatMap(pluggableEntityService::expand)
|
||||
.filter(pluggableEntityService.excludeTypesFilter())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public void deleteAllEntriesForEntity(GRN grn) {
|
||||
|
||||
@@ -31,7 +31,11 @@ import org.graylog.plugins.views.startpage.lastOpened.LastOpenedForUserDTO;
|
||||
import org.graylog.plugins.views.startpage.lastOpened.LastOpenedService;
|
||||
import org.graylog.plugins.views.startpage.recentActivities.RecentActivityService;
|
||||
import org.graylog.plugins.views.startpage.title.StartPageItemTitleRetriever;
|
||||
import org.graylog.security.CapabilityRegistry;
|
||||
import org.graylog.security.DBGrantService;
|
||||
import org.graylog.security.PermissionAndRoleResolver;
|
||||
import org.graylog.security.shares.GranteeService;
|
||||
import org.graylog.security.shares.PluggableEntityService;
|
||||
import org.graylog.testing.GRNExtension;
|
||||
import org.graylog.testing.TestUserServiceExtension;
|
||||
import org.graylog.testing.mongodb.MongoDBExtension;
|
||||
@@ -75,11 +79,9 @@ public class StartPageServiceTest {
|
||||
MongoCollections mongoCollections,
|
||||
GRNRegistry grnRegistry) {
|
||||
|
||||
var admin = TestUser.builder().withId("637748db06e1d74da0a54331").withUsername("local:admin").isLocalAdmin(true).build();
|
||||
var user = TestUser.builder().withId("637748db06e1d74da0a54330").withUsername("test").isLocalAdmin(false).build();
|
||||
this.searchUser = TestSearchUser.builder().withUser(user).build();
|
||||
this.grnRegistry = grnRegistry;
|
||||
var searchAdmin = TestSearchUser.builder().withUser(admin).build();
|
||||
|
||||
var permissionAndRoleResolver = new PermissionAndRoleResolver() {
|
||||
@Override
|
||||
@@ -101,7 +103,8 @@ public class StartPageServiceTest {
|
||||
var eventbus = new EventBus();
|
||||
final var connection = mongodb.mongoConnection();
|
||||
var lastOpenedService = new LastOpenedService(mongoCollections, eventbus);
|
||||
var recentActivityService = new RecentActivityService(mongoCollections, connection, eventbus, grnRegistry, permissionAndRoleResolver);
|
||||
var recentActivityService = new RecentActivityService(mongoCollections, connection, eventbus, grnRegistry, permissionAndRoleResolver,
|
||||
new DBGrantService(mongoCollections), mock(GranteeService.class), new PluggableEntityService(Set.of()), mock(CapabilityRegistry.class));
|
||||
catalog = mock(Catalog.class);
|
||||
doReturn(Optional.of(new Catalog.Entry("", ""))).when(catalog).getEntry(any());
|
||||
startPageService = new StartPageService(grnRegistry, lastOpenedService, recentActivityService, eventbus, new StartPageItemTitleRetriever(catalog, Map.of()));
|
||||
|
||||
@@ -23,7 +23,11 @@ import org.graylog.grn.GRNTypes;
|
||||
import org.graylog.plugins.views.search.permissions.SearchUser;
|
||||
import org.graylog.plugins.views.search.rest.TestSearchUser;
|
||||
import org.graylog.plugins.views.search.rest.TestUser;
|
||||
import org.graylog.security.CapabilityRegistry;
|
||||
import org.graylog.security.DBGrantService;
|
||||
import org.graylog.security.PermissionAndRoleResolver;
|
||||
import org.graylog.security.shares.GranteeService;
|
||||
import org.graylog.security.shares.PluggableEntityService;
|
||||
import org.graylog.testing.GRNExtension;
|
||||
import org.graylog.testing.TestUserService;
|
||||
import org.graylog.testing.TestUserServiceExtension;
|
||||
@@ -40,6 +44,7 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@ExtendWith(MongoDBExtension.class)
|
||||
@ExtendWith(MongoJackExtension.class)
|
||||
@@ -89,12 +94,18 @@ public class RecentActivityServiceTest {
|
||||
|
||||
this.testUserService = testUserService;
|
||||
this.grnRegistry = grnRegistry;
|
||||
this.recentActivityService = new RecentActivityService(mongoCollections,
|
||||
this.recentActivityService = new RecentActivityService(
|
||||
mongoCollections,
|
||||
mongodb.mongoConnection(),
|
||||
null,
|
||||
grnRegistry,
|
||||
permissionAndRoleResolver,
|
||||
MAXIMUM);
|
||||
MAXIMUM,
|
||||
new DBGrantService(mongoCollections),
|
||||
mock(GranteeService.class),
|
||||
new PluggableEntityService(Set.of()),
|
||||
mock(CapabilityRegistry.class)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -17,18 +17,17 @@
|
||||
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import useCurrentUser from 'hooks/useCurrentUser';
|
||||
import getPermissionPrefixByType from 'util/getPermissionPrefixByType';
|
||||
import { isPermitted } from 'util/PermissionsMixin';
|
||||
import { getValuesFromGRN } from 'logic/permissions/GRN';
|
||||
import usePermissions from 'hooks/usePermissions';
|
||||
|
||||
const useHasEntityPermissionByGRN = (grn: string, permissionType: string = 'read') => {
|
||||
const { id, type } = getValuesFromGRN(grn);
|
||||
const { permissions } = useCurrentUser();
|
||||
const { isPermitted } = usePermissions();
|
||||
|
||||
return useMemo(
|
||||
() => isPermitted(permissions, `${getPermissionPrefixByType(type, id)}${permissionType}:${id}`),
|
||||
[id, permissionType, permissions, type],
|
||||
() => isPermitted(`${getPermissionPrefixByType(type, id)}${permissionType}:${id}`),
|
||||
[id, permissionType, isPermitted, type],
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ const typePrefixCornerCasesMap = {
|
||||
event_definition: 'eventdefinitions:',
|
||||
notification: 'eventnotifications:',
|
||||
search: 'view:',
|
||||
dashboard: 'view:',
|
||||
};
|
||||
|
||||
const standardEntityPermissionsMapper: EntityPermissionsMapper = {
|
||||
|
||||
Reference in New Issue
Block a user