blob: 0aa24db8cca2248a07fbbe015bd2b2afdeb2bed7 [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.base.Objects;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.apache.cassandra.config.Schema;
/**
* The primary type of resource in Cassandra.
*
* Used to represent a table or a keyspace or the root level "data" resource.
* "data" - the root level data resource.
* "data/keyspace_name" - keyspace-level data resource.
* "data/keyspace_name/table_name" - table-level data resource.
*/
public class DataResource implements IResource
{
enum Level
{
ROOT, KEYSPACE, TABLE
}
// permissions which may be granted on tables
private static final Set<Permission> TABLE_LEVEL_PERMISSIONS = Sets.immutableEnumSet(Permission.ALTER,
Permission.DROP,
Permission.SELECT,
Permission.MODIFY,
Permission.AUTHORIZE);
// permissions which may be granted on one or all keyspaces
private static final Set<Permission> KEYSPACE_LEVEL_PERMISSIONS = Sets.immutableEnumSet(Permission.CREATE,
Permission.ALTER,
Permission.DROP,
Permission.SELECT,
Permission.MODIFY,
Permission.AUTHORIZE);
private static final String ROOT_NAME = "data";
private static final DataResource ROOT_RESOURCE = new DataResource(Level.ROOT, null, null);
private final Level level;
private final String keyspace;
private final String table;
// memoized hashcode since DataRessource is immutable and used in hashmaps often
private final transient int hash;
private DataResource(Level level, String keyspace, String table)
{
this.level = level;
this.keyspace = keyspace;
this.table = table;
this.hash = Objects.hashCode(level, keyspace, table);
}
/**
* @return the root-level resource.
*/
public static DataResource root()
{
return ROOT_RESOURCE;
}
/**
* Creates a DataResource representing a keyspace.
*
* @param keyspace Name of the keyspace.
* @return DataResource instance representing the keyspace.
*/
public static DataResource keyspace(String keyspace)
{
return new DataResource(Level.KEYSPACE, keyspace, null);
}
/**
* Creates a DataResource instance representing a table.
*
* @param keyspace Name of the keyspace.
* @param table Name of the table.
* @return DataResource instance representing the column family.
*/
public static DataResource table(String keyspace, String table)
{
return new DataResource(Level.TABLE, keyspace, table);
}
/**
* Parses a data resource name into a DataResource instance.
*
* @param name Name of the data resource.
* @return DataResource instance matching the name.
*/
public static DataResource fromName(String name)
{
String[] parts = StringUtils.split(name, '/');
if (!parts[0].equals(ROOT_NAME) || parts.length > 3)
throw new IllegalArgumentException(String.format("%s is not a valid data resource name", name));
if (parts.length == 1)
return root();
if (parts.length == 2)
return keyspace(parts[1]);
return table(parts[1], parts[2]);
}
/**
* @return Printable name of the resource.
*/
public String getName()
{
switch (level)
{
case ROOT:
return ROOT_NAME;
case KEYSPACE:
return String.format("%s/%s", ROOT_NAME, keyspace);
case TABLE:
return String.format("%s/%s/%s", ROOT_NAME, keyspace, table);
}
throw new AssertionError();
}
/**
* @return Parent of the resource, if any. Throws IllegalStateException if it's the root-level resource.
*/
public IResource getParent()
{
switch (level)
{
case KEYSPACE:
return root();
case TABLE:
return keyspace(keyspace);
}
throw new IllegalStateException("Root-level resource can't have a parent");
}
public boolean isRootLevel()
{
return level == Level.ROOT;
}
public boolean isKeyspaceLevel()
{
return level == Level.KEYSPACE;
}
public boolean isTableLevel()
{
return level == Level.TABLE;
}
/**
* @return keyspace of the resource. Throws IllegalStateException if it's the root-level resource.
*/
public String getKeyspace()
{
if (isRootLevel())
throw new IllegalStateException("ROOT data resource has no keyspace");
return keyspace;
}
/**
* @return column family of the resource. Throws IllegalStateException if it's not a table-level resource.
*/
public String getTable()
{
if (!isTableLevel())
throw new IllegalStateException(String.format("%s data resource has no table", level));
return table;
}
/**
* @return Whether or not the resource has a parent in the hierarchy.
*/
public boolean hasParent()
{
return level != Level.ROOT;
}
/**
* @return Whether or not the resource exists in Cassandra.
*/
public boolean exists()
{
switch (level)
{
case ROOT:
return true;
case KEYSPACE:
return Schema.instance.getKeyspaces().contains(keyspace);
case TABLE:
return Schema.instance.getCFMetaData(keyspace, table) != null;
}
throw new AssertionError();
}
public Set<Permission> applicablePermissions()
{
switch (level)
{
case ROOT:
case KEYSPACE:
return KEYSPACE_LEVEL_PERMISSIONS;
case TABLE:
return TABLE_LEVEL_PERMISSIONS;
}
throw new AssertionError();
}
@Override
public String toString()
{
switch (level)
{
case ROOT:
return "<all keyspaces>";
case KEYSPACE:
return String.format("<keyspace %s>", keyspace);
case TABLE:
return String.format("<table %s.%s>", keyspace, table);
}
throw new AssertionError();
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof DataResource))
return false;
DataResource ds = (DataResource) o;
return Objects.equal(level, ds.level)
&& Objects.equal(keyspace, ds.keyspace)
&& Objects.equal(table, ds.table);
}
@Override
public int hashCode()
{
return hash;
}
}