Files
gerrit/java/com/google/gerrit/server/permissions/PermissionBackendCondition.java
Patrick Hiesel 80f2ab6255 Implement equals() and hashCode() on PermissionBackendCondition
We want to be able to deduplicate PermissionBackendConditions when given
a list of such objects. Therefore, this commit implements equals() and
hashCode().

This is not trivial since the individual implementations of
PermissionBackendCondition encapsulate a CurrentUser as well as a
PermissionBackend. Both of which do not implement equals() and
hashCode().

This is correct, since PermissionBackend is a service and comparing it
to other PermissionBackends makes no sense. CurrentUser could be
compared to other CurrentUser objects but the outcome depends on
wheather two annonymous users should be considered equal or not. This
depends on the use case.

Therefore, this commit adds user() and resourcePath() to
PermissionBackend to get the entities that a PermissionBackend object is
scoped to and evaluate the case at hand directly in
PermissionBackendCondition.

resourcePath() can come in handy in other places as well since it is an
accurate representation of the resource that we are performing checks
on. Concrete PermissionBackend implementations can use it to communicate
with their remove service if desired.

The implementation for resourcePath() picks /+ as the delimiter since it
is a forbidden character combination for project names. An alternative
would be to URL-encode the project name, but this is more expensive.

Change-Id: I7b357e61bfc6f14acc7b0d06830b615847798ec3
2018-01-26 09:48:55 +01:00

231 lines
6.3 KiB
Java

// Copyright (C) 2017 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.permissions;
import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission;
import com.google.gerrit.extensions.conditions.BooleanCondition;
import com.google.gerrit.extensions.conditions.PrivateInternals_BooleanCondition;
import com.google.gerrit.server.CurrentUser;
import java.util.Objects;
/** {@link BooleanCondition} to evaluate a permission. */
public abstract class PermissionBackendCondition
extends PrivateInternals_BooleanCondition.SubclassOnlyInCoreServer {
Boolean value;
/**
* Assign a specific {@code testOrFalse} result to this condition.
*
* <p>By setting the condition to a specific value the condition will bypass calling {@link
* PermissionBackend} during {@code value()}, and immediately return the set value instead.
*
* @param val value to return from {@code value()}.
*/
public void set(boolean val) {
value = val;
}
@Override
public abstract String toString();
public static class WithUser extends PermissionBackendCondition {
private final PermissionBackend.WithUser impl;
private final GlobalOrPluginPermission perm;
WithUser(PermissionBackend.WithUser impl, GlobalOrPluginPermission perm) {
this.impl = impl;
this.perm = perm;
}
public PermissionBackend.WithUser withUser() {
return impl;
}
public GlobalOrPluginPermission permission() {
return perm;
}
@Override
public boolean value() {
return value != null ? value : impl.testOrFalse(perm);
}
@Override
public String toString() {
return "PermissionBackendCondition.WithUser(" + perm + ")";
}
@Override
public int hashCode() {
return Objects.hash(perm, hashForUser(impl.user()));
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof WithUser)) {
return false;
}
WithUser other = (WithUser) obj;
return Objects.equals(perm, other.perm) && usersAreEqual(impl.user(), other.impl.user());
}
}
public static class ForProject extends PermissionBackendCondition {
private final PermissionBackend.ForProject impl;
private final ProjectPermission perm;
ForProject(PermissionBackend.ForProject impl, ProjectPermission perm) {
this.impl = impl;
this.perm = perm;
}
public PermissionBackend.ForProject project() {
return impl;
}
public ProjectPermission permission() {
return perm;
}
@Override
public boolean value() {
return value != null ? value : impl.testOrFalse(perm);
}
@Override
public String toString() {
return "PermissionBackendCondition.ForProject(" + perm + ")";
}
@Override
public int hashCode() {
return Objects.hash(perm, impl.resourcePath(), hashForUser(impl.user()));
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ForProject)) {
return false;
}
ForProject other = (ForProject) obj;
return Objects.equals(perm, other.perm)
&& Objects.equals(impl.resourcePath(), other.impl.resourcePath())
&& usersAreEqual(impl.user(), other.impl.user());
}
}
public static class ForRef extends PermissionBackendCondition {
private final PermissionBackend.ForRef impl;
private final RefPermission perm;
ForRef(PermissionBackend.ForRef impl, RefPermission perm) {
this.impl = impl;
this.perm = perm;
}
public PermissionBackend.ForRef ref() {
return impl;
}
public RefPermission permission() {
return perm;
}
@Override
public boolean value() {
return value != null ? value : impl.testOrFalse(perm);
}
@Override
public String toString() {
return "PermissionBackendCondition.ForRef(" + perm + ")";
}
@Override
public int hashCode() {
return Objects.hash(perm, impl.resourcePath(), hashForUser(impl.user()));
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ForRef)) {
return false;
}
ForRef other = (ForRef) obj;
return Objects.equals(perm, other.perm)
&& Objects.equals(impl.resourcePath(), other.impl.resourcePath())
&& usersAreEqual(impl.user(), other.impl.user());
}
}
public static class ForChange extends PermissionBackendCondition {
private final PermissionBackend.ForChange impl;
private final ChangePermissionOrLabel perm;
ForChange(PermissionBackend.ForChange impl, ChangePermissionOrLabel perm) {
this.impl = impl;
this.perm = perm;
}
public PermissionBackend.ForChange change() {
return impl;
}
public ChangePermissionOrLabel permission() {
return perm;
}
@Override
public boolean value() {
return value != null ? value : impl.testOrFalse(perm);
}
@Override
public String toString() {
return "PermissionBackendCondition.ForChange(" + perm + ")";
}
@Override
public int hashCode() {
return Objects.hash(perm, impl.resourcePath(), hashForUser(impl.user()));
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ForChange)) {
return false;
}
ForChange other = (ForChange) obj;
return Objects.equals(perm, other.perm)
&& Objects.equals(impl.resourcePath(), other.impl.resourcePath())
&& usersAreEqual(impl.user(), other.impl.user());
}
}
private static int hashForUser(CurrentUser user) {
if (!user.isIdentifiedUser()) {
return 0;
}
return user.getAccountId().get();
}
private static boolean usersAreEqual(CurrentUser user1, CurrentUser user2) {
if (user1.isIdentifiedUser() && user2.isIdentifiedUser()) {
return user1.getAccountId().equals(user2.getAccountId());
}
return false;
}
}