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

com.google.gerrit.server.project.PermissionCollection Maven / Gradle / Ivy

There is a newer version: 3.10.0-rc4
Show newest version
// Copyright (C) 2011 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.gerrit.server.project;

import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.gerrit.server.project.RefPattern.isRE;

import com.google.auto.value.AutoValue;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.MultimapBuilder;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Effective permissions applied to a reference in a project.
 *
 * 

A collection may be user specific if a matching {@link AccessSection} uses "${username}" in * its name. The permissions granted in that section may only be granted to the username that * appears in the reference name, and also only if the user is a member of the relevant group. */ public class PermissionCollection { @Singleton public static class Factory { private final SectionSortCache sorter; @Inject Factory(SectionSortCache sorter) { this.sorter = sorter; } /** * Get all permissions that apply to a reference. * * @param matcherList collection of sections that should be considered, in priority order * (project specific definitions must appear before inherited ones). * @param ref reference being accessed. * @param user if the reference is a per-user reference, e.g. access sections using the * parameter variable "${username}" will have each username inserted into them to see if * they apply to the reference named by {@code ref}. * @return map of permissions that apply to this reference, keyed by permission name. */ PermissionCollection filter( Iterable matcherList, String ref, CurrentUser user) { if (isRE(ref)) { ref = RefPattern.shortestExample(ref); } else if (ref.endsWith("/*")) { ref = ref.substring(0, ref.length() - 1); } boolean perUser = false; Map sectionToProject = new LinkedHashMap<>(); for (SectionMatcher sm : matcherList) { // If the matcher has to expand parameters and its prefix matches the // reference there is a very good chance the reference is actually user // specific, even if the matcher does not match the reference. Since its // difficult to prove this is true all of the time, use an approximation // to prevent reuse of collections across users accessing the same // reference at the same time. // // This check usually gets caching right, as most per-user references // use a common prefix like "refs/sandbox/" or "refs/heads/users/" // that will never be shared with non-user references, and the per-user // references are usually less frequent than the non-user references. // if (sm.matcher instanceof RefPatternMatcher.ExpandParameters) { if (!((RefPatternMatcher.ExpandParameters) sm.matcher).matchPrefix(ref)) { continue; } perUser = true; if (sm.match(ref, user)) { sectionToProject.put(sm.section, sm.project); } } else if (sm.match(ref, null)) { sectionToProject.put(sm.section, sm.project); } } List sections = Lists.newArrayList(sectionToProject.keySet()); sorter.sort(ref, sections); Set seen = new HashSet<>(); Set exclusiveGroupPermissions = new HashSet<>(); HashMap> permissions = new HashMap<>(); HashMap> overridden = new HashMap<>(); Map ruleProps = Maps.newIdentityHashMap(); ListMultimap exclusivePermissionsByProject = MultimapBuilder.hashKeys().arrayListValues().build(); for (AccessSection section : sections) { Project.NameKey project = sectionToProject.get(section); for (Permission permission : section.getPermissions()) { boolean exclusivePermissionExists = exclusiveGroupPermissions.contains(permission.getName()); for (PermissionRule rule : permission.getRules()) { SeenRule s = SeenRule.create(section, permission, rule); boolean addRule; if (rule.isBlock()) { addRule = !exclusivePermissionsByProject.containsEntry(project, permission.getName()); } else { addRule = seen.add(s) && !rule.isDeny() && !exclusivePermissionExists; } HashMap> p = null; if (addRule) { p = permissions; } else if (!rule.isDeny() && !exclusivePermissionExists) { p = overridden; } if (p != null) { List r = p.get(permission.getName()); if (r == null) { r = new ArrayList<>(2); p.put(permission.getName(), r); } r.add(rule); ruleProps.put(rule, ProjectRef.create(project, section.getName())); } } if (permission.getExclusiveGroup()) { exclusivePermissionsByProject.put(project, permission.getName()); exclusiveGroupPermissions.add(permission.getName()); } } } return new PermissionCollection(permissions, overridden, ruleProps, perUser); } } private final Map> rules; private final Map> overridden; private final Map ruleProps; private final boolean perUser; private PermissionCollection( Map> rules, Map> overridden, Map ruleProps, boolean perUser) { this.rules = rules; this.overridden = overridden; this.ruleProps = ruleProps; this.perUser = perUser; } /** * @return true if a "${username}" pattern might need to be expanded to build this collection, * making the results user specific. */ public boolean isUserSpecific() { return perUser; } /** * Obtain all permission rules for a given type of permission. * * @param permissionName type of permission. * @return all rules that apply to this reference, for any group. Never null; the empty list is * returned when there are no rules for the requested permission name. */ public List getPermission(String permissionName) { List r = rules.get(permissionName); return r != null ? r : Collections.emptyList(); } List getOverridden(String permissionName) { return firstNonNull(overridden.get(permissionName), Collections.emptyList()); } ProjectRef getRuleProps(PermissionRule rule) { return ruleProps.get(rule); } /** * Obtain all declared permission rules that match the reference. * * @return all rules. The collection will iterate a permission if it was declared in the project * configuration, either directly or inherited. If the project owner did not use a known * permission (for example {@link Permission#FORGE_SERVER}, then it will not be represented in * the result even if {@link #getPermission(String)} returns an empty list for the same * permission. */ public Iterable>> getDeclaredPermissions() { return rules.entrySet(); } /** Tracks whether or not a permission has been overridden. */ @AutoValue abstract static class SeenRule { public abstract String refPattern(); public abstract String permissionName(); @Nullable public abstract AccountGroup.UUID group(); static SeenRule create( AccessSection section, Permission permission, @Nullable PermissionRule rule) { AccountGroup.UUID group = rule != null && rule.getGroup() != null ? rule.getGroup().getUUID() : null; return new AutoValue_PermissionCollection_SeenRule( section.getName(), permission.getName(), group); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy