From 514101d90c5ad1daec526012d1d7ce4a8b9c44eb Mon Sep 17 00:00:00 2001 From: martin-schaub Date: Tue, 9 Feb 2021 18:16:28 +0100 Subject: [PATCH] alts: Introduce AltsContext to allow outside packages accessing ALTS information --- alts/BUILD.bazel | 1 + .../main/java/io/grpc/alts/AltsContext.java | 92 +++++++++++++++++++ .../java/io/grpc/alts/AltsContextUtil.java | 56 +++++++++++ .../io/grpc/alts/AltsContextUtilTest.java | 91 ++++++++++++++++++ 4 files changed, 240 insertions(+) create mode 100644 alts/src/main/java/io/grpc/alts/AltsContext.java create mode 100644 alts/src/main/java/io/grpc/alts/AltsContextUtil.java create mode 100644 alts/src/test/java/io/grpc/alts/AltsContextUtilTest.java diff --git a/alts/BUILD.bazel b/alts/BUILD.bazel index b4468c89c57..c99689bac11 100644 --- a/alts/BUILD.bazel +++ b/alts/BUILD.bazel @@ -35,6 +35,7 @@ java_library( visibility = ["//visibility:public"], deps = [ ":alts_internal", + ":handshaker_java_proto", ":handshaker_java_grpc", "//api", "//auth", diff --git a/alts/src/main/java/io/grpc/alts/AltsContext.java b/alts/src/main/java/io/grpc/alts/AltsContext.java new file mode 100644 index 00000000000..f264ad112d7 --- /dev/null +++ b/alts/src/main/java/io/grpc/alts/AltsContext.java @@ -0,0 +1,92 @@ +/* + * Copyright 2018 The gRPC Authors + * + * 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 io.grpc.alts; + +import io.grpc.ExperimentalApi; +import io.grpc.alts.internal.AltsInternalContext; +import io.grpc.alts.internal.HandshakerResult; +import io.grpc.alts.internal.Identity; +import io.grpc.alts.internal.SecurityLevel; + +/** {@code AltsContext} contains security-related information on the ALTS channel. */ +@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7864") +public final class AltsContext { + + private final AltsInternalContext wrapped; + + AltsContext(AltsInternalContext wrapped) { + this.wrapped = wrapped; + } + + /** + * Creates an {@code AltsContext} for testing purposes. + * @param peerServiceAccount the peer service account of the to be created {@code AltsContext} + * @param localServiceAccount the local service account of the to be created {@code AltsContext} + * @return the created {@code AltsContext} + */ + public static AltsContext createTestInstance(String peerServiceAccount, + String localServiceAccount) { + return new AltsContext(new AltsInternalContext(HandshakerResult.newBuilder() + .setPeerIdentity(Identity.newBuilder().setServiceAccount(peerServiceAccount).build()) + .setLocalIdentity(Identity.newBuilder().setServiceAccount(localServiceAccount).build()) + .build())); + } + + /** + * Get security level. + * + * @return the context's security level. + */ + public SecurityLevel getSecurityLevel() { + switch (wrapped.getSecurityLevel()) { + case SECURITY_NONE: + return SecurityLevel.SECURITY_NONE; + case INTEGRITY_ONLY: + return SecurityLevel.INTEGRITY_ONLY; + case INTEGRITY_AND_PRIVACY: + return SecurityLevel.INTEGRITY_AND_PRIVACY; + default: + return SecurityLevel.UNKNOWN; + } + } + + /** + * Get peer service account. + * + * @return the context's peer service account. + */ + public String getPeerServiceAccount() { + return wrapped.getPeerServiceAccount(); + } + + /** + * Get local service account. + * + * @return the context's local service account. + */ + public String getLocalServiceAccount() { + return wrapped.getLocalServiceAccount(); + } + + /** SecurityLevel of the ALTS channel. */ + public enum SecurityLevel { + UNKNOWN, + SECURITY_NONE, + INTEGRITY_ONLY, + INTEGRITY_AND_PRIVACY, + } +} diff --git a/alts/src/main/java/io/grpc/alts/AltsContextUtil.java b/alts/src/main/java/io/grpc/alts/AltsContextUtil.java new file mode 100644 index 00000000000..a5d7c0e3ff9 --- /dev/null +++ b/alts/src/main/java/io/grpc/alts/AltsContextUtil.java @@ -0,0 +1,56 @@ +/* + * Copyright 2018 The gRPC Authors + * + * 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 io.grpc.alts; + +import io.grpc.ExperimentalApi; +import io.grpc.ServerCall; +import io.grpc.alts.internal.AltsInternalContext; +import io.grpc.alts.internal.AltsProtocolNegotiator; + +/** Utility class for {@link AltsContext}. */ +@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7864") +public final class AltsContextUtil { + + private AltsContextUtil(){} + + /** + * Creates a {@link AltsContext} from ALTS context information in the {@link ServerCall}. + * + * @param call the {@link ServerCall} containing the ALTS information + * @return the created {@link AltsContext} + * @throws IllegalArgumentException if the {@link ServerCall} has no ALTS information. + */ + public static AltsContext createFrom(ServerCall call) { + Object authContext = call.getAttributes().get(AltsProtocolNegotiator.AUTH_CONTEXT_KEY); + if (!(authContext instanceof AltsInternalContext)) { + throw new IllegalArgumentException("No ALTS context information found"); + } + return new AltsContext((AltsInternalContext) authContext); + } + + /** + * Checks if the {@link ServerCall} contains ALTS information. + * + * @param call the {@link ServerCall} to check + * @return true, if the {@link ServerCall} contains ALTS information and false otherwise. + */ + public static boolean check(ServerCall call) { + Object authContext = call.getAttributes().get(AltsProtocolNegotiator.AUTH_CONTEXT_KEY); + return authContext instanceof AltsInternalContext; + } +} diff --git a/alts/src/test/java/io/grpc/alts/AltsContextUtilTest.java b/alts/src/test/java/io/grpc/alts/AltsContextUtilTest.java new file mode 100644 index 00000000000..6fd2d840d45 --- /dev/null +++ b/alts/src/test/java/io/grpc/alts/AltsContextUtilTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 2018 The gRPC Authors + * + * 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 io.grpc.alts; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.grpc.Attributes; +import io.grpc.ServerCall; +import io.grpc.alts.AltsContext.SecurityLevel; +import io.grpc.alts.internal.AltsInternalContext; +import io.grpc.alts.internal.AltsProtocolNegotiator; +import io.grpc.alts.internal.HandshakerResult; +import io.grpc.alts.internal.Identity; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link AltsContextUtil}. */ +@RunWith(JUnit4.class) +public class AltsContextUtilTest { + + private final ServerCall call = mock(ServerCall.class); + + @Test + public void check_noAttributeValue() { + when(call.getAttributes()).thenReturn(Attributes.newBuilder().build()); + + assertFalse(AltsContextUtil.check(call)); + } + + @Test + public void contains_unexpectedAttributeValueType() { + when(call.getAttributes()).thenReturn(Attributes.newBuilder() + .set(AltsProtocolNegotiator.AUTH_CONTEXT_KEY, new Object()) + .build()); + + assertFalse(AltsContextUtil.check(call)); + } + + @Test + public void contains_altsInternalContext() { + when(call.getAttributes()).thenReturn(Attributes.newBuilder() + .set(AltsProtocolNegotiator.AUTH_CONTEXT_KEY, AltsInternalContext.getDefaultInstance()) + .build()); + + assertTrue(AltsContextUtil.check(call)); + } + + @Test + public void from_altsInternalContext() { + HandshakerResult handshakerResult = + HandshakerResult.newBuilder() + .setPeerIdentity(Identity.newBuilder().setServiceAccount("remote@peer")) + .setLocalIdentity(Identity.newBuilder().setServiceAccount("local@peer")) + .build(); + when(call.getAttributes()).thenReturn(Attributes.newBuilder() + .set(AltsProtocolNegotiator.AUTH_CONTEXT_KEY, new AltsInternalContext(handshakerResult)) + .build()); + + AltsContext context = AltsContextUtil.createFrom(call); + assertEquals("remote@peer", context.getPeerServiceAccount()); + assertEquals("local@peer", context.getLocalServiceAccount()); + assertEquals(SecurityLevel.INTEGRITY_AND_PRIVACY, context.getSecurityLevel()); + } + + @Test(expected = IllegalArgumentException.class) + public void from_noAttributeValue() { + when(call.getAttributes()).thenReturn(Attributes.newBuilder().build()); + + AltsContextUtil.createFrom(call); + } +}