Line data Source code
1 : // Copyright (C) 2019 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.acceptance.testsuite.project; 16 : 17 : import static com.google.common.base.Preconditions.checkArgument; 18 : import static com.google.gerrit.entities.AccessSection.GLOBAL_CAPABILITIES; 19 : import static java.util.Objects.requireNonNull; 20 : 21 : import com.google.auto.value.AutoValue; 22 : import com.google.common.collect.ImmutableList; 23 : import com.google.common.collect.ImmutableMap; 24 : import com.google.gerrit.acceptance.testsuite.ThrowingConsumer; 25 : import com.google.gerrit.common.data.GlobalCapability; 26 : import com.google.gerrit.entities.AccountGroup; 27 : import com.google.gerrit.entities.LabelType; 28 : import com.google.gerrit.entities.Permission; 29 : import com.google.gerrit.entities.PermissionRange; 30 : import com.google.gerrit.entities.PermissionRule; 31 : import com.google.gerrit.entities.Project; 32 : import com.google.gerrit.server.config.AllProjectsName; 33 : import java.util.Optional; 34 : import org.eclipse.jgit.lib.Constants; 35 : 36 : @AutoValue 37 81 : public abstract class TestProjectUpdate { 38 : /** Starts a builder for allowing a capability. */ 39 : public static TestCapability.Builder allowCapability(String name) { 40 22 : return TestCapability.builder().name(name); 41 : } 42 : 43 : /** Records a global capability to be updated. */ 44 : @AutoValue 45 22 : public abstract static class TestCapability { 46 : private static Builder builder() { 47 22 : return new AutoValue_TestProjectUpdate_TestCapability.Builder(); 48 : } 49 : 50 : abstract String name(); 51 : 52 : abstract AccountGroup.UUID group(); 53 : 54 : abstract int min(); 55 : 56 : abstract int max(); 57 : 58 : /** Builder for {@link TestCapability}. */ 59 : @AutoValue.Builder 60 22 : public abstract static class Builder { 61 : /** Sets the name of the capability. */ 62 : public abstract Builder name(String name); 63 : 64 : abstract String name(); 65 : 66 : /** Sets the group to which the capability applies. */ 67 : public abstract Builder group(AccountGroup.UUID group); 68 : 69 : abstract Builder min(int min); 70 : 71 : abstract Optional<Integer> min(); 72 : 73 : abstract Builder max(int max); 74 : 75 : abstract Optional<Integer> max(); 76 : 77 : /** Sets the minimum and maximum values for the capability. */ 78 : public Builder range(int min, int max) { 79 22 : checkNonInvertedRange(min, max); 80 22 : return min(min).max(max); 81 : } 82 : 83 : /** Builds the {@link TestCapability}. */ 84 : abstract TestCapability autoBuild(); 85 : 86 : public TestCapability build() { 87 22 : PermissionRange.WithDefaults withDefaults = GlobalCapability.getRange(name()); 88 22 : if (withDefaults != null) { 89 8 : int min = min().orElse(withDefaults.getDefaultMin()); 90 8 : int max = max().orElse(withDefaults.getDefaultMax()); 91 8 : range(min, max); 92 : // Don't enforce range is nonempty; this is allowed for e.g. batchChangesLimit. 93 8 : } else { 94 16 : checkArgument( 95 16 : !min().isPresent() && !max().isPresent(), 96 : "capability %s does not support ranges", 97 16 : name()); 98 16 : range(0, 0); 99 : } 100 : 101 22 : return autoBuild(); 102 : } 103 : } 104 : } 105 : 106 : /** Starts a builder for allowing a permission. */ 107 : public static TestPermission.Builder allow(String name) { 108 55 : return TestPermission.builder().name(name).action(PermissionRule.Action.ALLOW); 109 : } 110 : 111 : /** Starts a builder for denying a permission. */ 112 : public static TestPermission.Builder deny(String name) { 113 6 : return TestPermission.builder().name(name).action(PermissionRule.Action.DENY); 114 : } 115 : 116 : /** Starts a builder for blocking a permission. */ 117 : public static TestPermission.Builder block(String name) { 118 47 : return TestPermission.builder().name(name).action(PermissionRule.Action.BLOCK); 119 : } 120 : 121 : /** 122 : * Records a permission to be updated. 123 : * 124 : * <p>Not used for permissions that have ranges (label permissions) or global capabilities. 125 : */ 126 : @AutoValue 127 71 : public abstract static class TestPermission { 128 : private static Builder builder() { 129 71 : return new AutoValue_TestProjectUpdate_TestPermission.Builder().force(false); 130 : } 131 : 132 : abstract String name(); 133 : 134 : abstract String ref(); 135 : 136 : abstract AccountGroup.UUID group(); 137 : 138 : abstract PermissionRule.Action action(); 139 : 140 : abstract boolean force(); 141 : 142 : /** Builder for {@link TestPermission}. */ 143 : @AutoValue.Builder 144 71 : public abstract static class Builder { 145 : abstract Builder name(String name); 146 : 147 : /** Sets the ref pattern used on the permission. */ 148 : public abstract Builder ref(String ref); 149 : 150 : /** Sets the group to which the permission applies. */ 151 : public abstract Builder group(AccountGroup.UUID groupUuid); 152 : 153 : abstract Builder action(PermissionRule.Action action); 154 : 155 : /** Sets whether the permission is a force permission. */ 156 : public abstract Builder force(boolean force); 157 : 158 : /** Builds the {@link TestPermission}. */ 159 : public abstract TestPermission build(); 160 : } 161 : } 162 : 163 : /** Starts a builder for allowing a label permission. */ 164 : public static TestLabelPermission.Builder allowLabel(String name) { 165 32 : return TestLabelPermission.builder().name(name).action(PermissionRule.Action.ALLOW); 166 : } 167 : 168 : /** Starts a builder for denying a label permission. */ 169 : public static TestLabelPermission.Builder blockLabel(String name) { 170 4 : return TestLabelPermission.builder().name(name).action(PermissionRule.Action.BLOCK); 171 : } 172 : 173 : /** Records a label permission to be updated. */ 174 : @AutoValue 175 32 : public abstract static class TestLabelPermission { 176 : private static Builder builder() { 177 32 : return new AutoValue_TestProjectUpdate_TestLabelPermission.Builder().impersonation(false); 178 : } 179 : 180 : abstract String name(); 181 : 182 : abstract String ref(); 183 : 184 : abstract AccountGroup.UUID group(); 185 : 186 : abstract PermissionRule.Action action(); 187 : 188 : abstract int min(); 189 : 190 : abstract int max(); 191 : 192 : abstract boolean impersonation(); 193 : 194 : /** Builder for {@link TestLabelPermission}. */ 195 : @AutoValue.Builder 196 32 : public abstract static class Builder { 197 : abstract Builder name(String name); 198 : 199 : /** Sets the ref pattern used on the permission. */ 200 : public abstract Builder ref(String ref); 201 : 202 : /** Sets the group to which the permission applies. */ 203 : public abstract Builder group(AccountGroup.UUID group); 204 : 205 : abstract Builder action(PermissionRule.Action action); 206 : 207 : abstract Builder min(int min); 208 : 209 : abstract Builder max(int max); 210 : 211 : /** Sets the minimum and maximum values for the permission. */ 212 : public Builder range(int min, int max) { 213 32 : checkArgument(min != 0 || max != 0, "empty range"); 214 32 : checkNonInvertedRange(min, max); 215 32 : return min(min).max(max); 216 : } 217 : 218 : /** Sets whether this permission should be for impersonating another user's votes. */ 219 : public abstract Builder impersonation(boolean impersonation); 220 : 221 : abstract TestLabelPermission autoBuild(); 222 : 223 : /** Builds the {@link TestPermission}. */ 224 : public TestLabelPermission build() { 225 32 : TestLabelPermission result = autoBuild(); 226 32 : checkLabelName(result.name()); 227 32 : return result; 228 : } 229 : } 230 : } 231 : 232 : /** 233 : * Starts a builder for describing a permission key for deletion. Not for label permissions or 234 : * global capabilities. 235 : */ 236 : public static TestPermissionKey.Builder permissionKey(String name) { 237 13 : return TestPermissionKey.builder().name(name); 238 : } 239 : 240 : /** Starts a builder for describing a label permission key for deletion. */ 241 : public static TestPermissionKey.Builder labelPermissionKey(String name) { 242 5 : checkLabelName(name); 243 5 : return TestPermissionKey.builder().name(Permission.forLabel(name)); 244 : } 245 : 246 : /** Starts a builder for describing a capability key for deletion. */ 247 : public static TestPermissionKey.Builder capabilityKey(String name) { 248 5 : return TestPermissionKey.builder().name(name).section(GLOBAL_CAPABILITIES); 249 : } 250 : 251 : /** Records the key of a permission (of any type) for deletion. */ 252 : @AutoValue 253 19 : public abstract static class TestPermissionKey { 254 : private static Builder builder() { 255 19 : return new AutoValue_TestProjectUpdate_TestPermissionKey.Builder(); 256 : } 257 : 258 : abstract String section(); 259 : 260 : abstract String name(); 261 : 262 : abstract Optional<AccountGroup.UUID> group(); 263 : 264 : @AutoValue.Builder 265 19 : public abstract static class Builder { 266 : abstract Builder section(String section); 267 : 268 : abstract Optional<String> section(); 269 : 270 : /** Sets the ref pattern used on the permission. Not for global capabilities. */ 271 : public Builder ref(String ref) { 272 15 : requireNonNull(ref); 273 15 : checkArgument(ref.startsWith(Constants.R_REFS), "must be a ref: %s", ref); 274 15 : checkArgument( 275 15 : !section().isPresent() || !section().get().equals(GLOBAL_CAPABILITIES), 276 : "can't set ref on global capability"); 277 15 : return section(ref); 278 : } 279 : 280 : abstract Builder name(String name); 281 : 282 : /** Sets the group to which the permission applies. */ 283 : public abstract Builder group(AccountGroup.UUID group); 284 : 285 : /** Builds the {@link TestPermissionKey}. */ 286 : public abstract TestPermissionKey build(); 287 : } 288 : } 289 : 290 : static Builder builder( 291 : Project.NameKey nameKey, 292 : AllProjectsName allProjectsName, 293 : ThrowingConsumer<TestProjectUpdate> projectUpdater) { 294 81 : return new AutoValue_TestProjectUpdate.Builder() 295 81 : .nameKey(nameKey) 296 81 : .allProjectsName(allProjectsName) 297 81 : .projectUpdater(projectUpdater) 298 81 : .removeAllAccessSections(false); 299 : } 300 : 301 : /** Builder for {@link TestProjectUpdate}. */ 302 : @AutoValue.Builder 303 81 : public abstract static class Builder { 304 : abstract Builder nameKey(Project.NameKey project); 305 : 306 : abstract Builder allProjectsName(AllProjectsName allProjects); 307 : 308 : abstract ImmutableList.Builder<TestPermission> addedPermissionsBuilder(); 309 : 310 : abstract ImmutableList.Builder<TestLabelPermission> addedLabelPermissionsBuilder(); 311 : 312 : abstract ImmutableList.Builder<TestCapability> addedCapabilitiesBuilder(); 313 : 314 : abstract ImmutableList.Builder<TestPermissionKey> removedPermissionsBuilder(); 315 : 316 : abstract ImmutableMap.Builder<TestPermissionKey, Boolean> exclusiveGroupPermissionsBuilder(); 317 : 318 : abstract Builder removeAllAccessSections(boolean value); 319 : 320 : /** 321 : * Removes all access sections. Useful when testing against a specific set of access sections or 322 : * permissions. 323 : */ 324 : public Builder removeAllAccessSections() { 325 2 : return removeAllAccessSections(true); 326 : } 327 : 328 : /** Adds a permission to be included in this update. */ 329 : public Builder add(TestPermission testPermission) { 330 71 : addedPermissionsBuilder().add(testPermission); 331 71 : return this; 332 : } 333 : 334 : /** Adds a permission to be included in this update. */ 335 : public Builder add(TestPermission.Builder testPermissionBuilder) { 336 71 : return add(testPermissionBuilder.build()); 337 : } 338 : 339 : /** Adds a label permission to be included in this update. */ 340 : public Builder add(TestLabelPermission testLabelPermission) { 341 32 : addedLabelPermissionsBuilder().add(testLabelPermission); 342 32 : return this; 343 : } 344 : 345 : /** Adds a label permission to be included in this update. */ 346 : public Builder add(TestLabelPermission.Builder testLabelPermissionBuilder) { 347 31 : return add(testLabelPermissionBuilder.build()); 348 : } 349 : 350 : /** Adds a capability to be included in this update. */ 351 : public Builder add(TestCapability testCapability) { 352 22 : addedCapabilitiesBuilder().add(testCapability); 353 22 : return this; 354 : } 355 : 356 : /** Adds a capability to be included in this update. */ 357 : public Builder add(TestCapability.Builder testCapabilityBuilder) { 358 22 : return add(testCapabilityBuilder.build()); 359 : } 360 : 361 : /** Removes a permission, label permission, or capability as part of this update. */ 362 : public Builder remove(TestPermissionKey testPermissionKey) { 363 17 : removedPermissionsBuilder().add(testPermissionKey); 364 17 : return this; 365 : } 366 : 367 : /** Removes a permission, label permission, or capability as part of this update. */ 368 : public Builder remove(TestPermissionKey.Builder testPermissionKeyBuilder) { 369 15 : return remove(testPermissionKeyBuilder.build()); 370 : } 371 : 372 : /** Sets the exclusive bit bit for the given permission key. */ 373 : public Builder setExclusiveGroup( 374 : TestPermissionKey.Builder testPermissionKeyBuilder, boolean exclusive) { 375 4 : return setExclusiveGroup(testPermissionKeyBuilder.build(), exclusive); 376 : } 377 : 378 : /** Sets the exclusive bit bit for the given permission key. */ 379 : public Builder setExclusiveGroup(TestPermissionKey testPermissionKey, boolean exclusive) { 380 4 : checkArgument( 381 4 : !testPermissionKey.group().isPresent(), 382 : "do not specify group for setExclusiveGroup: %s", 383 : testPermissionKey); 384 4 : checkArgument( 385 4 : !testPermissionKey.section().equals(GLOBAL_CAPABILITIES), 386 : "setExclusiveGroup not valid for global capabilities: %s", 387 : testPermissionKey); 388 4 : exclusiveGroupPermissionsBuilder().put(testPermissionKey, exclusive); 389 4 : return this; 390 : } 391 : 392 : abstract Builder projectUpdater(ThrowingConsumer<TestProjectUpdate> projectUpdater); 393 : 394 : abstract TestProjectUpdate autoBuild(); 395 : 396 : TestProjectUpdate build() { 397 81 : TestProjectUpdate projectUpdate = autoBuild(); 398 81 : if (projectUpdate.hasCapabilityUpdates()) { 399 22 : checkArgument( 400 22 : projectUpdate.nameKey().equals(projectUpdate.allProjectsName()), 401 : "cannot update global capabilities on %s, only %s: %s", 402 22 : projectUpdate.nameKey(), 403 22 : projectUpdate.allProjectsName(), 404 : projectUpdate); 405 : } 406 81 : return projectUpdate; 407 : } 408 : 409 : /** Executes the update, updating the underlying project. */ 410 : public void update() { 411 81 : TestProjectUpdate projectUpdate = build(); 412 81 : projectUpdate.projectUpdater().acceptAndThrowSilently(projectUpdate); 413 81 : } 414 : } 415 : 416 : abstract Project.NameKey nameKey(); 417 : 418 : abstract AllProjectsName allProjectsName(); 419 : 420 : abstract ImmutableList<TestPermission> addedPermissions(); 421 : 422 : abstract ImmutableList<TestLabelPermission> addedLabelPermissions(); 423 : 424 : abstract ImmutableList<TestCapability> addedCapabilities(); 425 : 426 : abstract ImmutableList<TestPermissionKey> removedPermissions(); 427 : 428 : abstract ImmutableMap<TestPermissionKey, Boolean> exclusiveGroupPermissions(); 429 : 430 : abstract ThrowingConsumer<TestProjectUpdate> projectUpdater(); 431 : 432 : abstract boolean removeAllAccessSections(); 433 : 434 : boolean hasCapabilityUpdates() { 435 81 : return !addedCapabilities().isEmpty() 436 81 : || removedPermissions().stream().anyMatch(k -> k.section().equals(GLOBAL_CAPABILITIES)); 437 : } 438 : 439 : private static void checkLabelName(String name) { 440 : // "label-Code-Review" is technically a valid label name, and we don't prevent users from 441 : // using it in production, but specifying it in a test is programmer error. 442 32 : checkArgument(!Permission.isLabel(name), "expected label name, got permission name: %s", name); 443 32 : LabelType.checkName(name); 444 32 : } 445 : 446 : private static void checkNonInvertedRange(int min, int max) { 447 39 : checkArgument(min <= max, "inverted range: %s > %s", min, max); 448 39 : } 449 : }