mirror of https://github.com/hyperledger/besu
Privacy RPC multi-tenancy token public enclave key validation (#266)
Signed-off-by: Jason Frame <jasonwframe@gmail.com>pull/269/head
parent
991dcf9237
commit
c8ff44d035
@ -0,0 +1,57 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods; |
||||
|
||||
import static org.apache.logging.log4j.LogManager.getLogger; |
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcUnauthorizedResponse; |
||||
|
||||
import java.util.Optional; |
||||
|
||||
import io.vertx.ext.auth.User; |
||||
import org.apache.logging.log4j.Logger; |
||||
|
||||
public class MultiTenancyRpcMethodDecorator implements JsonRpcMethod { |
||||
private static final Logger LOG = getLogger(); |
||||
private JsonRpcMethod rpcMethod; |
||||
|
||||
public MultiTenancyRpcMethodDecorator(final JsonRpcMethod rpcMethod) { |
||||
this.rpcMethod = rpcMethod; |
||||
} |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return rpcMethod.getName(); |
||||
} |
||||
|
||||
@Override |
||||
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { |
||||
final Optional<User> user = requestContext.getUser(); |
||||
final Object id = requestContext.getRequest().getId(); |
||||
if (user.isEmpty()) { |
||||
LOG.error("Request does not contain an authorization token"); |
||||
return new JsonRpcUnauthorizedResponse(id, JsonRpcError.UNAUTHORIZED); |
||||
} else if (MultiTenancyUserUtil.enclavePublicKey(user).isEmpty()) { |
||||
LOG.error("Request token does not contain an enclave public key"); |
||||
return new JsonRpcUnauthorizedResponse(id, JsonRpcError.UNAUTHORIZED); |
||||
} else { |
||||
return rpcMethod.response(requestContext); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,27 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods; |
||||
|
||||
import java.util.Optional; |
||||
|
||||
import io.vertx.ext.auth.User; |
||||
|
||||
public class MultiTenancyUserUtil { |
||||
private static final String ENCLAVE_PRIVACY_PUBLIC_KEY_CLAIM = "privacyPublicKey"; |
||||
|
||||
public static Optional<String> enclavePublicKey(final Optional<User> user) { |
||||
return user.map(u -> u.principal().getString(ENCLAVE_PRIVACY_PUBLIC_KEY_CLAIM)); |
||||
} |
||||
} |
@ -0,0 +1,94 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponseType; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcUnauthorizedResponse; |
||||
|
||||
import io.vertx.core.json.JsonObject; |
||||
import io.vertx.ext.auth.jwt.impl.JWTUser; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.Mock; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
|
||||
@RunWith(MockitoJUnitRunner.class) |
||||
public class MultiTenancyRpcMethodDecoratorTest { |
||||
|
||||
@Mock private JsonRpcMethod jsonRpcMethod; |
||||
private final JsonRpcRequest rpcRequest = new JsonRpcRequest("1", "test", new String[] {"a"}); |
||||
|
||||
@Test |
||||
public void delegatesWhenHasValidToken() { |
||||
final JsonObject principle = new JsonObject(); |
||||
principle.put("privacyPublicKey", "ABC123"); |
||||
final JWTUser user = new JWTUser(principle, ""); |
||||
final JsonRpcRequestContext rpcRequestContext = new JsonRpcRequestContext(rpcRequest, user); |
||||
|
||||
when(jsonRpcMethod.response(rpcRequestContext)) |
||||
.thenReturn(new JsonRpcSuccessResponse("1", "b")); |
||||
when(jsonRpcMethod.getName()).thenReturn("delegate"); |
||||
|
||||
final MultiTenancyRpcMethodDecorator tokenRpcDecorator = |
||||
new MultiTenancyRpcMethodDecorator(jsonRpcMethod); |
||||
|
||||
assertThat(tokenRpcDecorator.getName()).isEqualTo("delegate"); |
||||
|
||||
final JsonRpcResponse response = tokenRpcDecorator.response(rpcRequestContext); |
||||
assertThat(response.getType()).isEqualTo(JsonRpcResponseType.SUCCESS); |
||||
final JsonRpcSuccessResponse successResponse = (JsonRpcSuccessResponse) response; |
||||
assertThat(successResponse.getResult()).isEqualTo("b"); |
||||
} |
||||
|
||||
@Test |
||||
public void failsWhenHasNoToken() { |
||||
final JsonRpcRequestContext rpcRequestContext = new JsonRpcRequestContext(rpcRequest); |
||||
final MultiTenancyRpcMethodDecorator tokenRpcDecorator = |
||||
new MultiTenancyRpcMethodDecorator(jsonRpcMethod); |
||||
when(jsonRpcMethod.getName()).thenReturn("delegate"); |
||||
|
||||
assertThat(tokenRpcDecorator.getName()).isEqualTo("delegate"); |
||||
|
||||
final JsonRpcResponse response = tokenRpcDecorator.response(rpcRequestContext); |
||||
assertThat(response.getType()).isEqualTo(JsonRpcResponseType.UNAUTHORIZED); |
||||
final JsonRpcUnauthorizedResponse errorResponse = (JsonRpcUnauthorizedResponse) response; |
||||
assertThat(errorResponse.getError()).isEqualTo(JsonRpcError.UNAUTHORIZED); |
||||
} |
||||
|
||||
@Test |
||||
public void failsWhenTokenDoesNotHavePrivacyPublicKey() { |
||||
final JWTUser user = new JWTUser(new JsonObject(), ""); |
||||
final JsonRpcRequestContext rpcRequestContext = new JsonRpcRequestContext(rpcRequest, user); |
||||
final MultiTenancyRpcMethodDecorator tokenRpcDecorator = |
||||
new MultiTenancyRpcMethodDecorator(jsonRpcMethod); |
||||
when(jsonRpcMethod.getName()).thenReturn("delegate"); |
||||
|
||||
assertThat(tokenRpcDecorator.getName()).isEqualTo("delegate"); |
||||
|
||||
final JsonRpcResponse response = tokenRpcDecorator.response(rpcRequestContext); |
||||
assertThat(response.getType()).isEqualTo(JsonRpcResponseType.UNAUTHORIZED); |
||||
final JsonRpcUnauthorizedResponse errorResponse = (JsonRpcUnauthorizedResponse) response; |
||||
assertThat(errorResponse.getError()).isEqualTo(JsonRpcError.UNAUTHORIZED); |
||||
} |
||||
} |
@ -0,0 +1,51 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods; |
||||
|
||||
import static java.util.Optional.empty; |
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.MultiTenancyUserUtil.enclavePublicKey; |
||||
|
||||
import java.util.Optional; |
||||
|
||||
import io.vertx.core.json.JsonObject; |
||||
import io.vertx.ext.auth.User; |
||||
import io.vertx.ext.auth.jwt.impl.JWTUser; |
||||
import org.junit.Test; |
||||
|
||||
public class MultiTenancyUserUtilTest { |
||||
|
||||
@Test |
||||
public void noEnclavePublicKeyWhenNoUserProvided() { |
||||
assertThat(enclavePublicKey(empty())).isEmpty(); |
||||
} |
||||
|
||||
@Test |
||||
public void noEnclavePublicKeyWhenUserWithoutEnclavePublicKeyClaimProvided() { |
||||
final JsonObject token = new JsonObject(); |
||||
final Optional<User> user = Optional.of(new JWTUser(token, "")); |
||||
|
||||
assertThat(enclavePublicKey(user)).isEmpty(); |
||||
} |
||||
|
||||
@Test |
||||
public void enclavePublicKeyKeyReturnedForUserWithEnclavePublicKeyClaim() { |
||||
final JsonObject principle = new JsonObject(); |
||||
principle.put("privacyPublicKey", "ABC123"); |
||||
final Optional<User> user = Optional.of(new JWTUser(principle, "")); |
||||
|
||||
assertThat(enclavePublicKey(user)).contains("ABC123"); |
||||
} |
||||
} |
Loading…
Reference in new issue