| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package io.v.v23.services.groups; |
| |
| import com.google.common.base.Function; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableMultimap; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Multimap; |
| import io.v.impl.google.naming.NamingUtil; |
| import io.v.impl.google.services.groups.GroupServer; |
| import io.v.v23.V; |
| import io.v.v23.VFutures; |
| import io.v.v23.context.VContext; |
| import io.v.v23.naming.Endpoint; |
| import io.v.v23.rpc.ListenSpec; |
| import io.v.v23.rpc.Server; |
| import io.v.v23.security.BlessingPattern; |
| import io.v.v23.security.BlessingRoots; |
| import io.v.v23.security.Blessings; |
| import io.v.v23.security.Call; |
| import io.v.v23.security.CallParams; |
| import io.v.v23.security.VPrincipal; |
| import io.v.v23.security.VSecurity; |
| import io.v.v23.security.VSigner; |
| import io.v.v23.security.access.Access; |
| import io.v.v23.security.access.AccessList; |
| import io.v.v23.security.access.Permissions; |
| import io.v.v23.vdl.VdlValue; |
| import io.v.v23.verror.VException; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| |
| import java.security.interfaces.ECPublicKey; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.List; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static org.junit.Assert.fail; |
| |
| /** |
| * Tests the implementation of {@code PermissionsAuthorizer}. |
| */ |
| @RunWith(Parameterized.class) |
| public class PermissionsAuthorizerTest { |
| private static final VContext CONTEXT; |
| private static final Blessings SERVER_BLESSINGS; |
| private static final VPrincipal SERVER_PRINCIPAL; |
| private static final VPrincipal CLIENT_PRINCIPAL; |
| private static final PermissionsAuthorizer AUTHORIZER; |
| |
| private static final Server GROUP_SERVER; |
| |
| static { |
| try { |
| CONTEXT = V.withListenSpec(V.init(), V.getListenSpec(V.init()).withAddress( |
| new ListenSpec.Address("tcp", "localhost:0"))); |
| AccessList acl = new AccessList( |
| ImmutableList.of(new BlessingPattern("...")), ImmutableList.<String>of()); |
| Permissions allowAll = new Permissions(ImmutableMap.of( |
| io.v.v23.security.access.Constants.READ.getValue(), acl, |
| io.v.v23.security.access.Constants.WRITE.getValue(), acl, |
| io.v.v23.security.access.Constants.ADMIN.getValue(), acl)); |
| CLIENT_PRINCIPAL = newPrincipal(); |
| SERVER_PRINCIPAL = newPrincipal(); |
| SERVER_BLESSINGS = SERVER_PRINCIPAL.blessSelf("server"); |
| |
| // Start group server. |
| GROUP_SERVER = V.getServer( |
| GroupServer.withNewServer(CONTEXT, new GroupServer.Params() |
| .withStorageEngine(GroupServer.StorageEngine.MEMSTORE))); |
| assertThat(GROUP_SERVER).isNotNull(); |
| assertThat(GROUP_SERVER.getStatus().getEndpoints()).isNotEmpty(); |
| Endpoint groupServerEndpoint = GROUP_SERVER.getStatus().getEndpoints()[0]; |
| String groupNameReaders = NamingUtil.join(groupServerEndpoint.name(), "readers"); |
| String groupNameWriters = NamingUtil.join(groupServerEndpoint.name(), "writers"); |
| |
| // Populate the group server. |
| { |
| GroupClient client = GroupClientFactory.getGroupClient(groupNameReaders); |
| VFutures.sync(client.create(CONTEXT, allowAll, ImmutableList.of( |
| new BlessingPatternChunk("root:alice"), |
| new BlessingPatternChunk("root:bob")))); |
| } |
| { |
| GroupClient client = GroupClientFactory.getGroupClient(groupNameWriters); |
| VFutures.sync(client.create(CONTEXT, allowAll, ImmutableList.of( |
| new BlessingPatternChunk("root:alice")))); |
| } |
| |
| AUTHORIZER = PermissionsAuthorizer.create(new Permissions(ImmutableMap.of( |
| "Read", new AccessList( |
| ImmutableList.of(new BlessingPattern("<grp:" + groupNameReaders + ">")), |
| null), |
| "Write", new AccessList( |
| ImmutableList.of(new BlessingPattern("<grp:" + groupNameWriters + ">")), |
| null))), Access.typicalTagType()); |
| } catch (VException e) { |
| throw new RuntimeException(e); |
| } catch (GroupServer.StartException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| private static VPrincipal newPrincipal() throws VException { |
| VSigner signer = VSecurity.newInMemorySigner(); |
| return VSecurity.newPrincipal(signer, null, new TrustAllRoots()); |
| } |
| |
| private final String methodName; |
| private final Blessings clientBlessings; |
| private final boolean shouldAccept; |
| private final VdlValue[] methodTags; |
| |
| @Parameterized.Parameters |
| public static Collection<Object[]> data() { |
| return Arrays.asList(new Object[][]{ |
| {"get", io.v.v23.security.access.Constants.READ, |
| ImmutableList.of("root:alice"), true}, |
| {"get", io.v.v23.security.access.Constants.WRITE, |
| ImmutableList.of("root:alice"), true}, |
| {"get", io.v.v23.security.access.Constants.READ, |
| ImmutableList.of("root:bob"), true}, |
| {"put", io.v.v23.security.access.Constants.WRITE, |
| ImmutableList.of("root:bob"), false} |
| }); |
| } |
| |
| public PermissionsAuthorizerTest(String methodName, VdlValue methodTag, |
| List<String> clientBlessingNames, |
| boolean isAccepted) throws VException { |
| this.methodName = methodName; |
| this.methodTags = new VdlValue[]{ methodTag }; |
| this.clientBlessings = VSecurity.unionOfBlessings(Lists.transform( |
| clientBlessingNames, new Function<String, Blessings>() { |
| @Override |
| public Blessings apply(String name) { |
| try { |
| return CLIENT_PRINCIPAL.blessSelf(name); |
| } catch (VException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| }).toArray(new Blessings[0])); |
| this.shouldAccept = isAccepted; |
| } |
| |
| @Test |
| public void testAuthorize() throws VException { |
| Call call = VSecurity.newCall(new CallParams() |
| .withLocalPrincipal(SERVER_PRINCIPAL) |
| .withLocalBlessings(SERVER_BLESSINGS) |
| .withRemoteBlessings(clientBlessings) |
| .withMethod(methodName) |
| .withMethodTags(methodTags)); |
| try { |
| AUTHORIZER.authorize(CONTEXT, call); |
| if (!shouldAccept) { |
| fail(String.format( |
| "Access granted for method %s to %s", methodName, clientBlessings)); |
| } |
| } catch (VException e) { |
| if (shouldAccept) { |
| throw e; |
| } |
| } |
| } |
| |
| private static class TrustAllRoots implements BlessingRoots { |
| @Override |
| public void add(ECPublicKey root, BlessingPattern pattern) throws VException {} |
| @Override |
| public void recognized(ECPublicKey root, String blessing) throws VException {} |
| @Override |
| public String debugString() { return TrustAllRoots.class.toString(); } |
| @Override |
| public Multimap<BlessingPattern, ECPublicKey> dump() { |
| return ImmutableMultimap.of(); |
| } |
| } |
| } |