Fix permission handling
Users with MAINTAIN_SERVER capability were not able to get or change
the read only state. The reason was, that the @RequireAnyCapability
annotation does not support GlobalCapabilities provided by Gerrit core
in plugins.
This change fixes that by making an explicit permission check.
Change-Id: Ie38143587b9bd8dad2fabb2107e3195f6a3c1a90
diff --git a/src/main/java/com/googlesource/gerrit/plugins/readonly/DeleteReadOnly.java b/src/main/java/com/googlesource/gerrit/plugins/readonly/DeleteReadOnly.java
new file mode 100644
index 0000000..89162f5
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/readonly/DeleteReadOnly.java
@@ -0,0 +1,49 @@
+// Copyright (C) 2024 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.googlesource.gerrit.plugins.readonly;
+
+import com.google.gerrit.extensions.common.Input;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.config.ConfigResource;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import java.io.IOException;
+
+public class DeleteReadOnly extends ReadOnlyEndpoint
+ implements RestModifyView<ConfigResource, Input> {
+ private final ReadOnlyState state;
+
+ @Inject
+ public DeleteReadOnly(
+ Provider<CurrentUser> userProvider,
+ PermissionBackend permissionBackend,
+ ReadOnlyState state) {
+ super(userProvider, permissionBackend);
+ this.state = state;
+ }
+
+ @Override
+ public Response<String> apply(ConfigResource resource, Input input)
+ throws IOException, AuthException, PermissionBackendException {
+ checkPermissions();
+ state.setReadOnly(false);
+ return Response.ok("off");
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/readonly/DisableReadOnlyCommand.java b/src/main/java/com/googlesource/gerrit/plugins/readonly/DisableReadOnlyCommand.java
index a64d0b1..75f5124 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/readonly/DisableReadOnlyCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/readonly/DisableReadOnlyCommand.java
@@ -18,6 +18,8 @@
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
@@ -26,10 +28,10 @@
@RequiresAnyCapability({ADMINISTRATE_SERVER, MAINTAIN_SERVER})
@CommandMetaData(name = "disable", description = "Disable read only mode")
class DisableReadOnlyCommand extends SshCommand {
- @Inject ReadOnlyEndpoint.Delete delete;
+ @Inject DeleteReadOnly delete;
@Override
- protected void run() throws IOException {
+ protected void run() throws IOException, AuthException, PermissionBackendException {
delete.apply(null, null);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/readonly/EnableReadOnlyCommand.java b/src/main/java/com/googlesource/gerrit/plugins/readonly/EnableReadOnlyCommand.java
index 322a9f9..c92717d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/readonly/EnableReadOnlyCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/readonly/EnableReadOnlyCommand.java
@@ -14,22 +14,19 @@
package com.googlesource.gerrit.plugins.readonly;
-import static com.google.gerrit.common.data.GlobalCapability.ADMINISTRATE_SERVER;
-import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
-
-import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
import java.io.IOException;
-@RequiresAnyCapability({ADMINISTRATE_SERVER, MAINTAIN_SERVER})
@CommandMetaData(name = "enable", description = "Enable read only mode")
class EnableReadOnlyCommand extends SshCommand {
- @Inject ReadOnlyEndpoint.Put put;
+ @Inject PutReadOnly put;
@Override
- protected void run() throws IOException {
+ protected void run() throws IOException, AuthException, PermissionBackendException {
put.apply(null, null);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/readonly/GetReadOnly.java b/src/main/java/com/googlesource/gerrit/plugins/readonly/GetReadOnly.java
new file mode 100644
index 0000000..0a3db04
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/readonly/GetReadOnly.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2024 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.googlesource.gerrit.plugins.readonly;
+
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.config.ConfigResource;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+@Singleton
+public class GetReadOnly extends ReadOnlyEndpoint implements RestReadView<ConfigResource> {
+ private final ReadOnlyState state;
+
+ @Inject
+ public GetReadOnly(
+ Provider<CurrentUser> userProvider,
+ PermissionBackend permissionBackend,
+ ReadOnlyState state) {
+ super(userProvider, permissionBackend);
+ this.state = state;
+ }
+
+ @Override
+ public Response<String> apply(ConfigResource resource)
+ throws AuthException, PermissionBackendException {
+ checkPermissions();
+ return Response.ok(state.isReadOnly() ? "on" : "off");
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/readonly/GetReadOnlyStatusCommand.java b/src/main/java/com/googlesource/gerrit/plugins/readonly/GetReadOnlyStatusCommand.java
index 06a41e5..5ca5c94 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/readonly/GetReadOnlyStatusCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/readonly/GetReadOnlyStatusCommand.java
@@ -14,21 +14,18 @@
package com.googlesource.gerrit.plugins.readonly;
-import static com.google.gerrit.common.data.GlobalCapability.ADMINISTRATE_SERVER;
-import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
-
-import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
-@RequiresAnyCapability({ADMINISTRATE_SERVER, MAINTAIN_SERVER})
@CommandMetaData(name = "status", description = "Show read only mode state")
class GetReadOnlyStatusCommand extends SshCommand {
- @Inject ReadOnlyEndpoint.Get get;
+ @Inject GetReadOnly get;
@Override
- protected void run() {
+ protected void run() throws AuthException, PermissionBackendException {
stdout.println(get.apply(null));
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/readonly/Module.java b/src/main/java/com/googlesource/gerrit/plugins/readonly/Module.java
index 69fa804..6044f2e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/readonly/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/readonly/Module.java
@@ -29,9 +29,9 @@
new RestApiModule() {
@Override
protected void configure() {
- put(CONFIG_KIND, "readonly").to(ReadOnlyEndpoint.Put.class);
- delete(CONFIG_KIND, "readonly").to(ReadOnlyEndpoint.Delete.class);
- get(CONFIG_KIND, "readonly").to(ReadOnlyEndpoint.Get.class);
+ put(CONFIG_KIND, "readonly").to(PutReadOnly.class);
+ delete(CONFIG_KIND, "readonly").to(DeleteReadOnly.class);
+ get(CONFIG_KIND, "readonly").to(GetReadOnly.class);
}
});
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/readonly/PutReadOnly.java b/src/main/java/com/googlesource/gerrit/plugins/readonly/PutReadOnly.java
new file mode 100644
index 0000000..92ca1be
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/readonly/PutReadOnly.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2024 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.googlesource.gerrit.plugins.readonly;
+
+import com.google.gerrit.extensions.common.Input;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.config.ConfigResource;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.io.IOException;
+
+@Singleton
+public class PutReadOnly extends ReadOnlyEndpoint implements RestModifyView<ConfigResource, Input> {
+ private final ReadOnlyState state;
+
+ @Inject
+ public PutReadOnly(
+ Provider<CurrentUser> userProvider,
+ PermissionBackend permissionBackend,
+ ReadOnlyState state) {
+ super(userProvider, permissionBackend);
+ this.state = state;
+ }
+
+ @Override
+ public Response<String> apply(ConfigResource resource, Input input)
+ throws IOException, AuthException, PermissionBackendException {
+ checkPermissions();
+ state.setReadOnly(true);
+ return Response.ok("on");
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/readonly/ReadOnlyEndpoint.java b/src/main/java/com/googlesource/gerrit/plugins/readonly/ReadOnlyEndpoint.java
index 6a4b916..5b63a08 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/readonly/ReadOnlyEndpoint.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/readonly/ReadOnlyEndpoint.java
@@ -14,68 +14,31 @@
package com.googlesource.gerrit.plugins.readonly;
-import static com.google.gerrit.common.data.GlobalCapability.ADMINISTRATE_SERVER;
-import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
+import static com.google.gerrit.server.permissions.GlobalPermission.ADMINISTRATE_SERVER;
+import static com.google.gerrit.server.permissions.GlobalPermission.MAINTAIN_SERVER;
-import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
-import com.google.gerrit.extensions.restapi.Response;
-import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.server.config.ConfigResource;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.io.IOException;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.inject.Provider;
+import java.util.Set;
public class ReadOnlyEndpoint {
- static class Input {}
+ private final Provider<CurrentUser> userProvider;
+ private final PermissionBackend permissionBackend;
- @RequiresAnyCapability({ADMINISTRATE_SERVER, MAINTAIN_SERVER})
- @Singleton
- public static class Get implements RestReadView<ConfigResource> {
- private final ReadOnlyState state;
-
- @Inject
- Get(ReadOnlyState state) {
- this.state = state;
- }
-
- @Override
- public Response<String> apply(ConfigResource resource) {
- return Response.ok(state.isReadOnly() ? "on" : "off");
- }
+ public ReadOnlyEndpoint(Provider<CurrentUser> userProvider, PermissionBackend permissionBackend) {
+ this.userProvider = userProvider;
+ this.permissionBackend = permissionBackend;
}
- @RequiresAnyCapability({ADMINISTRATE_SERVER, MAINTAIN_SERVER})
- @Singleton
- public static class Put implements RestModifyView<ConfigResource, Input> {
- private final ReadOnlyState state;
-
- @Inject
- Put(ReadOnlyState state) {
- this.state = state;
+ void checkPermissions() throws AuthException, PermissionBackendException {
+ CurrentUser requestingUser = userProvider.get();
+ if (requestingUser == null || !requestingUser.isIdentifiedUser()) {
+ throw new AuthException("authentication required");
}
- @Override
- public Response<String> apply(ConfigResource resource, Input input) throws IOException {
- state.setReadOnly(true);
- return Response.ok("on");
- }
- }
-
- @RequiresAnyCapability({ADMINISTRATE_SERVER, MAINTAIN_SERVER})
- @Singleton
- public static class Delete implements RestModifyView<ConfigResource, Input> {
- private final ReadOnlyState state;
-
- @Inject
- Delete(ReadOnlyState state) {
- this.state = state;
- }
-
- @Override
- public Response<String> apply(ConfigResource resource, Input input) throws IOException {
- state.setReadOnly(false);
- return Response.ok("off");
- }
+ permissionBackend.user(requestingUser).checkAny(Set.of(ADMINISTRATE_SERVER, MAINTAIN_SERVER));
}
}