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

org.graylog.security.shares.GranteeSharesService Maven / Gradle / Ivy

/*
 * 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.shares;

import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.primitives.Ints;
import org.graylog.grn.GRN;
import org.graylog.grn.GRNDescriptor;
import org.graylog.grn.GRNDescriptorService;
import org.graylog.grn.GRNRegistry;
import org.graylog.security.Capability;
import org.graylog.security.DBGrantService;
import org.graylog.security.GrantDTO;
import org.graylog.security.entities.EntityDescriptor;
import org.graylog2.database.PaginatedList;
import org.graylog2.rest.PaginationParameters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

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

public class GranteeSharesService {
    private static final Logger LOG = LoggerFactory.getLogger(GranteeSharesService.class);

    private final DBGrantService grantService;
    private final GRNDescriptorService descriptorService;
    private final GranteeService granteeService;

    @Inject
    public GranteeSharesService(DBGrantService grantService,
                                GRNDescriptorService descriptorService, GranteeService granteeService) {
        this.grantService = grantService;
        this.descriptorService = descriptorService;
        this.granteeService = granteeService;
    }

    public SharesResponse getPaginatedSharesFor(GRN grantee,
                                                PaginationParameters paginationParameters,
                                                String capabilityFilterString,
                                                String entityTypeFilterString) {
        final Optional capability = parseCapabilityFilter(capabilityFilterString);
        // Get all aliases for the grantee to make sure we find all entities the grantee has access to
        final Set granteeAliases = granteeService.getGranteeAliases(grantee);
        final ImmutableSet grants = capability
                .map(c -> grantService.getForGranteesOrGlobalWithCapability(granteeAliases, c))
                .orElseGet(() -> grantService.getForGranteesOrGlobal(granteeAliases));

        final Set targets = grants.stream().map(GrantDTO::target).collect(Collectors.toSet());

        final Map> targetOwners = getTargetOwners(targets);

        final Supplier> filteredStream = () -> targets.stream()
                .map(descriptorService::getDescriptor)
                .filter(queryPredicate(paginationParameters))
                .filter(entityTypeFilterPredicate(entityTypeFilterString))
                .map(toEntityDescriptor(targetOwners))
                .sorted(Comparator.comparing(EntityDescriptor::title, (t1, t2) -> {
                    if (paginationParameters.getOrder().toLowerCase(Locale.US).equals("desc")) {
                        return t2.compareTo(t1);
                    }
                    return t1.compareTo(t2);
                }));

        final int filteredResultCount = Ints.saturatedCast(filteredStream.get().count());

        final List entityDescriptors = filteredStream.get()
                .skip(paginationParameters.getPerPage() * (paginationParameters.getPage() - 1))
                .limit(paginationParameters.getPerPage())
                .collect(Collectors.toList());

        final Set entityDescriptorsGRNs = entityDescriptors.stream()
                .map(EntityDescriptor::id)
                .collect(Collectors.toSet());

        final Map granteeCapabilities = grants.stream()
                .filter(grant -> entityDescriptorsGRNs.contains(grant.target()))
                // Group grants by target so we can select the grant with the highest capability priority later
                .collect(Collectors.groupingBy(GrantDTO::target))
                .values()
                .stream()
                // Select the grant with the highest capability priority
                .map(grantsList -> grantsList.stream().max(Comparator.comparing(grant -> grant.capability().priority())))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toMap(GrantDTO::target, GrantDTO::capability));

        final PaginatedList paginatedList = new PaginatedList<>(
                entityDescriptors,
                filteredResultCount,
                paginationParameters.getPage(),
                paginationParameters.getPerPage(),
                (long) targets.size()
        );

        return SharesResponse.create(paginatedList, granteeCapabilities);
    }

    private Function toEntityDescriptor(Map> targetOwners) {
        return grnDescriptor -> EntityDescriptor.create(
                grnDescriptor.grn(),
                grnDescriptor.title(),
                targetOwners.getOrDefault(grnDescriptor.grn(), Collections.emptySet())
        );
    }

    private Map> getTargetOwners(Set targets) {
        return grantService.getOwnersForTargets(targets).entrySet()
                .stream()
                .map(entry -> Maps.immutableEntry(entry.getKey(), getOwners(entry)))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }

    private Set getOwners(Map.Entry> entry) {
        return descriptorService.getDescriptors(entry.getValue())
                .stream()
                .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());
    }

    private Optional parseCapabilityFilter(String capabilityFilterString) {
        final String capabilityFilter = firstNonNull(capabilityFilterString, "").trim().toUpperCase(Locale.US);

        if (capabilityFilter.isEmpty()) {
            return Optional.empty();
        }

        try {
            return Optional.of(Capability.valueOf(capabilityFilter));
        } catch (IllegalArgumentException e) {
            LOG.warn("Unknown capability", e);
            return Optional.empty();
        }
    }

    private Predicate queryPredicate(PaginationParameters paginationParameters) {
        final String query = firstNonNull(paginationParameters.getQuery(), "").trim().toLowerCase(Locale.US);

        if (query.isEmpty()) {
            return descriptor -> true;
        }

        return descriptor -> descriptor.title().toLowerCase(Locale.US).contains(query);
    }

    private Predicate entityTypeFilterPredicate(String entityTypeFilter) {
        final String type = firstNonNull(entityTypeFilter, "").trim().toLowerCase(Locale.US);

        if (type.isEmpty()) {
            return descriptor -> true;
        }

        return descriptor -> descriptor.grn().type().equals(type);
    }

    @AutoValue
    public static abstract class SharesResponse {
        public abstract PaginatedList paginatedEntities();

        public abstract Map capabilities();

        public static SharesResponse create(PaginatedList paginatedEntities, Map capabilities) {
            return new AutoValue_GranteeSharesService_SharesResponse(paginatedEntities, capabilities);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy