diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/authentication/AuthenticationUtils.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/authentication/AuthenticationUtils.java index cac92cf333..92303aa90c 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/authentication/AuthenticationUtils.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/authentication/AuthenticationUtils.java @@ -37,27 +37,31 @@ public class AuthenticationUtils { AtomicBoolean foundMatchingPermission = new AtomicBoolean(); - if (authenticationService.isPresent()) { - if (optionalUser.isPresent()) { - User user = optionalUser.get(); - for (String perm : jsonRpcMethod.getPermissions()) { - user.isAuthorized( - perm, - (authed) -> { - if (authed.result()) { - LOG.trace( - "user {} authorized : {} via permission {}", - user, - jsonRpcMethod.getName(), - perm); - foundMatchingPermission.set(true); - } - }); + if (authenticationService.isEmpty()) { + // no auth provider configured thus anything is permitted + return true; + } + + if (optionalUser.isPresent()) { + User user = optionalUser.get(); + for (String perm : jsonRpcMethod.getPermissions()) { + user.isAuthorized( + perm, + (authed) -> { + if (authed.result()) { + LOG.trace( + "user {} authorized : {} via permission {}", + user, + jsonRpcMethod.getName(), + perm); + foundMatchingPermission.set(true); + } + }); + // exit if a matching permission was found, no need to keep checking + if (foundMatchingPermission.get()) { + return foundMatchingPermission.get(); } } - } else { - // no auth provider configured thus anything is permitted - foundMatchingPermission.set(true); } if (!foundMatchingPermission.get()) { diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/JsonRpcMethod.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/JsonRpcMethod.java index 24faf23867..20e3a521d1 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/JsonRpcMethod.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/JsonRpcMethod.java @@ -23,7 +23,7 @@ import java.util.List; public interface JsonRpcMethod { /** - * Standardised JSON-RPC method name. + * Standardized JSON-RPC method name. * * @return identification of the JSON-RPC method. */ @@ -38,15 +38,17 @@ public interface JsonRpcMethod { JsonRpcResponse response(JsonRpcRequestContext request); /** - * The list of Permissions that correspond to this JSON-RPC method. e.g. [net/*, net/listening] + * The list of Permissions that correspond to this JSON-RPC method. + * + *

e.g. [*:*, net:*, net:listening] * * @return list of permissions that match this method. */ default List getPermissions() { List permissions = new ArrayList<>(); permissions.add("*:*"); - permissions.add(this.getName().replace('_', ':')); permissions.add(this.getName().substring(0, this.getName().indexOf('_')) + ":*"); + permissions.add(this.getName().replace('_', ':')); return permissions; }; } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceLoginTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceLoginTest.java index c06e9a0306..a0ed2caf69 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceLoginTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceLoginTest.java @@ -393,7 +393,7 @@ public class JsonRpcHttpServiceLoginTest { AuthenticationUtils.isPermitted( service.authenticationService, Optional.of(user), ethBlockNumber)) .isTrue(); - // eth/accounts not permitted + // eth/accounts NOT permitted assertThat( AuthenticationUtils.isPermitted( service.authenticationService, Optional.of(user), ethAccounts)) @@ -407,7 +407,7 @@ public class JsonRpcHttpServiceLoginTest { AuthenticationUtils.isPermitted( service.authenticationService, Optional.of(user), web3Sha3)) .isTrue(); - // no net permissions + // NO net permissions assertThat( AuthenticationUtils.isPermitted( service.authenticationService, Optional.of(user), netVersion)) @@ -416,6 +416,66 @@ public class JsonRpcHttpServiceLoginTest { } } + @Test + public void checkJsonRpcMethodsAvailableWithGoodCredentialsAndAllPermissions() + throws IOException { + final RequestBody body = + RequestBody.create(JSON, "{\"username\":\"adminuser\",\"password\":\"pegasys\"}"); + final Request request = new Request.Builder().post(body).url(baseUrl + "/login").build(); + try (final Response resp = client.newCall(request).execute()) { + assertThat(resp.code()).isEqualTo(200); + assertThat(resp.message()).isEqualTo("OK"); + assertThat(resp.body().contentType()).isNotNull(); + assertThat(resp.body().contentType().type()).isEqualTo("application"); + assertThat(resp.body().contentType().subtype()).isEqualTo("json"); + final String bodyString = resp.body().string(); + assertThat(bodyString).isNotNull(); + assertThat(bodyString).isNotBlank(); + + final JsonObject respBody = new JsonObject(bodyString); + final String token = respBody.getString("token"); + assertThat(token).isNotNull(); + + final JsonRpcMethod ethAccounts = new EthAccounts(); + final JsonRpcMethod netVersion = new NetVersion(Optional.of(BigInteger.valueOf(123))); + final JsonRpcMethod ethBlockNumber = new EthBlockNumber(blockchainQueries); + final JsonRpcMethod web3Sha3 = new Web3Sha3(); + final JsonRpcMethod web3ClientVersion = new Web3ClientVersion("777"); + + // adminuser has *:* permissions so everything should be allowed + jwtAuth.authenticate( + new JsonObject().put("jwt", token), + (r) -> { + assertThat(r.succeeded()).isTrue(); + final User user = r.result(); + // single eth/blockNumber method permitted + Assertions.assertThat( + AuthenticationUtils.isPermitted( + service.authenticationService, Optional.of(user), ethBlockNumber)) + .isTrue(); + // eth/accounts IS permitted + assertThat( + AuthenticationUtils.isPermitted( + service.authenticationService, Optional.of(user), ethAccounts)) + .isTrue(); + // allowed by *:* + assertThat( + AuthenticationUtils.isPermitted( + service.authenticationService, Optional.of(user), web3ClientVersion)) + .isTrue(); + assertThat( + AuthenticationUtils.isPermitted( + service.authenticationService, Optional.of(user), web3Sha3)) + .isTrue(); + // YES net permissions + assertThat( + AuthenticationUtils.isPermitted( + service.authenticationService, Optional.of(user), netVersion)) + .isTrue(); + }); + } + } + @Test public void checkPermissionsWithEmptyUser() { final JsonRpcMethod ethAccounts = new EthAccounts(); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/NetListeningTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/NetListeningTest.java index b7d1fe4921..fc0e3e216b 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/NetListeningTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/NetListeningTest.java @@ -69,7 +69,7 @@ public class NetListeningTest { @Test public void getPermissions() { List permissions = method.getPermissions(); - assertThat(permissions).containsExactlyInAnyOrder("net:*", "net:listening", "*:*"); + assertThat(permissions).containsExactly("*:*", "net:*", "net:listening"); } private JsonRpcRequestContext netListeningRequest() { diff --git a/ethereum/api/src/test/resources/JsonRpcHttpService/auth.toml b/ethereum/api/src/test/resources/JsonRpcHttpService/auth.toml index 48e7bf1e5c..3de16f86ff 100644 --- a/ethereum/api/src/test/resources/JsonRpcHttpService/auth.toml +++ b/ethereum/api/src/test/resources/JsonRpcHttpService/auth.toml @@ -2,3 +2,7 @@ password = "$2a$10$l3GA7K8g6rJ/Yv.YFSygCuI9byngpEzxgWS9qEg5emYDZomQW7fGC" permissions = ["fakePermission","eth:blockNumber","eth:subscribe","web3:*"] privacyPublicKey = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=" +[Users.adminuser] +password = "$2a$10$l3GA7K8g6rJ/Yv.YFSygCuI9byngpEzxgWS9qEg5emYDZomQW7fGC" +permissions = ["*:*"] +privacyPublicKey = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="