/**
* 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.service;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.auth.AuthenticatedUser;
import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.config.Config.RequestSchedulerId;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.thrift.AuthenticationException;
import org.apache.cassandra.thrift.InvalidRequestException;
/**
* A container for per-client, thread-local state that Avro/Thrift threads must hold.
* TODO: Kill thrift exceptions
*/
public class ClientState
{
private static Logger logger = LoggerFactory.getLogger(ClientState.class);
// true if the keyspace should be used as the scheduling id
private final boolean SCHEDULE_ON_KEYSPACE = DatabaseDescriptor.getRequestSchedulerId().equals(RequestSchedulerId.keyspace);
// Current user for the session
private final ThreadLocal<AuthenticatedUser> user = new ThreadLocal<AuthenticatedUser>()
{
@Override
public AuthenticatedUser initialValue()
{
return DatabaseDescriptor.getAuthenticator().defaultUser();
}
};
// Keyspace and keyspace Permissions associated with the session
private final ThreadLocal<String> keyspace = new ThreadLocal<String>();
private final ThreadLocal<Set<Permission>> keyspaceAccess = new ThreadLocal<Set<Permission>>();
/**
* Called when the keyspace or user have changed.
*/
private void updateKeyspaceAccess()
{
if (user.get() == null || keyspace.get() == null)
// user is not logged in or keyspace is not set
keyspaceAccess.set(null);
else
// authorize the user for the current keyspace
keyspaceAccess.set(DatabaseDescriptor.getAuthority().authorize(user.get(), keyspace.get()));
}
public String getKeyspace()
{
return keyspace.get();
}
public void setKeyspace(String ks)
{
keyspace.set(ks);
updateKeyspaceAccess();
}
public String getSchedulingId()
{
if (SCHEDULE_ON_KEYSPACE)
return keyspace.get();
return "default";
}
/**
* Attempts to login this client with the given credentials map.
*/
public void login(Map<? extends CharSequence,? extends CharSequence> credentials) throws AuthenticationException
{
AuthenticatedUser user = DatabaseDescriptor.getAuthenticator().authenticate(credentials);
if (logger.isDebugEnabled())
logger.debug("logged in: {}", user);
this.user.set(user);
updateKeyspaceAccess();
}
public void logout()
{
if (logger.isDebugEnabled())
logger.debug("logged out: {}", user.get());
user.remove();
keyspace.remove();
keyspaceAccess.remove();
}
/**
* Confirms that the client thread has the given Permission in the context of the current Keyspace.
*/
public void hasKeyspaceAccess(Permission perm) throws InvalidRequestException
{
if (user.get() == null)
throw new InvalidRequestException("You have not logged in");
if (keyspaceAccess.get() == null)
throw new InvalidRequestException("You have not set a keyspace for this session");
if (!keyspaceAccess.get().contains(perm))
throw new InvalidRequestException(String.format("You (%s) do not have permission %s for %s", user, perm, keyspace.get()));
}
}