Skip to content

Commit

Permalink
Adding admin as a special case to the suggestions list (#20243)
Browse files Browse the repository at this point in the history
* adding admin as a special case to the suggestions list

* fixing paging

* name refactoring

* small refactorings, fixing Title-Service

* include admin in filtering

* using default locale

* using english locale

* minor code improvements

* minor simplifications
  • Loading branch information
janheise committed Sep 9, 2024
1 parent 5e0d3aa commit 327265d
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.graylog2.database.suggestions;

import com.google.common.base.Strings;
import com.google.common.collect.Streams;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
Expand All @@ -31,10 +32,14 @@
import org.graylog2.shared.security.EntityPermissionsUtils;

import java.util.List;
import java.util.Locale;
import java.util.function.Predicate;
import java.util.stream.Stream;

import static org.graylog2.shared.security.EntityPermissionsUtils.ID_FIELD;
import static org.graylog2.users.UserImpl.COLLECTION_NAME;
import static org.graylog2.users.UserImpl.LocalAdminUser.LOCAL_ADMIN_ID;
import static org.graylog2.users.UserImpl.USERNAME;

public class MongoEntitySuggestionService implements EntitySuggestionService {

Expand All @@ -48,6 +53,10 @@ public MongoEntitySuggestionService(final MongoConnection mongoConnection,
this.permissionsUtils = permissionsUtils;
}

private boolean addAdminToSuggestions(final String collection, final String valueColumn, final boolean filterIsEmpty, final String query) {
return COLLECTION_NAME.equals(collection) && USERNAME.equals(valueColumn) && (filterIsEmpty || LOCAL_ADMIN_ID.contains(query.toLowerCase(Locale.ENGLISH)));
}

@Override
public EntitySuggestionResponse suggest(final String collection,
final String valueColumn,
Expand All @@ -56,8 +65,12 @@ public EntitySuggestionResponse suggest(final String collection,
final int perPage,
final Subject subject) {
final MongoCollection<Document> mongoCollection = mongoConnection.getMongoDatabase().getCollection(collection);
final boolean filterIsEmpty = Strings.isNullOrEmpty(query);
final boolean isSpecialCollection = addAdminToSuggestions(collection, valueColumn, filterIsEmpty, query);
final var isFirstPageAndSpecialCollection = isSpecialCollection && page == 1;
final var fixNumberOfItemsToReadFromDB = isFirstPageAndSpecialCollection ? 1 : 0;

final var bsonFilter = !Strings.isNullOrEmpty(query)
final var bsonFilter = !filterIsEmpty
? Filters.regex(valueColumn, query, "i")
: Filters.empty();

Expand All @@ -67,20 +80,23 @@ public EntitySuggestionResponse suggest(final String collection,
.sort(Sorts.ascending(valueColumn));

final var userCanReadAllEntities = permissionsUtils.hasAllPermission(subject) || permissionsUtils.hasReadPermissionForWholeCollection(subject, collection);
final var skip = (page - 1) * perPage;
final var skip = Math.max(0, (page - 1) * perPage - fixNumberOfItemsToReadFromDB);
final var checkPermission = permissionsUtils.createPermissionCheck(subject, collection);
final var documents = userCanReadAllEntities
? mongoPaginate(resultWithoutPagination, perPage, skip)
: paginateWithPermissionCheck(resultWithoutPagination, perPage, skip, checkPermission);
? mongoPaginate(resultWithoutPagination, perPage - fixNumberOfItemsToReadFromDB, skip)
: paginateWithPermissionCheck(resultWithoutPagination, perPage - fixNumberOfItemsToReadFromDB, skip, checkPermission);

final List<EntitySuggestion> suggestions = documents
final List<EntitySuggestion> staticEntry = isFirstPageAndSpecialCollection ? List.of(new EntitySuggestion(LOCAL_ADMIN_ID, "admin")) : List.of();

final Stream<EntitySuggestion> suggestionsFromDB = documents
.map(doc ->
new EntitySuggestion(
doc.getObjectId(ID_FIELD).toString(),
doc.getString(valueColumn)
)
)
.toList();
);

final List<EntitySuggestion> suggestions = Streams.concat(staticEntry.stream(), suggestionsFromDB).toList();

final long total = userCanReadAllEntities
? mongoCollection.countDocuments(bsonFilter)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
import static java.util.stream.Collectors.groupingBy;
import static org.graylog2.database.DbEntity.NO_TITLE;
import static org.graylog2.rest.resources.system.contentpacks.titles.model.EntitiesTitleResponse.EMPTY_RESPONSE;
import static org.graylog2.users.UserImpl.COLLECTION_NAME;
import static org.graylog2.users.UserImpl.LocalAdminUser.LOCAL_ADMIN_ID;

public class EntityTitleServiceImpl implements EntityTitleService {

Expand Down Expand Up @@ -77,54 +79,72 @@ public EntitiesTitleResponse getTitles(final EntityTitleRequest request, final E
return entitiesTitleResponse.orElse(EMPTY_RESPONSE);
}

private boolean matchesAdminIdAndUsersCollection(final EntityIdentifier entity) {
return entity.id().equals(LOCAL_ADMIN_ID) && entity.type().equals(COLLECTION_NAME);
}

private boolean notMatchesAdminIdAndUsersCollection(final EntityIdentifier entity) {
return !matchesAdminIdAndUsersCollection(entity);
}

private EntitiesTitleResponse getTitlesForEntitiesFromSingleCollection(final EntityPermissions permissions,
final String collection,
final List<EntityIdentifier> entities) {
final List<EntityIdentifier> en) {
final Optional<DbEntityCatalogEntry> dbEntityCatalogEntry = this.entitiesCatalog.getByCollectionName(collection);
if (dbEntityCatalogEntry.isEmpty() || entities.isEmpty()) {
if (dbEntityCatalogEntry.isEmpty() || en.isEmpty()) {
return EMPTY_RESPONSE;
}

final String titleField = dbEntityCatalogEntry.get().titleField();
if (titleField.equals(NO_TITLE)) {
return new EntitiesTitleResponse(
entities.stream()
.map(e -> new EntityTitleResponse(e.id(), e.type(), NO_TITLE))
.collect(Collectors.toSet()),
Set.of()
);
}
boolean adminIdAndUsersCollectionFound = en.stream().anyMatch(this::matchesAdminIdAndUsersCollection);
List<EntityIdentifier> entities = adminIdAndUsersCollectionFound ? en.stream().filter(this::notMatchesAdminIdAndUsersCollection).toList() : en;

final Set<EntityTitleResponse> titles = new HashSet<>();
final Set<String> notPermitted = new HashSet<>();

final MongoCollection<Document> mongoCollection = mongoConnection.getMongoDatabase().getCollection(collection);
if(!entities.isEmpty()) {
final String titleField = dbEntityCatalogEntry.get().titleField();
if (titleField.equals(NO_TITLE)) {
return new EntitiesTitleResponse(
entities.stream()
.map(e -> new EntityTitleResponse(e.id(), e.type(), NO_TITLE))
.collect(Collectors.toSet()),
Set.of()
);
}

Bson bsonFilter = Filters.or(
entities.stream()
.map(e -> Filters.eq("_id", new ObjectId(e.id())))
.collect(Collectors.toList())
);
final MongoCollection<Document> mongoCollection = mongoConnection.getMongoDatabase().getCollection(collection);

final FindIterable<Document> documents = mongoCollection
.find(bsonFilter)
.projection(Projections.include(titleField));
Bson bsonFilter = Filters.or(
entities.stream()
.map(e -> Filters.eq("_id", new ObjectId(e.id())))
.collect(Collectors.toList())
);

final Set<EntityTitleResponse> titles = new HashSet<>();
final Set<String> notPermitted = new HashSet<>();
documents.forEach(doc ->
{
final String idAsString = doc.getObjectId("_id").toString();
final boolean canReadTitle = checkCanReadTitle(permissions, dbEntityCatalogEntry.get().readPermission(), idAsString);
titles.add(
new EntityTitleResponse(
idAsString,
collection,
canReadTitle ? doc.getString(titleField) : TITLE_IF_NOT_PERMITTED
)
);
if (!canReadTitle) {
notPermitted.add(idAsString);
final FindIterable<Document> documents = mongoCollection
.find(bsonFilter)
.projection(Projections.include(titleField));

documents.forEach(doc ->
{
final String idAsString = doc.getObjectId("_id").toString();
final boolean canReadTitle = checkCanReadTitle(permissions, dbEntityCatalogEntry.get().readPermission(), idAsString);
titles.add(
new EntityTitleResponse(
idAsString,
collection,
canReadTitle ? doc.getString(titleField) : TITLE_IF_NOT_PERMITTED
)
);
if (!canReadTitle) {
notPermitted.add(idAsString);
}
}
}
);
);
}

if(adminIdAndUsersCollectionFound) {
titles.add(new EntityTitleResponse(LOCAL_ADMIN_ID, collection, "Administrator"));
}

return new EntitiesTitleResponse(titles, notPermitted);
}
Expand Down

0 comments on commit 327265d

Please sign in to comment.