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 : 19 : import com.google.auto.value.AutoValue; 20 : import com.google.common.collect.ImmutableList; 21 : import com.google.gerrit.common.Nullable; 22 : import java.util.ArrayList; 23 : import java.util.Iterator; 24 : import java.util.List; 25 : import java.util.function.Consumer; 26 : 27 : /** A single permission within an {@link AccessSection} of a project. */ 28 : @AutoValue 29 153 : public abstract class Permission implements Comparable<Permission> { 30 : public static final String ABANDON = "abandon"; 31 : public static final String ADD_PATCH_SET = "addPatchSet"; 32 : public static final String CREATE = "create"; 33 : public static final String CREATE_SIGNED_TAG = "createSignedTag"; 34 : public static final String CREATE_TAG = "createTag"; 35 : public static final String DELETE = "delete"; 36 : public static final String DELETE_CHANGES = "deleteChanges"; 37 : public static final String DELETE_OWN_CHANGES = "deleteOwnChanges"; 38 : public static final String EDIT_ASSIGNEE = "editAssignee"; 39 : public static final String EDIT_HASHTAGS = "editHashtags"; 40 : public static final String EDIT_TOPIC_NAME = "editTopicName"; 41 : public static final String FORGE_AUTHOR = "forgeAuthor"; 42 : public static final String FORGE_COMMITTER = "forgeCommitter"; 43 : public static final String FORGE_SERVER = "forgeServerAsCommitter"; 44 : public static final String LABEL = "label-"; 45 : public static final String LABEL_AS = "labelAs-"; 46 : public static final String OWNER = "owner"; 47 : public static final String PUSH = "push"; 48 : public static final String PUSH_MERGE = "pushMerge"; 49 : public static final String READ = "read"; 50 : public static final String REBASE = "rebase"; 51 : public static final String REMOVE_REVIEWER = "removeReviewer"; 52 : public static final String REVERT = "revert"; 53 : public static final String SUBMIT = "submit"; 54 : public static final String SUBMIT_AS = "submitAs"; 55 : public static final String TOGGLE_WORK_IN_PROGRESS_STATE = "toggleWipState"; 56 : public static final String VIEW_PRIVATE_CHANGES = "viewPrivateChanges"; 57 : 58 : public static final boolean DEF_EXCLUSIVE_GROUP = false; 59 : 60 : private static final List<String> NAMES_LC; 61 : private static final int LABEL_INDEX; 62 : private static final int LABEL_AS_INDEX; 63 : 64 : static { 65 153 : NAMES_LC = new ArrayList<>(); 66 153 : NAMES_LC.add(ABANDON.toLowerCase()); 67 153 : NAMES_LC.add(ADD_PATCH_SET.toLowerCase()); 68 153 : NAMES_LC.add(CREATE.toLowerCase()); 69 153 : NAMES_LC.add(CREATE_SIGNED_TAG.toLowerCase()); 70 153 : NAMES_LC.add(CREATE_TAG.toLowerCase()); 71 153 : NAMES_LC.add(DELETE.toLowerCase()); 72 153 : NAMES_LC.add(DELETE_CHANGES.toLowerCase()); 73 153 : NAMES_LC.add(DELETE_OWN_CHANGES.toLowerCase()); 74 153 : NAMES_LC.add(EDIT_ASSIGNEE.toLowerCase()); 75 153 : NAMES_LC.add(EDIT_HASHTAGS.toLowerCase()); 76 153 : NAMES_LC.add(EDIT_TOPIC_NAME.toLowerCase()); 77 153 : NAMES_LC.add(FORGE_AUTHOR.toLowerCase()); 78 153 : NAMES_LC.add(FORGE_COMMITTER.toLowerCase()); 79 153 : NAMES_LC.add(FORGE_SERVER.toLowerCase()); 80 153 : NAMES_LC.add(LABEL.toLowerCase()); 81 153 : NAMES_LC.add(LABEL_AS.toLowerCase()); 82 153 : NAMES_LC.add(OWNER.toLowerCase()); 83 153 : NAMES_LC.add(PUSH.toLowerCase()); 84 153 : NAMES_LC.add(PUSH_MERGE.toLowerCase()); 85 153 : NAMES_LC.add(READ.toLowerCase()); 86 153 : NAMES_LC.add(REBASE.toLowerCase()); 87 153 : NAMES_LC.add(REMOVE_REVIEWER.toLowerCase()); 88 153 : NAMES_LC.add(REVERT.toLowerCase()); 89 153 : NAMES_LC.add(SUBMIT.toLowerCase()); 90 153 : NAMES_LC.add(SUBMIT_AS.toLowerCase()); 91 153 : NAMES_LC.add(TOGGLE_WORK_IN_PROGRESS_STATE.toLowerCase()); 92 153 : NAMES_LC.add(VIEW_PRIVATE_CHANGES.toLowerCase()); 93 : 94 153 : LABEL_INDEX = NAMES_LC.indexOf(Permission.LABEL); 95 153 : LABEL_AS_INDEX = NAMES_LC.indexOf(Permission.LABEL_AS.toLowerCase()); 96 153 : } 97 : 98 : /** Returns true if the name is recognized as a permission name. */ 99 : public static boolean isPermission(String varName) { 100 152 : return isLabel(varName) || isLabelAs(varName) || NAMES_LC.contains(varName.toLowerCase()); 101 : } 102 : 103 : public static boolean hasRange(String varName) { 104 152 : return isLabel(varName) || isLabelAs(varName); 105 : } 106 : 107 : /** Returns true if the permission name is actually for a review label. */ 108 : public static boolean isLabel(String varName) { 109 152 : return varName.startsWith(LABEL) && LABEL.length() < varName.length(); 110 : } 111 : 112 : /** Returns true if the permission is for impersonated review labels. */ 113 : public static boolean isLabelAs(String var) { 114 152 : return var.startsWith(LABEL_AS) && LABEL_AS.length() < var.length(); 115 : } 116 : 117 : /** Returns permission name for the given review label. */ 118 : public static String forLabel(String labelName) { 119 104 : return LABEL + labelName; 120 : } 121 : 122 : /** Returns permission name to apply a label for another user. */ 123 : public static String forLabelAs(String labelName) { 124 4 : return LABEL_AS + labelName; 125 : } 126 : 127 : @Nullable 128 : public static String extractLabel(String varName) { 129 11 : if (isLabel(varName)) { 130 5 : return varName.substring(LABEL.length()); 131 11 : } else if (isLabelAs(varName)) { 132 1 : return varName.substring(LABEL_AS.length()); 133 : } 134 11 : return null; 135 : } 136 : 137 : public static boolean canBeOnAllProjects(String ref, String permissionName) { 138 152 : if (AccessSection.ALL.equals(ref)) { 139 152 : return !OWNER.equals(permissionName); 140 : } 141 152 : return true; 142 : } 143 : 144 : /** The permission name, eg. {@code Permission.SUBMIT} */ 145 : public abstract String getName(); 146 : 147 : protected abstract boolean isExclusiveGroup(); 148 : 149 : public abstract ImmutableList<PermissionRule> getRules(); 150 : 151 : public static Builder builder(String name) { 152 153 : return new AutoValue_Permission.Builder() 153 153 : .setName(name) 154 153 : .setExclusiveGroup(DEF_EXCLUSIVE_GROUP) 155 153 : .setRules(ImmutableList.of()); 156 : } 157 : 158 : public static Permission create(String name) { 159 1 : return builder(name).build(); 160 : } 161 : 162 : public String getLabel() { 163 11 : return extractLabel(getName()); 164 : } 165 : 166 : public boolean getExclusiveGroup() { 167 : // Only permit exclusive group behavior on non OWNER permissions, 168 : // otherwise an owner might lose access to a delegated subspace. 169 : // 170 153 : return isExclusiveGroup() && !OWNER.equals(getName()); 171 : } 172 : 173 : @Nullable 174 : public PermissionRule getRule(GroupReference group) { 175 5 : for (PermissionRule r : getRules()) { 176 5 : if (sameGroup(r, group)) { 177 5 : return r; 178 : } 179 2 : } 180 : 181 1 : return null; 182 : } 183 : 184 : private static boolean sameGroup(PermissionRule rule, GroupReference group) { 185 14 : if (group.getUUID() != null && rule.getGroup().getUUID() != null) { 186 14 : return group.getUUID().equals(rule.getGroup().getUUID()); 187 0 : } else if (group.getName() != null && rule.getGroup().getName() != null) { 188 0 : return group.getName().equals(rule.getGroup().getName()); 189 : } else { 190 0 : return false; 191 : } 192 : } 193 : 194 : @Override 195 : public final int compareTo(Permission b) { 196 151 : int cmp = index(this) - index(b); 197 151 : if (cmp == 0) { 198 151 : cmp = getName().compareTo(b.getName()); 199 : } 200 151 : return cmp; 201 : } 202 : 203 : private static int index(Permission a) { 204 151 : if (isLabel(a.getName())) { 205 151 : return LABEL_INDEX; 206 151 : } else if (isLabelAs(a.getName())) { 207 1 : return LABEL_AS_INDEX; 208 : } 209 : 210 151 : int index = NAMES_LC.indexOf(a.getName().toLowerCase()); 211 151 : return 0 <= index ? index : NAMES_LC.size(); 212 : } 213 : 214 : @Override 215 : public final String toString() { 216 1 : StringBuilder bldr = new StringBuilder(); 217 1 : bldr.append(getName()).append(" "); 218 1 : if (isExclusiveGroup()) { 219 0 : bldr.append("[exclusive] "); 220 : } 221 1 : bldr.append("["); 222 1 : Iterator<PermissionRule> it = getRules().iterator(); 223 1 : while (it.hasNext()) { 224 0 : bldr.append(it.next()); 225 0 : if (it.hasNext()) { 226 0 : bldr.append(", "); 227 : } 228 : } 229 1 : bldr.append("]"); 230 1 : return bldr.toString(); 231 : } 232 : 233 : protected abstract Builder autoToBuilder(); 234 : 235 : public Builder toBuilder() { 236 153 : Builder b = autoToBuilder(); 237 153 : getRules().stream().map(PermissionRule::toBuilder).forEach(r -> b.add(r)); 238 153 : return b; 239 : } 240 : 241 : @AutoValue.Builder 242 : public abstract static class Builder { 243 : private final List<PermissionRule.Builder> rulesBuilders; 244 : 245 153 : Builder() { 246 153 : rulesBuilders = new ArrayList<>(); 247 153 : } 248 : 249 : public abstract Builder setName(String value); 250 : 251 : public abstract String getName(); 252 : 253 : public abstract Builder setExclusiveGroup(boolean value); 254 : 255 : public Builder modifyRules(Consumer<List<PermissionRule.Builder>> modification) { 256 153 : modification.accept(rulesBuilders); 257 153 : return this; 258 : } 259 : 260 : public Builder add(PermissionRule.Builder rule) { 261 153 : return modifyRules(r -> r.add(rule)); 262 : } 263 : 264 : public Builder remove(PermissionRule rule) { 265 2 : if (rule != null) { 266 2 : return removeRule(rule.getGroup()); 267 : } 268 0 : return this; 269 : } 270 : 271 : public Builder removeRule(GroupReference group) { 272 12 : return modifyRules(rules -> rules.removeIf(rule -> sameGroup(rule.build(), group))); 273 : } 274 : 275 : public Builder clearRules() { 276 11 : return modifyRules(r -> r.clear()); 277 : } 278 : 279 : public Permission build() { 280 153 : setRules( 281 153 : rulesBuilders.stream().map(PermissionRule.Builder::build).collect(toImmutableList())); 282 153 : return autoBuild(); 283 : } 284 : 285 : public List<PermissionRule.Builder> getRulesBuilders() { 286 1 : return rulesBuilders; 287 : } 288 : 289 : protected abstract ImmutableList<PermissionRule> getRules(); 290 : 291 : protected abstract Builder setRules(ImmutableList<PermissionRule> rules); 292 : 293 : protected abstract Permission autoBuild(); 294 : } 295 : }