All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.graylog.security.entities.EntityDependencyResolver Maven / Gradle / Ivy

There is a newer version: 6.1.4
Show newest version
/*
 * Copyright (C) 2020 Graylog, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Server Side Public License, version 1,
 * as published by MongoDB, Inc.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * Server Side Public License for more details.
 *
 * You should have received a copy of the Server Side Public License
 * along with this program. If not, see
 * .
 */
package org.graylog.security.entities;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.graylog.grn.GRN;
import org.graylog.grn.GRNDescriptorService;
import org.graylog.grn.GRNRegistry;
import org.graylog.grn.GRNType;
import org.graylog.grn.GRNTypes;
import org.graylog.security.DBGrantService;
import org.graylog.security.shares.Grantee;
import org.graylog2.contentpacks.ContentPackService;
import org.graylog2.contentpacks.model.ModelId;
import org.graylog2.contentpacks.model.ModelType;
import org.graylog2.contentpacks.model.ModelTypes;

import javax.annotation.Nullable;

import jakarta.inject.Inject;

import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static com.google.common.base.MoreObjects.firstNonNull;

public class EntityDependencyResolver {
    private final ContentPackService contentPackService;
    private final GRNRegistry grnRegistry;
    private final GRNDescriptorService descriptorService;
    private final DBGrantService grantService;
    // Some dependencies can be ignored.
    // E.g. To view a stream with a custom output, a user does not need output permissions
    private static final Map> IGNORED_DEPENDENCIES = ImmutableMap.>builder()
            .put(GRNTypes.SEARCH, ImmutableSet.of(ModelTypes.OUTPUT_V1))
            .put(GRNTypes.STREAM, ImmutableSet.of(ModelTypes.OUTPUT_V1))
            .put(GRNTypes.DASHBOARD, ImmutableSet.of(ModelTypes.OUTPUT_V1))
            .build();

    @Inject
    public EntityDependencyResolver(ContentPackService contentPackService,
                                    GRNRegistry grnRegistry,
                                    GRNDescriptorService descriptorService,
                                    DBGrantService grantService) {
        this.contentPackService = contentPackService;
        this.grnRegistry = grnRegistry;
        this.descriptorService = descriptorService;
        this.grantService = grantService;
    }

    public ImmutableSet resolve(GRN entity) {
        // TODO: Replace entity excerpt usage with GRNDescriptors once we implemented GRN descriptors for every entity
        final ImmutableMap> entityExcerpts = contentPackService.listAllEntityExcerpts().stream()
                // TODO: Use the GRNRegistry instead of manually building a GRN. Requires all entity types to be in the registry.
                .collect(ImmutableMap.toImmutableMap(e -> GRNType.create(e.type().name(), e.type().name() + ":").newGRNBuilder().entity(e.id().id()).build(),
                        v -> Optional.ofNullable(v.title())));

        final Set descriptors = contentPackService.resolveEntities(Collections.singleton(org.graylog2.contentpacks.model.entities.EntityDescriptor.builder()
                .id(ModelId.of(entity.entity()))
                // TODO: This is a hack! Until we stop using the content-pack dependency resolver, we have to use a different version for dashboards here
                .type(ModelType.of(entity.type(), "dashboard".equals(entity.type()) ? "2" : "1")) // TODO: Any way of NOT hardcoding the version here?
                .build()));

        final ImmutableSet dependencies = descriptors.stream()
                .filter(dep -> {
                    // Filter dependencies that aren't needed for grants sharing
                    // TODO This is another reason why we shouldn't be using the content pack resolver ¯\_(ツ)_/¯
                    final Set ignoredDeps = IGNORED_DEPENDENCIES.getOrDefault(entity.grnType(), ImmutableSet.of());
                    return !ignoredDeps.contains(dep.type());
                })
                // TODO: Work around from using the content pack dependency resolver:
                //  We've added stream_title content pack entities in https://github.com/Graylog2/graylog2-server/pull/17089,
                //  but in this context we want to return the actual dependent Stream to add additional permissions to.
                .map(descriptor -> ModelTypes.STREAM_REF_V1.equals(descriptor.type())
                        ? org.graylog2.contentpacks.model.entities.EntityDescriptor.create(descriptor.id(), ModelTypes.STREAM_V1)
                        : descriptor)
                .map(descriptor -> grnRegistry.newGRN(descriptor.type().name(), descriptor.id().id()))
                .filter(dependency -> !entity.equals(dependency)) // Don't include the given entity in dependencies
                .collect(ImmutableSet.toImmutableSet());

        final Map> targetOwners = grantService.getOwnersForTargets(dependencies);

        return dependencies.stream()
                .map(dependency -> {
                    String title = entityExcerpts.get(dependency) != null ? entityExcerpts.get(dependency).orElse("unnamed dependency: <" + dependency + ">") : "unknown dependency: <" + dependency + ">";
                    return EntityDescriptor.create(
                            dependency,
                            title,
                            getOwners(targetOwners.get(dependency))
                    );
                })
                .collect(ImmutableSet.toImmutableSet());
    }

    private Set getOwners(@Nullable Set owners) {
        return firstNonNull(owners, Collections.emptySet()).stream()
                .map(descriptorService::getDescriptor)
                // TODO there is a duplicate in GranteeSharesService
                .map(descriptor -> {
                            if (descriptor.grn().equals(GRNRegistry.GLOBAL_USER_GRN)) {
                                return Grantee.createGlobal();
                            }
                            return Grantee.create(descriptor.grn(), descriptor.grn().type(), descriptor.title());
                        }
                )
                .collect(Collectors.toSet());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy