Line data Source code
1 : // Copyright (C) 2010 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.entities; 16 : 17 : import static com.google.common.collect.ImmutableList.toImmutableList; 18 : import static java.util.Objects.requireNonNull; 19 : 20 : import com.google.auto.value.AutoValue; 21 : import com.google.auto.value.extension.memoized.Memoized; 22 : import com.google.common.collect.ImmutableList; 23 : import com.google.gerrit.common.Nullable; 24 : import java.util.ArrayList; 25 : import java.util.List; 26 : import java.util.Optional; 27 : import java.util.function.Consumer; 28 : import java.util.regex.Pattern; 29 : 30 : /** Portion of a {@link Project} describing access rules. */ 31 : @AutoValue 32 153 : public abstract class AccessSection implements Comparable<AccessSection> { 33 : /** Special name given to the global capabilities; not a valid reference. */ 34 : public static final String GLOBAL_CAPABILITIES = "GLOBAL_CAPABILITIES"; 35 : /** Pattern that matches all references in a project. */ 36 : public static final String ALL = "refs/*"; 37 : 38 : /** Pattern that matches all branches in a project. */ 39 : public static final String HEADS = "refs/heads/*"; 40 : 41 : /** Prefix that triggers a regular expression pattern. */ 42 : public static final String REGEX_PREFIX = "^"; 43 : 44 : /** Name of the access section. It could be a ref pattern or something else. */ 45 : public abstract String getName(); 46 : 47 : /** 48 : * A compiled regular expression in case {@link #getName()} is a regular expression. This is 49 : * memoized to save callers from compiling patterns for every use. 50 : */ 51 : @Memoized 52 : public Optional<Pattern> getNamePattern() { 53 145 : if (isValidRefSectionName(getName()) 54 145 : && getName().startsWith(REGEX_PREFIX) 55 1 : && !getName().contains("${")) { 56 1 : return Optional.of(Pattern.compile(getName())); 57 : } 58 145 : return Optional.empty(); 59 : } 60 : 61 : public abstract ImmutableList<Permission> getPermissions(); 62 : 63 : public static AccessSection create(String name) { 64 3 : return builder(name).build(); 65 : } 66 : 67 : public static Builder builder(String name) { 68 153 : return new AutoValue_AccessSection.Builder().setName(name).setPermissions(ImmutableList.of()); 69 : } 70 : 71 : /** Returns true if the name is likely to be a valid reference section name. */ 72 : public static boolean isValidRefSectionName(String name) { 73 151 : return name.startsWith("refs/") || name.startsWith("^refs/"); 74 : } 75 : 76 : @Nullable 77 : public Permission getPermission(String name) { 78 152 : requireNonNull(name); 79 152 : for (Permission p : getPermissions()) { 80 152 : if (p.getName().equalsIgnoreCase(name)) { 81 149 : return p; 82 : } 83 152 : } 84 152 : return null; 85 : } 86 : 87 : @Override 88 : public final int compareTo(AccessSection o) { 89 151 : return comparePattern().compareTo(o.comparePattern()); 90 : } 91 : 92 : private String comparePattern() { 93 151 : if (getName().startsWith(REGEX_PREFIX)) { 94 0 : return getName().substring(REGEX_PREFIX.length()); 95 : } 96 151 : return getName(); 97 : } 98 : 99 : @Override 100 : public final String toString() { 101 0 : return "AccessSection[" + getName() + "]"; 102 : } 103 : 104 : public Builder toBuilder() { 105 151 : Builder b = autoToBuilder(); 106 151 : b.getPermissions().stream().map(Permission::toBuilder).forEach(p -> b.addPermission(p)); 107 151 : return b; 108 : } 109 : 110 : protected abstract Builder autoToBuilder(); 111 : 112 : @AutoValue.Builder 113 : public abstract static class Builder { 114 : private final List<Permission.Builder> permissionBuilders; 115 : 116 153 : protected Builder() { 117 153 : permissionBuilders = new ArrayList<>(); 118 153 : } 119 : 120 : public abstract Builder setName(String name); 121 : 122 : public abstract String getName(); 123 : 124 : public Builder modifyPermissions(Consumer<List<Permission.Builder>> modification) { 125 153 : modification.accept(permissionBuilders); 126 153 : return this; 127 : } 128 : 129 : public Builder addPermission(Permission.Builder permission) { 130 153 : requireNonNull(permission, "permission must be non-null"); 131 153 : return modifyPermissions(p -> p.add(permission)); 132 : } 133 : 134 : public Builder remove(Permission.Builder permission) { 135 6 : requireNonNull(permission, "permission must be non-null"); 136 6 : return removePermission(permission.getName()); 137 : } 138 : 139 : public Builder removePermission(String name) { 140 9 : requireNonNull(name, "name must be non-null"); 141 9 : return modifyPermissions( 142 9 : p -> p.removeIf(permissionBuilder -> name.equalsIgnoreCase(permissionBuilder.getName()))); 143 : } 144 : 145 : public Permission.Builder upsertPermission(String permissionName) { 146 152 : requireNonNull(permissionName, "permissionName must be non-null"); 147 : 148 152 : Optional<Permission.Builder> maybePermission = 149 152 : permissionBuilders.stream() 150 152 : .filter(p -> p.getName().equalsIgnoreCase(permissionName)) 151 152 : .findAny(); 152 152 : if (maybePermission.isPresent()) { 153 152 : return maybePermission.get(); 154 : } 155 : 156 152 : Permission.Builder permission = Permission.builder(permissionName); 157 152 : modifyPermissions(p -> p.add(permission)); 158 152 : return permission; 159 : } 160 : 161 : public AccessSection build() { 162 153 : setPermissions( 163 153 : permissionBuilders.stream().map(Permission.Builder::build).collect(toImmutableList())); 164 153 : if (getPermissions().size() 165 153 : > getPermissions().stream() 166 153 : .map(Permission::getName) 167 153 : .map(String::toLowerCase) 168 153 : .distinct() 169 153 : .count()) { 170 1 : throw new IllegalArgumentException("duplicate permissions: " + getPermissions()); 171 : } 172 153 : return autoBuild(); 173 : } 174 : 175 : protected abstract AccessSection autoBuild(); 176 : 177 : protected abstract ImmutableList<Permission> getPermissions(); 178 : 179 : abstract Builder setPermissions(ImmutableList<Permission> permissions); 180 : } 181 : }