blob: 350ee2090e2fb296d686e01f7ed38be05986d75b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.cassandra.auth;
import java.util.Set;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLStatement;
import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.cql3.UntypedResultSet;
import org.apache.cassandra.cql3.statements.AlterRoleStatement;
import org.apache.cassandra.cql3.statements.AuthenticationStatement;
import org.apache.cassandra.cql3.statements.CreateRoleStatement;
import org.apache.cassandra.cql3.statements.DropRoleStatement;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.service.ClientState;
import static org.apache.cassandra.auth.AuthKeyspace.NETWORK_PERMISSIONS;
import static org.apache.cassandra.auth.RoleTestUtils.LocalCassandraRoleManager;
import static org.apache.cassandra.schema.SchemaConstants.AUTH_KEYSPACE_NAME;
import static org.apache.cassandra.auth.RoleTestUtils.getReadCount;
public class CassandraNetworkAuthorizerTest
{
private static void setupSuperUser()
{
QueryProcessor.executeInternal(String.format("INSERT INTO %s.%s (role, is_superuser, can_login, salted_hash) "
+ "VALUES ('%s', true, true, '%s')",
AUTH_KEYSPACE_NAME,
AuthKeyspace.ROLES,
CassandraRoleManager.DEFAULT_SUPERUSER_NAME,
"xxx"));
}
@BeforeClass
public static void defineSchema() throws ConfigurationException
{
SchemaLoader.prepareServer();
SchemaLoader.setupAuth(new LocalCassandraRoleManager(),
new PasswordAuthenticator(),
new RoleTestUtils.LocalCassandraAuthorizer(),
new RoleTestUtils.LocalCassandraNetworkAuthorizer());
setupSuperUser();
// not strictly necessary to init the cache here, but better to be explicit
Roles.initRolesCache(DatabaseDescriptor.getRoleManager(), () -> true);
}
@Before
public void clear()
{
Keyspace.open(AUTH_KEYSPACE_NAME).getColumnFamilyStore(NETWORK_PERMISSIONS).truncateBlocking();
}
private static void assertNoDcPermRow(String username)
{
String query = String.format("SELECT dcs FROM %s.%s WHERE role = '%s'",
AUTH_KEYSPACE_NAME,
NETWORK_PERMISSIONS,
RoleResource.role(username).getName());
UntypedResultSet results = QueryProcessor.executeInternal(query);
Assert.assertTrue(results.isEmpty());
}
private static void assertDcPermRow(String username, String... dcs)
{
Set<String> expected = Sets.newHashSet(dcs);
String query = String.format("SELECT dcs FROM %s.%s WHERE role = '%s'",
AUTH_KEYSPACE_NAME,
NETWORK_PERMISSIONS,
RoleResource.role(username).getName());
UntypedResultSet results = QueryProcessor.executeInternal(query);
UntypedResultSet.Row row = Iterables.getOnlyElement(results);
Set<String> actual = row.getFrozenSet("dcs", UTF8Type.instance);
Assert.assertEquals(expected, actual);
}
private static String createName()
{
return RandomStringUtils.randomAlphabetic(8).toLowerCase();
}
private static ClientState getClientState()
{
ClientState state = ClientState.forInternalCalls();
state.login(new AuthenticatedUser(CassandraRoleManager.DEFAULT_SUPERUSER_NAME));
return state;
}
private static void auth(String query, Object... args)
{
CQLStatement statement = QueryProcessor.parseStatement(String.format(query, args)).prepare(ClientState.forInternalCalls());
assert statement instanceof CreateRoleStatement
|| statement instanceof AlterRoleStatement
|| statement instanceof DropRoleStatement;
AuthenticationStatement authStmt = (AuthenticationStatement) statement;
// invalidate roles cache so that any changes to the underlying roles are picked up
Roles.clearCache();
authStmt.execute(getClientState());
}
private static DCPermissions dcPerms(String username)
{
AuthenticatedUser user = new AuthenticatedUser(username);
return DatabaseDescriptor.getNetworkAuthorizer().authorize(user.getPrimaryRole());
}
@Test
public void create()
{
String username = createName();
// user should implicitly have access to all datacenters
assertNoDcPermRow(username);
auth("CREATE ROLE %s WITH password = 'password' AND LOGIN = true AND ACCESS TO DATACENTERS {'dc1', 'dc2'}", username);
Assert.assertEquals(DCPermissions.subset("dc1", "dc2"), dcPerms(username));
assertDcPermRow(username, "dc1", "dc2");
}
@Test
public void alter()
{
String username = createName();
assertNoDcPermRow(username);
// user should implicitly have access to all datacenters
auth("CREATE ROLE %s WITH password = 'password' AND LOGIN = true", username);
Assert.assertEquals(DCPermissions.all(), dcPerms(username));
assertDcPermRow(username);
// unless explicitly restricted
auth("ALTER ROLE %s WITH ACCESS TO DATACENTERS {'dc1', 'dc2'}", username);
Assert.assertEquals(DCPermissions.subset("dc1", "dc2"), dcPerms(username));
assertDcPermRow(username, "dc1", "dc2");
auth("ALTER ROLE %s WITH ACCESS TO DATACENTERS {'dc1'}", username);
Assert.assertEquals(DCPermissions.subset("dc1"), dcPerms(username));
assertDcPermRow(username, "dc1");
auth("ALTER ROLE %s WITH ACCESS TO ALL DATACENTERS", username);
Assert.assertEquals(DCPermissions.all(), dcPerms(username));
assertDcPermRow(username);
}
@Test
public void drop()
{
String username = createName();
assertNoDcPermRow(username);
// user should implicitly have access to all datacenters
auth("CREATE ROLE %s WITH password = 'password' AND LOGIN = true AND ACCESS TO DATACENTERS {'dc1'}", username);
assertDcPermRow(username, "dc1");
auth("DROP ROLE %s", username);
assertNoDcPermRow(username);
}
@Test
public void superUser()
{
String username = createName();
auth("CREATE ROLE %s WITH password = 'password' AND LOGIN = true AND ACCESS TO DATACENTERS {'dc1'}", username);
Assert.assertEquals(DCPermissions.subset("dc1"), dcPerms(username));
assertDcPermRow(username, "dc1");
// clear the roles cache to lose the (non-)superuser status for the user
Roles.clearCache();
auth("ALTER ROLE %s WITH superuser = true", username);
Assert.assertEquals(DCPermissions.all(), dcPerms(username));
}
@Test
public void cantLogin()
{
String username = createName();
auth("CREATE ROLE %s", username);
Assert.assertEquals(DCPermissions.none(), dcPerms(username));
}
@Test
public void getLoginPrivilegeFromRolesCache() throws Exception
{
String username = createName();
auth("CREATE ROLE %s", username);
long readCount = getReadCount();
dcPerms(username);
Assert.assertEquals(++readCount, getReadCount());
dcPerms(username);
Assert.assertEquals(readCount, getReadCount());
}
}