/*
* Copyright © 2015-2016 Cask Data, Inc.
*
* 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 co.cask.cdap.security.spi.authorization;
import co.cask.cdap.proto.id.DatasetId;
import co.cask.cdap.proto.id.EntityId;
import co.cask.cdap.proto.id.Ids;
import co.cask.cdap.proto.id.NamespaceId;
import co.cask.cdap.proto.security.Action;
import co.cask.cdap.proto.security.Principal;
import co.cask.cdap.proto.security.Privilege;
import co.cask.cdap.proto.security.Role;
import org.junit.Assert;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
/**
* Tests for {@link Authorizer}.
*/
public abstract class AuthorizerTest {
private final NamespaceId namespace = Ids.namespace("foo");
private final Principal user = new Principal("alice", Principal.PrincipalType.USER);
protected abstract Authorizer get();
@Test
public void testSimple() throws Exception {
Authorizer authorizer = get();
verifyAuthFailure(namespace, user, Action.READ);
authorizer.grant(namespace, user, Collections.singleton(Action.READ));
authorizer.enforce(namespace, user, Action.READ);
Set<Privilege> expectedPrivileges = new HashSet<>();
expectedPrivileges.add(new Privilege(namespace, Action.READ));
Assert.assertEquals(expectedPrivileges, authorizer.listPrivileges(user));
authorizer.revoke(namespace, user, Collections.singleton(Action.READ));
verifyAuthFailure(namespace, user, Action.READ);
}
@Test
public void testWildcard() throws Exception {
Authorizer authorizer = get();
verifyAuthFailure(namespace, user, Action.READ);
authorizer.grant(namespace, user, EnumSet.allOf(Action.class));
authorizer.enforce(namespace, user, Action.READ);
authorizer.enforce(namespace, user, Action.WRITE);
authorizer.enforce(namespace, user, Action.ADMIN);
authorizer.enforce(namespace, user, Action.EXECUTE);
authorizer.revoke(namespace, user, EnumSet.allOf(Action.class));
verifyAuthFailure(namespace, user, Action.READ);
}
@Test
public void testAll() throws Exception {
Authorizer authorizer = get();
verifyAuthFailure(namespace, user, Action.READ);
authorizer.grant(namespace, user, Collections.singleton(Action.ALL));
authorizer.enforce(namespace, user, Action.READ);
authorizer.enforce(namespace, user, Action.WRITE);
authorizer.enforce(namespace, user, Action.ADMIN);
authorizer.enforce(namespace, user, Action.EXECUTE);
authorizer.revoke(namespace, user, EnumSet.allOf(Action.class));
verifyAuthFailure(namespace, user, Action.READ);
Principal role = new Principal("admins", Principal.PrincipalType.ROLE);
authorizer.grant(namespace, user, Collections.singleton(Action.READ));
authorizer.grant(namespace, role, Collections.singleton(Action.ALL));
authorizer.revoke(namespace);
verifyAuthFailure(namespace, user, Action.READ);
verifyAuthFailure(namespace, role, Action.ALL);
}
@Test
public void testHierarchy() throws Exception {
Authorizer authorizer = get();
DatasetId dataset = namespace.dataset("bar");
verifyAuthFailure(namespace, user, Action.READ);
authorizer.grant(namespace, user, Collections.singleton(Action.READ));
authorizer.enforce(dataset, user, Action.READ);
authorizer.grant(dataset, user, Collections.singleton(Action.WRITE));
verifyAuthFailure(namespace, user, Action.WRITE);
authorizer.revoke(namespace, user, Collections.singleton(Action.READ));
authorizer.revoke(dataset);
verifyAuthFailure(namespace, user, Action.READ);
}
@Test
public void testRBAC() throws Exception {
Authorizer authorizer = get();
Role admins = new Role("admins");
Role engineers = new Role("engineers");
// create a role
authorizer.createRole(admins);
// add another role
authorizer.createRole(engineers);
// listing role should show the added role
Set<Role> roles = authorizer.listAllRoles();
Assert.assertEquals(Collections.singleton(Arrays.asList(admins, engineers)), roles);
// creating a role which already exists should throw an exception
try {
authorizer.createRole(admins);
Assert.fail(String.format("Created a role %s which already exists. Should have failed.", admins.getName()));
} catch (RoleAlreadyExistsException expected) {
// expected
}
// drop an existing role
authorizer.dropRole(admins);
// the list should not have the dropped role
roles = authorizer.listAllRoles();
Assert.assertEquals(Collections.singleton(engineers), roles);
// dropping a non-existing role should throw exception
try {
authorizer.dropRole(admins);
Assert.fail(String.format("Dropped a role %s which does not exists. Should have failed.", admins.getName()));
} catch (RoleNotFoundException expected) {
// expected
}
// add an user to an existing role
Principal spiderman = new Principal("spiderman", Principal.PrincipalType.USER);
authorizer.addRoleToPrincipal(engineers, spiderman);
// add an user to an non-existing role should throw an exception
try {
authorizer.addRoleToPrincipal(admins, spiderman);
Assert.fail(String.format("Added role %s to principal %s. Should have failed.", admins, spiderman));
} catch (RoleNotFoundException expected) {
// expectedRoles
}
// check listing roles for spiderman have engineers role
Assert.assertEquals(Collections.singleton(engineers), authorizer.listRoles(spiderman));
// authorization checks with roles
NamespaceId ns1 = Ids.namespace("ns1");
// check that spiderman who has engineers roles cannot read from ns1
verifyAuthFailure(ns1, spiderman, Action.READ);
// give a permission to engineers role
authorizer.grant(ns1, engineers, Collections.singleton(Action.READ));
// check that a spiderman who has engineers role has access
authorizer.enforce(ns1, spiderman, Action.READ);
// list privileges for spiderman should have read action on ns1
Assert.assertEquals((Collections.singleton(new Privilege(ns1, Action.READ))),
authorizer.listPrivileges(spiderman));
// revoke action from the role
authorizer.revoke(ns1, engineers, (Collections.singleton(Action.READ)));
// now the privileges for spiderman should be empty
Assert.assertEquals(Collections.EMPTY_SET, authorizer.listPrivileges(spiderman));
// check that the user of this role is not authorized to do the revoked operation
verifyAuthFailure(ns1, spiderman, Action.READ);
// remove an user from a existing role
authorizer.removeRoleFromPrincipal(engineers, spiderman);
// check listing roles for spiderman should be empty
Assert.assertEquals(Collections.EMPTY_SET, authorizer.listRoles(spiderman));
// remove an user from a non-existing role should throw exception
try {
authorizer.removeRoleFromPrincipal(admins, spiderman);
Assert.fail(String.format("Removed non-existing role %s from principal %s. Should have failed.", admins,
spiderman));
} catch (RoleNotFoundException expected) {
// expectedRoles
}
}
private void verifyAuthFailure(EntityId entity, Principal principal, Action action) throws Exception {
try {
get().enforce(entity, principal, action);
Assert.fail(String.format("Expected authorization failure, but it succeeded for entity %s, principal %s," +
" action %s", entity, principal, action));
} catch (UnauthorizedException expected) {
// expected
}
}
}