diff --git a/graylog2-server/src/main/java/org/graylog2/database/suggestions/MongoEntitySuggestionService.java b/graylog2-server/src/main/java/org/graylog2/database/suggestions/MongoEntitySuggestionService.java index dbe3961096a1..ccd2215e3ccd 100644 --- a/graylog2-server/src/main/java/org/graylog2/database/suggestions/MongoEntitySuggestionService.java +++ b/graylog2-server/src/main/java/org/graylog2/database/suggestions/MongoEntitySuggestionService.java @@ -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; @@ -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 { @@ -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, @@ -56,8 +65,12 @@ public EntitySuggestionResponse suggest(final String collection, final int perPage, final Subject subject) { final MongoCollection 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(); @@ -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 suggestions = documents + final List staticEntry = isFirstPageAndSpecialCollection ? List.of(new EntitySuggestion(LOCAL_ADMIN_ID, "admin")) : List.of(); + + final Stream suggestionsFromDB = documents .map(doc -> new EntitySuggestion( doc.getObjectId(ID_FIELD).toString(), doc.getString(valueColumn) ) - ) - .toList(); + ); + + final List suggestions = Streams.concat(staticEntry.stream(), suggestionsFromDB).toList(); final long total = userCanReadAllEntities ? mongoCollection.countDocuments(bsonFilter) diff --git a/graylog2-server/src/main/java/org/graylog2/rest/resources/system/contentpacks/titles/EntityTitleServiceImpl.java b/graylog2-server/src/main/java/org/graylog2/rest/resources/system/contentpacks/titles/EntityTitleServiceImpl.java index 581deff6a0fa..3bea54a8dee3 100644 --- a/graylog2-server/src/main/java/org/graylog2/rest/resources/system/contentpacks/titles/EntityTitleServiceImpl.java +++ b/graylog2-server/src/main/java/org/graylog2/rest/resources/system/contentpacks/titles/EntityTitleServiceImpl.java @@ -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 { @@ -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 entities) { + final List en) { final Optional 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 entities = adminIdAndUsersCollectionFound ? en.stream().filter(this::notMatchesAdminIdAndUsersCollection).toList() : en; + + final Set titles = new HashSet<>(); + final Set notPermitted = new HashSet<>(); - final MongoCollection 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 mongoCollection = mongoConnection.getMongoDatabase().getCollection(collection); - final FindIterable 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 titles = new HashSet<>(); - final Set 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 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); }