mirror of https://github.com/apache/cassandra
Update IAuthenticator to match the new IAuthorizer;
patch by Aleksey Yeschenko, reviewed by Jonathan Ellis for CASSANDRA-5003
This commit is contained in:
parent
fbf9f5533e
commit
bddfa9e120
|
|
@ -6,6 +6,7 @@
|
|||
* Fix preparing updates with collections (CASSANDRA-5017)
|
||||
* Don't generate UUID based on other node address (CASSANDRA-5002)
|
||||
* Fix message when trying to alter a clustering key type (CASSANDRA-5012)
|
||||
* Update IAuthenticator to match the new IAuthorizer (CASSANDRA-5003)
|
||||
Merged from 1.1
|
||||
* Improve schema propagation performance (CASSANDRA-5025)
|
||||
* Fix for IndexHelper.IndexFor throws OOB Exception (CASSANDRA-5030)
|
||||
|
|
|
|||
14
NEWS.txt
14
NEWS.txt
|
|
@ -14,6 +14,15 @@ by version X, but the inverse is not necessarily the case.)
|
|||
|
||||
Upgrading
|
||||
---------
|
||||
- IAuthenticator interface has been updated to support dynamic
|
||||
user creation, modification and removal. Users, even when stored
|
||||
externally, now have to be explicitly created using
|
||||
CREATE USER query first. AllowAllAuthenticator and SimpleAuthenticator
|
||||
have been updated for the new interface, but you'll have to update
|
||||
your old IAuthenticator implementations for 1.2. To ease this process,
|
||||
a new abstract LegacyAuthenticator class has been added - subclass it
|
||||
in your old IAuthenticator implementaion and everything should just work
|
||||
(this only affects users who implemented custom authenticators).
|
||||
- IAuthority interface has been deprecated in favor of IAuthorizer.
|
||||
AllowAllAuthority and SimpleAuthority have been renamed to
|
||||
AllowAllAuthorizer and SimpleAuthorizer, respectively. In order to
|
||||
|
|
@ -107,6 +116,11 @@ Features
|
|||
GRANT, REVOKE, LIST PERMISSIONS. A native implementation storing
|
||||
the permissions in Cassandra is being worked on and we expect to
|
||||
include it in 1.2.1 or 1.2.2.
|
||||
- IAuthenticator interface has been updated to support dynamic user
|
||||
creation, modification and removal via new CQL3 statements:
|
||||
CREATE USER, ALTER USER, DROP USER, LIST USERS. A native implementation
|
||||
that stores users in Cassandra itself is being worked on and is expected to
|
||||
become part of 1.2.1 or 1.2.2.
|
||||
|
||||
|
||||
1.1.5
|
||||
|
|
|
|||
|
|
@ -550,6 +550,8 @@ Table of Contents
|
|||
0x000A Protocol error: some client message triggered a protocol
|
||||
violation (for instance a QUERY message is sent before a STARTUP
|
||||
one has been sent)
|
||||
0x0100 Bad credentials: CREDENTIALS request failed because Cassandra
|
||||
did not accept the provided credentials.
|
||||
|
||||
0x1000 Unavailable exception. The rest of the ERROR message body will be
|
||||
<cl><required><alive>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@
|
|||
# This is a sample password file for SimpleAuthenticator. The format of
|
||||
# this file is username=password. If -Dpasswd.mode=MD5 then the password
|
||||
# is represented as an md5 digest, otherwise it is cleartext (keep this
|
||||
# in mind when setting file mode and ownership).
|
||||
# in mind when setting file mode and ownership). 'cassandra' is the default
|
||||
# superuser and can be removed later.
|
||||
cassandra=cassandra
|
||||
jsmith=havebadpass
|
||||
Elvis\ Presley=graceland4evar
|
||||
dilbert=nomoovertime
|
||||
|
|
|
|||
|
|
@ -29,23 +29,21 @@ import java.security.MessageDigest;
|
|||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.cassandra.exceptions.AuthenticationException;
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.io.util.FileUtils;
|
||||
import org.apache.cassandra.thrift.AuthenticationException;
|
||||
import org.apache.cassandra.utils.FBUtilities;
|
||||
import org.apache.cassandra.utils.Hex;
|
||||
|
||||
public class SimpleAuthenticator implements IAuthenticator
|
||||
public class SimpleAuthenticator extends LegacyAuthenticator
|
||||
{
|
||||
public final static String PASSWD_FILENAME_PROPERTY = "passwd.properties";
|
||||
public final static String PMODE_PROPERTY = "passwd.mode";
|
||||
public static final String USERNAME_KEY = "username";
|
||||
public static final String PASSWORD_KEY = "password";
|
||||
|
||||
public enum PasswordMode
|
||||
{
|
||||
PLAIN, MD5,
|
||||
};
|
||||
}
|
||||
|
||||
public AuthenticatedUser defaultUser()
|
||||
{
|
||||
|
|
@ -53,7 +51,7 @@ public class SimpleAuthenticator implements IAuthenticator
|
|||
return null;
|
||||
}
|
||||
|
||||
public AuthenticatedUser authenticate(Map<? extends CharSequence,? extends CharSequence> credentials) throws AuthenticationException
|
||||
public AuthenticatedUser authenticate(Map<String, String> credentials) throws AuthenticationException
|
||||
{
|
||||
String pmode_plain = System.getProperty(PMODE_PROPERTY);
|
||||
PasswordMode mode = PasswordMode.PLAIN;
|
||||
|
|
@ -78,19 +76,13 @@ public class SimpleAuthenticator implements IAuthenticator
|
|||
|
||||
String pfilename = System.getProperty(PASSWD_FILENAME_PROPERTY);
|
||||
|
||||
String username = null;
|
||||
CharSequence user = credentials.get(USERNAME_KEY);
|
||||
if (user == null)
|
||||
String username = credentials.get(USERNAME_KEY);
|
||||
if (username == null)
|
||||
throw new AuthenticationException("Authentication request was missing the required key '" + USERNAME_KEY + "'");
|
||||
else
|
||||
username = user.toString();
|
||||
|
||||
String password = null;
|
||||
CharSequence pass = credentials.get(PASSWORD_KEY);
|
||||
if (pass == null)
|
||||
String password = credentials.get(PASSWORD_KEY);
|
||||
if (password == null)
|
||||
throw new AuthenticationException("Authentication request was missing the required key '" + PASSWORD_KEY + "'");
|
||||
else
|
||||
password = pass.toString();
|
||||
|
||||
boolean authenticated = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -257,6 +257,7 @@ JUNK ::= /([ \t\r\f\v]+|(--|[/][/])[^\n\r]*([\n\r]|$)|[/][*].*?[*][/])/ ;
|
|||
| <selectStatement>
|
||||
| <dataChangeStatement>
|
||||
| <schemaChangeStatement>
|
||||
| <authenticationStatement>
|
||||
| <authorizationStatement>
|
||||
;
|
||||
|
||||
|
|
@ -277,10 +278,16 @@ JUNK ::= /([ \t\r\f\v]+|(--|[/][/])[^\n\r]*([\n\r]|$)|[/][*].*?[*][/])/ ;
|
|||
| <alterKeyspaceStatement>
|
||||
;
|
||||
|
||||
<authorizationStatement> ::= | <grantStatement>
|
||||
| <revokeStatement>
|
||||
| <listPermissionsStatement>
|
||||
;
|
||||
<authenticationStatement> ::= <createUserStatement>
|
||||
| <alterUserStatement>
|
||||
| <dropUserStatement>
|
||||
| <listUsersStatement>
|
||||
;
|
||||
|
||||
<authorizationStatement> ::= <grantStatement>
|
||||
| <revokeStatement>
|
||||
| <listPermissionsStatement>
|
||||
;
|
||||
|
||||
# timestamp is included here, since it's also a keyword
|
||||
<simpleStorageType> ::= typename=( <identifier> | <stringLiteral> | <K_TIMESTAMP> ) ;
|
||||
|
|
@ -1239,6 +1246,27 @@ syntax_rules += r'''
|
|||
;
|
||||
'''
|
||||
|
||||
syntax_rules += r'''
|
||||
<username> ::= user=( <identifier> | <stringLiteral> )
|
||||
;
|
||||
|
||||
<createUserStatement> ::= "CREATE" "USER" <username>
|
||||
( "WITH" "PASSWORD" <stringLiteral> )?
|
||||
( "SUPERUSER" | "NOSUPERUSER" )?
|
||||
;
|
||||
|
||||
<alterUserStatement> ::= "ALTER" "USER" <username>
|
||||
( "WITH" "PASSWORD" <stringLiteral> )?
|
||||
( "SUPERUSER" | "NOSUPERUSER" )?
|
||||
;
|
||||
|
||||
<dropUserStatement> ::= "DROP" "USER" <username>
|
||||
;
|
||||
|
||||
<listUsersStatement> ::= "LIST" "USERS"
|
||||
;
|
||||
'''
|
||||
|
||||
syntax_rules += r'''
|
||||
<grantStatement> ::= "GRANT" <permissionExpr> "ON" <resource> "TO" <username>
|
||||
;
|
||||
|
|
@ -1269,14 +1297,12 @@ syntax_rules += r'''
|
|||
| ( "KEYSPACE" <nonSystemKeyspaceName> )
|
||||
| ( "TABLE"? <columnFamilyName> )
|
||||
;
|
||||
|
||||
<username> ::= user=( <identifier> | <stringLiteral> )
|
||||
;
|
||||
'''
|
||||
|
||||
|
||||
@completer_for('username', 'user')
|
||||
def username_user_completer(ctxt, cass):
|
||||
# TODO: implement user autocompletion
|
||||
# TODO: implement user autocompletion for grant/revoke/list/drop user/alter user
|
||||
# with I could see a way to do this usefully, but I don't. I don't know
|
||||
# how any Authorities other than AllowAllAuthorizer work :/
|
||||
return [Hint('<username>')]
|
||||
|
|
|
|||
|
|
@ -17,31 +17,61 @@
|
|||
*/
|
||||
package org.apache.cassandra.auth;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cassandra.exceptions.AuthenticationException;
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.thrift.AuthenticationException;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
|
||||
public class AllowAllAuthenticator implements IAuthenticator
|
||||
{
|
||||
private final static AuthenticatedUser DEFAULT_USER = new AuthenticatedUser("nobody");
|
||||
|
||||
public AuthenticatedUser defaultUser()
|
||||
public boolean requireAuthentication()
|
||||
{
|
||||
return DEFAULT_USER;
|
||||
return false;
|
||||
}
|
||||
|
||||
public AuthenticatedUser authenticate(Map<? extends CharSequence,? extends CharSequence> credentials) throws AuthenticationException
|
||||
public Set<Option> supportedOptions()
|
||||
{
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
CharSequence username = credentials.get(IAuthenticator.USERNAME_KEY);
|
||||
if (username == null)
|
||||
return DEFAULT_USER;
|
||||
return new AuthenticatedUser((String)username);
|
||||
public Set<Option> alterableOptions()
|
||||
{
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public AuthenticatedUser authenticate(Map<String, String> credentials) throws AuthenticationException
|
||||
{
|
||||
return AuthenticatedUser.ANONYMOUS_USER;
|
||||
}
|
||||
|
||||
public void create(String username, Map<Option, Object> options) throws InvalidRequestException
|
||||
{
|
||||
throw new InvalidRequestException("CREATE USER operation is not supported by AllowAllAuthenticator");
|
||||
}
|
||||
|
||||
public void alter(String username, Map<Option, Object> options) throws InvalidRequestException
|
||||
{
|
||||
throw new InvalidRequestException("ALTER USER operation is not supported by AllowAllAuthenticator");
|
||||
}
|
||||
|
||||
public void drop(String username) throws InvalidRequestException
|
||||
{
|
||||
throw new InvalidRequestException("DROP USER operation is not supported by AllowAllAuthenticator");
|
||||
}
|
||||
|
||||
public Set<IResource> protectedResources()
|
||||
{
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public void validateConfiguration() throws ConfigurationException
|
||||
{
|
||||
// pass
|
||||
}
|
||||
|
||||
public void setup()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,14 +29,6 @@ public class AllowAllAuthorizer implements IAuthorizer
|
|||
return Permission.ALL;
|
||||
}
|
||||
|
||||
public void validateConfiguration()
|
||||
{
|
||||
}
|
||||
|
||||
public void setup()
|
||||
{
|
||||
}
|
||||
|
||||
public void grant(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String to)
|
||||
throws InvalidRequestException
|
||||
{
|
||||
|
|
@ -57,7 +49,7 @@ public class AllowAllAuthorizer implements IAuthorizer
|
|||
{
|
||||
}
|
||||
|
||||
public Set<PermissionDetails> listPermissions(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String of)
|
||||
public Set<PermissionDetails> list(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String of)
|
||||
throws InvalidRequestException
|
||||
{
|
||||
throw new InvalidRequestException("LIST PERMISSIONS operation is not supported by AllowAllAuthorizer");
|
||||
|
|
@ -67,4 +59,12 @@ public class AllowAllAuthorizer implements IAuthorizer
|
|||
{
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public void validateConfiguration()
|
||||
{
|
||||
}
|
||||
|
||||
public void setup()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* 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.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.cql3.UntypedResultSet;
|
||||
import org.apache.cassandra.cql3.QueryProcessor;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.service.MigrationManager;
|
||||
import org.apache.cassandra.service.StorageService;
|
||||
import org.apache.cassandra.utils.WrappedRunnable;
|
||||
|
||||
public class Auth
|
||||
{
|
||||
public static final String DEFAULT_SUPERUSER_NAME = "cassandra";
|
||||
|
||||
public static final String AUTH_KS = "system_auth";
|
||||
public static final String USERS_CF = "users";
|
||||
|
||||
/**
|
||||
* Checks if the username is stored in AUTH_KS.USERS_CF.
|
||||
*
|
||||
* @param username Username to query.
|
||||
* @return whether or not Cassandra knows about the user.
|
||||
*/
|
||||
public static boolean isExistingUser(String username)
|
||||
{
|
||||
String query = String.format("SELECT * FROM %s.%s WHERE name = '%s'", AUTH_KS, USERS_CF, escape(username));
|
||||
return !QueryProcessor.process(query).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user is a known superuser.
|
||||
*
|
||||
* @param username Username to query.
|
||||
* @return true is the user is a superuser, false if they aren't or don't exist at all.
|
||||
*/
|
||||
public static boolean isSuperuser(String username)
|
||||
{
|
||||
String query = String.format("SELECT super FROM %s.%s WHERE name = '%s'", AUTH_KS, USERS_CF, escape(username));
|
||||
UntypedResultSet result = QueryProcessor.process(query);
|
||||
return !result.isEmpty() && result.one().getBoolean("super");
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the user into AUTH_KS.USERS_CF (or overwrites their superuser status as a result of an ALTER USER query).
|
||||
*
|
||||
* @param username Username to insert.
|
||||
* @param isSuper User's new status.
|
||||
*/
|
||||
public static void insertUser(String username, boolean isSuper)
|
||||
{
|
||||
QueryProcessor.process(String.format("INSERT INTO %s.%s (name, super) VALUES ('%s', %s)",
|
||||
AUTH_KS,
|
||||
USERS_CF,
|
||||
escape(username),
|
||||
isSuper));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the user from AUTH_KS.USERS_CF.
|
||||
*
|
||||
* @param username Username to delete.
|
||||
*/
|
||||
public static void deleteUser(String username)
|
||||
{
|
||||
QueryProcessor.process(String.format("DELETE FROM %s.%s WHERE name = '%s'",
|
||||
AUTH_KS,
|
||||
USERS_CF,
|
||||
escape(username)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up Authenticator and Authorizer.
|
||||
*/
|
||||
public static void setup()
|
||||
{
|
||||
authenticator().setup();
|
||||
authorizer().setup();
|
||||
|
||||
// register a custom MigrationListener for permissions cleanup after dropped keyspaces/cfs.
|
||||
MigrationManager.instance.register(new MigrationListener());
|
||||
|
||||
// schedule seeding a superuser in RING_DELAY milliseconds.
|
||||
Runnable runnable = new WrappedRunnable()
|
||||
{
|
||||
public void runMayThrow() throws InvalidRequestException
|
||||
{
|
||||
// insert a default superuser if AUTH_KS.USERS_CF is empty.
|
||||
if (QueryProcessor.process(String.format("SELECT * FROM %s.%s", AUTH_KS, USERS_CF)).isEmpty())
|
||||
insertUser(DEFAULT_SUPERUSER_NAME, true);
|
||||
}
|
||||
};
|
||||
StorageService.tasks.schedule(runnable, StorageService.RING_DELAY, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
// we only worry about one character ('). Make sure it's properly escaped.
|
||||
private static String escape(String name)
|
||||
{
|
||||
return StringUtils.replace(name, "'", "''");
|
||||
}
|
||||
|
||||
private static IAuthenticator authenticator()
|
||||
{
|
||||
return DatabaseDescriptor.getAuthenticator();
|
||||
}
|
||||
|
||||
private static IAuthorizer authorizer()
|
||||
{
|
||||
return DatabaseDescriptor.getAuthorizer();
|
||||
}
|
||||
}
|
||||
|
|
@ -17,30 +17,48 @@
|
|||
*/
|
||||
package org.apache.cassandra.auth;
|
||||
|
||||
/**
|
||||
* Returned from IAuthenticator#authenticate(), represents an authenticated user everywhere internally.
|
||||
*/
|
||||
public class AuthenticatedUser
|
||||
{
|
||||
public final String username;
|
||||
private final boolean isSuperUser;
|
||||
public static final String ANONYMOUS_USERNAME = "anonymous";
|
||||
public static final AuthenticatedUser ANONYMOUS_USER = new AuthenticatedUser(ANONYMOUS_USERNAME);
|
||||
|
||||
public AuthenticatedUser(String username)
|
||||
private final String name;
|
||||
|
||||
public AuthenticatedUser(String name)
|
||||
{
|
||||
this(username, false);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public AuthenticatedUser(String username, boolean isSuperUser)
|
||||
public String getName()
|
||||
{
|
||||
this.username = username;
|
||||
this.isSuperUser = isSuperUser;
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isSuperUser()
|
||||
/**
|
||||
* Checks the user's superuser status.
|
||||
* Only a superuser is allowed to perform CREATE USER and DROP USER queries.
|
||||
* Im most cased, though not necessarily, a superuser will have Permission.ALL on every resource
|
||||
* (depends on IAuthorizer implementation).
|
||||
*/
|
||||
public boolean isSuper()
|
||||
{
|
||||
return isSuperUser;
|
||||
return !isAnonymous() && Auth.isSuperuser(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* If IAuthenticator doesn't require authentication, this method may return true.
|
||||
*/
|
||||
public boolean isAnonymous()
|
||||
{
|
||||
return this == ANONYMOUS_USER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("#<User %s super=%s>", username, isSuperUser);
|
||||
return String.format("#<User %s>", name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,25 +18,103 @@
|
|||
package org.apache.cassandra.auth;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cassandra.exceptions.AuthenticationException;
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.thrift.AuthenticationException;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
|
||||
public interface IAuthenticator
|
||||
{
|
||||
public static final String USERNAME_KEY = "username";
|
||||
public static final String PASSWORD_KEY = "password";
|
||||
static final String USERNAME_KEY = "username";
|
||||
static final String PASSWORD_KEY = "password";
|
||||
|
||||
/**
|
||||
* @return The user that a connection is initialized with, or 'null' if a user must call login().
|
||||
* Supported CREATE USER/ALTER USER options.
|
||||
* Currently only PASSWORD is available.
|
||||
*/
|
||||
public AuthenticatedUser defaultUser();
|
||||
enum Option
|
||||
{
|
||||
PASSWORD
|
||||
}
|
||||
|
||||
/**
|
||||
* @param credentials An implementation specific collection of identifying information.
|
||||
* @return A successfully authenticated user: should throw AuthenticationException rather than ever returning null.
|
||||
* Whether or not the authenticator requires explicit login.
|
||||
* If false will instantiate user with AuthenticatedUser.ANONYMOUS_USER.
|
||||
*/
|
||||
public AuthenticatedUser authenticate(Map<? extends CharSequence,? extends CharSequence> credentials) throws AuthenticationException;
|
||||
boolean requireAuthentication();
|
||||
|
||||
public void validateConfiguration() throws ConfigurationException;
|
||||
/**
|
||||
* Set of options supported by CREATE USER and ALTER USER queries.
|
||||
* Should never return null - always return an empty set instead.
|
||||
*/
|
||||
Set<Option> supportedOptions();
|
||||
|
||||
/**
|
||||
* Subset of supportedOptions that users are allowed to alter when performing ALTER USER [themselves].
|
||||
* Should never return null - always return an empty set instead.
|
||||
*/
|
||||
Set<Option> alterableOptions();
|
||||
|
||||
/**
|
||||
* Authenticates a user given a Map<String, String> of credentials.
|
||||
* Should never return null - always throw AuthenticationException instead.
|
||||
* Returning AuthenticatedUser.ANONYMOUS_USER is an option as well if authentication is not required.
|
||||
*
|
||||
* @throws AuthenticationException if credentials don't match any known user.
|
||||
*/
|
||||
AuthenticatedUser authenticate(Map<String, String> credentials) throws AuthenticationException;
|
||||
|
||||
/**
|
||||
* Called during execution of CREATE USER query (also may be called on startup, see seedSuperuserOptions method).
|
||||
* If authenticator is static then the body of the method should be left blank, but don't throw an exception.
|
||||
* options are guaranteed to be a subset of supportedOptions().
|
||||
*
|
||||
* @param username Username of the user to create.
|
||||
* @param options Options the user will be created with.
|
||||
* @throws InvalidRequestException
|
||||
*/
|
||||
void create(String username, Map<Option, Object> options) throws InvalidRequestException;
|
||||
|
||||
/**
|
||||
* Called during execution of ALTER USER query.
|
||||
* options are always guaranteed to be a subset of supportedOptions(). Furthermore, if the user performing the query
|
||||
* is not a superuser and is altering himself, then options are guaranteed to be a subset of alterableOptions().
|
||||
* Keep the body of the method blank if your implementation doesn't support any options.
|
||||
*
|
||||
* @param username Username of the user that will be altered.
|
||||
* @param options Options to alter.
|
||||
* @throws InvalidRequestException
|
||||
*/
|
||||
void alter(String username, Map<Option, Object> options) throws InvalidRequestException;
|
||||
|
||||
|
||||
/**
|
||||
* Called during execution of DROP USER query.
|
||||
*
|
||||
* @param username Username of the user that will be dropped.
|
||||
* @throws InvalidRequestException
|
||||
*/
|
||||
void drop(String username) throws InvalidRequestException;
|
||||
|
||||
/**
|
||||
* Set of resources that should be made inaccessible to users and only accessible internally.
|
||||
*
|
||||
* @return Keyspaces, column families that will be unreadable and unmodifiable by users; other resources.
|
||||
*/
|
||||
Set<? extends IResource> protectedResources();
|
||||
|
||||
/**
|
||||
* Validates configuration of IAuthenticator implementation (if configurable).
|
||||
*
|
||||
* @throws ConfigurationException when there is a configuration error.
|
||||
*/
|
||||
void validateConfiguration() throws ConfigurationException;
|
||||
|
||||
/**
|
||||
* Setup is called once upon system startup to initialize the IAuthenticator.
|
||||
*
|
||||
* For example, use this method to create any required keyspaces/column families.
|
||||
*/
|
||||
void setup();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public interface IAuthorizer
|
|||
* @param resource Resource for which the authorization is being requested. @see DataResource.
|
||||
* @return Set of permissions of the user on the resource. Should never return null. Use Permission.NONE instead.
|
||||
*/
|
||||
public Set<Permission> authorize(AuthenticatedUser user, IResource resource);
|
||||
Set<Permission> authorize(AuthenticatedUser user, IResource resource);
|
||||
|
||||
/**
|
||||
* Grants a set of permissions on a resource to a user.
|
||||
|
|
@ -49,7 +49,7 @@ public interface IAuthorizer
|
|||
* @throws UnauthorizedException if the granting user isn't allowed to grant (and revoke) the permissions on the resource.
|
||||
* @throws InvalidRequestException upon parameter misconfiguration or internal error.
|
||||
*/
|
||||
public void grant(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String to)
|
||||
void grant(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String to)
|
||||
throws UnauthorizedException, InvalidRequestException;
|
||||
|
||||
/**
|
||||
|
|
@ -64,7 +64,7 @@ public interface IAuthorizer
|
|||
* @throws UnauthorizedException if the revoking user isn't allowed to revoke the permissions on the resource.
|
||||
* @throws InvalidRequestException upon parameter misconfiguration or internal error.
|
||||
*/
|
||||
public void revoke(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String from)
|
||||
void revoke(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String from)
|
||||
throws UnauthorizedException, InvalidRequestException;
|
||||
|
||||
/**
|
||||
|
|
@ -81,7 +81,7 @@ public interface IAuthorizer
|
|||
* @throws UnauthorizedException if the user isn't allowed to view the requested permissions.
|
||||
* @throws InvalidRequestException upon parameter misconfiguration or internal error.
|
||||
*/
|
||||
public Set<PermissionDetails> listPermissions(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String of)
|
||||
Set<PermissionDetails> list(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String of)
|
||||
throws UnauthorizedException, InvalidRequestException;
|
||||
|
||||
/**
|
||||
|
|
@ -90,33 +90,33 @@ public interface IAuthorizer
|
|||
*
|
||||
* @param droppedUser The user to revoke all permissions from.
|
||||
*/
|
||||
public void revokeAll(String droppedUser);
|
||||
void revokeAll(String droppedUser);
|
||||
|
||||
/**
|
||||
* This method is called after a resource is removed (i.e. keyspace or a table is dropped).
|
||||
*
|
||||
* @param droppedResource The resource to revoke all permissions on.
|
||||
*/
|
||||
public void revokeAll(IResource droppedResource);
|
||||
void revokeAll(IResource droppedResource);
|
||||
|
||||
/**
|
||||
* Set of resources that should be made inaccessible to users and only accessible internally.
|
||||
*
|
||||
* @return Keyspaces, column families that will be unreadable and unmodifiable by users; other resources.
|
||||
*/
|
||||
public Set<? extends IResource> protectedResources();
|
||||
Set<? extends IResource> protectedResources();
|
||||
|
||||
/**
|
||||
* Validates configuration of IAuthorizer implementation (if configurable).
|
||||
*
|
||||
* @throws ConfigurationException when there is a configuration error.
|
||||
*/
|
||||
public void validateConfiguration() throws ConfigurationException;
|
||||
void validateConfiguration() throws ConfigurationException;
|
||||
|
||||
/**
|
||||
* Setup is called once upon system startup to initialize the IAuthorizer.
|
||||
*
|
||||
* For example, use this method to create any required keyspaces/column families.
|
||||
*/
|
||||
public void setup();
|
||||
void setup();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,14 +29,14 @@ public interface IResource
|
|||
/**
|
||||
* @return printable name of the resource.
|
||||
*/
|
||||
public String getName();
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Gets next resource in the hierarchy. Call hasParent first to make sure there is one.
|
||||
*
|
||||
* @return Resource parent (or IllegalStateException if there is none). Never a null.
|
||||
*/
|
||||
public IResource getParent();
|
||||
IResource getParent();
|
||||
|
||||
/**
|
||||
* Indicates whether or not this resource has a parent in the hierarchy.
|
||||
|
|
@ -44,10 +44,10 @@ public interface IResource
|
|||
* Please perform this check before calling getParent() method.
|
||||
* @return Whether or not the resource has a parent.
|
||||
*/
|
||||
public boolean hasParent();
|
||||
boolean hasParent();
|
||||
|
||||
/**
|
||||
* @return Whether or not this resource exists in Cassandra.
|
||||
*/
|
||||
public boolean exists();
|
||||
boolean exists();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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.*;
|
||||
|
||||
import org.apache.cassandra.exceptions.AuthenticationException;
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
|
||||
/**
|
||||
* Provides a transitional IAuthenticator implementation for old-style (pre-1.2) authenticators.
|
||||
*
|
||||
* Comes with default implementation for the all of the new methods.
|
||||
* Subclass LegacyAuthenticator instead of implementing the old IAuthenticator and your old IAuthenticator
|
||||
* implementation should continue to work.
|
||||
*/
|
||||
public abstract class LegacyAuthenticator implements IAuthenticator
|
||||
{
|
||||
/**
|
||||
* @return The user that a connection is initialized with, or 'null' if a user must call login().
|
||||
*/
|
||||
public abstract AuthenticatedUser defaultUser();
|
||||
|
||||
/**
|
||||
* @param credentials An implementation specific collection of identifying information.
|
||||
* @return A successfully authenticated user: should throw AuthenticationException rather than ever returning null.
|
||||
*/
|
||||
public abstract AuthenticatedUser authenticate(Map<String, String> credentials) throws AuthenticationException;
|
||||
|
||||
public abstract void validateConfiguration() throws ConfigurationException;
|
||||
|
||||
@Override
|
||||
public boolean requireAuthentication()
|
||||
{
|
||||
return defaultUser() == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Option> supportedOptions()
|
||||
{
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Option> alterableOptions()
|
||||
{
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(String username, Map<Option, Object> options) throws InvalidRequestException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void alter(String username, Map<Option, Object> options) throws InvalidRequestException
|
||||
{
|
||||
throw new InvalidRequestException("ALTER USER operation is not supported by LegacyAuthenticator");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(String username) throws InvalidRequestException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<IResource> protectedResources()
|
||||
{
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -19,13 +19,14 @@ package org.apache.cassandra.auth;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||
|
||||
/**
|
||||
* Provides a transitional IAuthorizer implementation for old-style (pre-1.2) authorizers.
|
||||
*
|
||||
* Translates old-style authorze() calls to the new-style, expands Permission.READ and Permission.WRITE
|
||||
* Translates old-style authorize() calls to the new-style, expands Permission.READ and Permission.WRITE
|
||||
* into the new Permission values, translates the new resource hierarchy into the old hierarchy.
|
||||
* Stubs the rest of the new methods.
|
||||
* Subclass LegacyAuthorizer instead of implementing the old IAuthority and your old IAuthority implementation should
|
||||
|
|
@ -40,10 +41,7 @@ public abstract class LegacyAuthorizer implements IAuthorizer
|
|||
*/
|
||||
public abstract EnumSet<Permission> authorize(AuthenticatedUser user, List<Object> resource);
|
||||
|
||||
@Override
|
||||
public void setup()
|
||||
{
|
||||
}
|
||||
public abstract void validateConfiguration() throws ConfigurationException;
|
||||
|
||||
/**
|
||||
* Translates new-style authorize() method call to the old-style (including permissions and the hierarchy).
|
||||
|
|
@ -97,7 +95,7 @@ public abstract class LegacyAuthorizer implements IAuthorizer
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<PermissionDetails> listPermissions(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String of)
|
||||
public Set<PermissionDetails> list(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String of)
|
||||
throws InvalidRequestException, UnauthorizedException
|
||||
{
|
||||
throw new InvalidRequestException("LIST PERMISSIONS operation is not supported by LegacyAuthorizer");
|
||||
|
|
@ -108,4 +106,9 @@ public abstract class LegacyAuthorizer implements IAuthorizer
|
|||
{
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import org.apache.commons.lang.builder.ToStringBuilder;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.apache.cassandra.auth.Auth;
|
||||
import org.apache.cassandra.cql3.CFDefinition;
|
||||
import org.apache.cassandra.cql3.QueryProcessor;
|
||||
import org.apache.cassandra.cql3.UntypedResultSet;
|
||||
|
|
@ -218,6 +219,11 @@ public final class CFMetaData
|
|||
+ "requested_at timestamp"
|
||||
+ ") WITH COMMENT='ranges requested for transfer here'");
|
||||
|
||||
public static final CFMetaData AuthUsersCf = compile(18, "CREATE TABLE " + Auth.USERS_CF + " ("
|
||||
+ "name text PRIMARY KEY,"
|
||||
+ "super boolean"
|
||||
+ ");", Auth.AUTH_KS);
|
||||
|
||||
public enum Caching
|
||||
{
|
||||
ALL, KEYS_ONLY, ROWS_ONLY, NONE;
|
||||
|
|
|
|||
|
|
@ -458,7 +458,9 @@ public class DatabaseDescriptor
|
|||
}
|
||||
|
||||
// Hardcoded system tables
|
||||
List<KSMetaData> systemKeyspaces = Arrays.asList(KSMetaData.systemKeyspace(), KSMetaData.traceKeyspace());
|
||||
List<KSMetaData> systemKeyspaces = Arrays.asList(KSMetaData.systemKeyspace(),
|
||||
KSMetaData.traceKeyspace(),
|
||||
KSMetaData.authKeyspace());
|
||||
assert systemKeyspaces.size() == Schema.systemKeyspaceNames.size();
|
||||
for (KSMetaData ksmd : systemKeyspaces)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableMap;
|
|||
import org.apache.commons.lang.ObjectUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import org.apache.cassandra.auth.Auth;
|
||||
import org.apache.cassandra.cql3.QueryProcessor;
|
||||
import org.apache.cassandra.cql3.UntypedResultSet;
|
||||
import org.apache.cassandra.db.*;
|
||||
|
|
@ -101,6 +102,12 @@ public final class KSMetaData
|
|||
return new KSMetaData(Tracing.TRACE_KS, SimpleStrategy.class, ImmutableMap.of("replication_factor", "1"), true, cfDefs);
|
||||
}
|
||||
|
||||
public static KSMetaData authKeyspace()
|
||||
{
|
||||
List<CFMetaData> cfDefs = Arrays.asList(CFMetaData.AuthUsersCf);
|
||||
return new KSMetaData(Auth.AUTH_KS, SimpleStrategy.class, ImmutableMap.of("replication_factor", "1"), true, cfDefs);
|
||||
}
|
||||
|
||||
public static KSMetaData testMetadata(String name, Class<? extends AbstractReplicationStrategy> strategyClass, Map<String, String> strategyOptions, CFMetaData... cfDefs)
|
||||
{
|
||||
return new KSMetaData(name, strategyClass, strategyOptions, true, Arrays.asList(cfDefs));
|
||||
|
|
|
|||
|
|
@ -24,16 +24,17 @@ import java.security.NoSuchAlgorithmException;
|
|||
import java.util.*;
|
||||
|
||||
import com.google.common.collect.*;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.apache.cassandra.auth.Auth;
|
||||
import org.apache.cassandra.db.*;
|
||||
import org.apache.cassandra.db.Table;
|
||||
import org.apache.cassandra.db.marshal.AbstractType;
|
||||
import org.apache.cassandra.io.sstable.Descriptor;
|
||||
import org.apache.cassandra.service.MigrationManager;
|
||||
import org.apache.cassandra.tracing.Tracing;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
import org.apache.cassandra.utils.Pair;
|
||||
import org.cliffc.high_scale_lib.NonBlockingHashMap;
|
||||
|
||||
|
|
@ -66,7 +67,7 @@ public class Schema
|
|||
|
||||
// 59adb24e-f3cd-3e02-97f0-5b395827453f
|
||||
public static final UUID emptyVersion;
|
||||
public static final ImmutableSet<String> systemKeyspaceNames = ImmutableSet.of(Table.SYSTEM_KS, Tracing.TRACE_KS);
|
||||
public static final ImmutableSet<String> systemKeyspaceNames = ImmutableSet.of(Table.SYSTEM_KS, Tracing.TRACE_KS, Auth.AUTH_KS);
|
||||
|
||||
static
|
||||
{
|
||||
|
|
|
|||
|
|
@ -169,10 +169,14 @@ cqlStatement returns [ParsedStatement stmt]
|
|||
| st12=dropColumnFamilyStatement { $stmt = st12; }
|
||||
| st13=dropIndexStatement { $stmt = st13; }
|
||||
| st14=alterTableStatement { $stmt = st14; }
|
||||
| st15=grantStatement { $stmt = st15; }
|
||||
| st16=revokeStatement { $stmt = st16; }
|
||||
| st17=listPermissionsStatement { $stmt = st17; }
|
||||
| st18=alterKeyspaceStatement { $stmt = st18; }
|
||||
| st15=alterKeyspaceStatement { $stmt = st15; }
|
||||
| st16=grantStatement { $stmt = st16; }
|
||||
| st17=revokeStatement { $stmt = st17; }
|
||||
| st18=listPermissionsStatement { $stmt = st18; }
|
||||
| st19=createUserStatement { $stmt = st19; }
|
||||
| st20=alterUserStatement { $stmt = st20; }
|
||||
| st21=dropUserStatement { $stmt = st21; }
|
||||
| st22=listUsersStatement { $stmt = st22; }
|
||||
;
|
||||
|
||||
/*
|
||||
|
|
@ -545,11 +549,6 @@ permissionOrAll returns [Set<Permission> perms]
|
|||
| p=permission ( K_PERMISSION )? { $perms = EnumSet.of($p.perm); }
|
||||
;
|
||||
|
||||
username
|
||||
: IDENT
|
||||
| STRING_LITERAL
|
||||
;
|
||||
|
||||
resource returns [IResource res]
|
||||
: r=dataResource { $res = $r.res; }
|
||||
;
|
||||
|
|
@ -561,6 +560,56 @@ dataResource returns [DataResource res]
|
|||
{ $res = DataResource.columnFamily($cf.name.getKeyspace(), $cf.name.getColumnFamily()); }
|
||||
;
|
||||
|
||||
/**
|
||||
* CREATE USER <username> [WITH PASSWORD <password>] [SUPERUSER|NOSUPERUSER]
|
||||
*/
|
||||
createUserStatement returns [CreateUserStatement stmt]
|
||||
@init {
|
||||
UserOptions opts = new UserOptions();
|
||||
boolean superuser = false;
|
||||
}
|
||||
: K_CREATE K_USER username
|
||||
( K_WITH userOptions[opts] )?
|
||||
( K_SUPERUSER { superuser = true; } | K_NOSUPERUSER { superuser = false; } )?
|
||||
{ $stmt = new CreateUserStatement($username.text, opts, superuser); }
|
||||
;
|
||||
|
||||
/**
|
||||
* ALTER USER <username> [WITH PASSWORD <password>] [SUPERUSER|NOSUPERUSER]
|
||||
*/
|
||||
alterUserStatement returns [AlterUserStatement stmt]
|
||||
@init {
|
||||
UserOptions opts = new UserOptions();
|
||||
Boolean superuser = null;
|
||||
}
|
||||
: K_ALTER K_USER username
|
||||
( K_WITH userOptions[opts] )?
|
||||
( K_SUPERUSER { superuser = true; } | K_NOSUPERUSER { superuser = false; } )?
|
||||
{ $stmt = new AlterUserStatement($username.text, opts, superuser); }
|
||||
;
|
||||
|
||||
/**
|
||||
* DROP USER <username>
|
||||
*/
|
||||
dropUserStatement returns [DropUserStatement stmt]
|
||||
: K_DROP K_USER username { $stmt = new DropUserStatement($username.text); }
|
||||
;
|
||||
|
||||
/**
|
||||
* LIST USERS
|
||||
*/
|
||||
listUsersStatement returns [ListUsersStatement stmt]
|
||||
: K_LIST K_USERS { $stmt = new ListUsersStatement(); }
|
||||
;
|
||||
|
||||
userOptions[UserOptions opts]
|
||||
: userOption[opts]
|
||||
;
|
||||
|
||||
userOption[UserOptions opts]
|
||||
: k=K_PASSWORD v=STRING_LITERAL { opts.put($k.text, $v.text); }
|
||||
;
|
||||
|
||||
/** DEFINITIONS **/
|
||||
|
||||
// Column Identifiers
|
||||
|
|
@ -767,6 +816,11 @@ collection_type returns [ParsedType pt]
|
|||
{ try { $pt = ParsedType.Collection.set(t); } catch (InvalidRequestException e) { addRecognitionError(e.getMessage()); } }
|
||||
;
|
||||
|
||||
username
|
||||
: IDENT
|
||||
| STRING_LITERAL
|
||||
;
|
||||
|
||||
unreserved_keyword returns [String str]
|
||||
: k=( K_KEY
|
||||
| K_CLUSTERING
|
||||
|
|
@ -784,6 +838,11 @@ unreserved_keyword returns [String str]
|
|||
| K_PERMISSIONS
|
||||
| K_KEYSPACES
|
||||
| K_ALL
|
||||
| K_USER
|
||||
| K_USERS
|
||||
| K_SUPERUSER
|
||||
| K_NOSUPERUSER
|
||||
| K_PASSWORD
|
||||
) { $str = $k.text; }
|
||||
| t=native_type { $str = t.toString(); }
|
||||
;
|
||||
|
|
@ -835,18 +894,25 @@ K_ORDER: O R D E R;
|
|||
K_BY: B Y;
|
||||
K_ASC: A S C;
|
||||
K_DESC: D E S C;
|
||||
K_ALLOW: A L L O W;
|
||||
K_FILTERING: F I L T E R I N G;
|
||||
|
||||
K_GRANT: G R A N T;
|
||||
K_ALL: A L L;
|
||||
K_PERMISSION: P E R M I S S I O N;
|
||||
K_PERMISSIONS: P E R M I S S I O N S;
|
||||
K_OF: O F;
|
||||
K_REVOKE: R E V O K E;
|
||||
K_ALLOW: A L L O W;
|
||||
K_FILTERING: F I L T E R I N G;
|
||||
K_MODIFY: M O D I F Y;
|
||||
K_AUTHORIZE: A U T H O R I Z E;
|
||||
K_NORECURSIVE: N O R E C U R S I V E;
|
||||
|
||||
K_USER: U S E R;
|
||||
K_USERS: U S E R S;
|
||||
K_SUPERUSER: S U P E R U S E R;
|
||||
K_NOSUPERUSER: N O S U P E R U S E R;
|
||||
K_PASSWORD: P A S S W O R D;
|
||||
|
||||
K_CLUSTERING: C L U S T E R I N G;
|
||||
K_ASCII: A S C I I;
|
||||
K_BIGINT: B I G I N T;
|
||||
|
|
|
|||
|
|
@ -140,6 +140,27 @@ public class QueryProcessor
|
|||
return processStatement(getStatement(queryString, queryState.getClientState()).statement, cl, queryState, Collections.<ByteBuffer>emptyList());
|
||||
}
|
||||
|
||||
public static UntypedResultSet process(String query)
|
||||
{
|
||||
try
|
||||
{
|
||||
QueryState state = new QueryState(new ClientState(true));
|
||||
ResultMessage result = process(query, ConsistencyLevel.ONE, state);
|
||||
if (result instanceof ResultMessage.Rows)
|
||||
return new UntypedResultSet(((ResultMessage.Rows)result).result);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
catch (RequestExecutionException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (RequestValidationException e)
|
||||
{
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static UntypedResultSet processInternal(String query)
|
||||
{
|
||||
try
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.cql3;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cassandra.auth.IAuthenticator;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.utils.FBUtilities;
|
||||
|
||||
public class UserOptions
|
||||
{
|
||||
private final Map<IAuthenticator.Option, Object> options = new HashMap<IAuthenticator.Option, Object>();
|
||||
|
||||
public void put(String name, Object value)
|
||||
{
|
||||
options.put(IAuthenticator.Option.valueOf(name.toUpperCase()), value);
|
||||
}
|
||||
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return options.isEmpty();
|
||||
}
|
||||
|
||||
public Map<IAuthenticator.Option, Object> getOptions()
|
||||
{
|
||||
return options;
|
||||
}
|
||||
|
||||
public void validate() throws InvalidRequestException
|
||||
{
|
||||
for (IAuthenticator.Option option : options.keySet())
|
||||
{
|
||||
if (!DatabaseDescriptor.getAuthenticator().supportedOptions().contains(option))
|
||||
throw new InvalidRequestException(String.format("%s doesn't support %s option",
|
||||
DatabaseDescriptor.getAuthenticator().getClass().getName(),
|
||||
option));
|
||||
}
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return FBUtilities.toString(options);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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.cql3.statements;
|
||||
|
||||
import org.apache.cassandra.auth.Auth;
|
||||
import org.apache.cassandra.auth.AuthenticatedUser;
|
||||
import org.apache.cassandra.auth.IAuthenticator;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.cql3.UserOptions;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
import org.apache.cassandra.transport.messages.ResultMessage;
|
||||
|
||||
public class AlterUserStatement extends AuthenticationStatement
|
||||
{
|
||||
private final String username;
|
||||
private final UserOptions opts;
|
||||
private final Boolean superuser;
|
||||
|
||||
public AlterUserStatement(String username, UserOptions opts, Boolean superuser)
|
||||
{
|
||||
this.username = username;
|
||||
this.opts = opts;
|
||||
this.superuser = superuser;
|
||||
}
|
||||
|
||||
public void validate(ClientState state) throws InvalidRequestException
|
||||
{
|
||||
opts.validate();
|
||||
|
||||
if (superuser == null && opts.isEmpty())
|
||||
throw new InvalidRequestException("ALTER USER can't be empty");
|
||||
|
||||
if (!Auth.isExistingUser(username))
|
||||
throw new InvalidRequestException(String.format("User %s doesn't exist", username));
|
||||
}
|
||||
|
||||
public void checkAccess(ClientState state) throws UnauthorizedException
|
||||
{
|
||||
state.validateLogin();
|
||||
AuthenticatedUser user = state.getUser();
|
||||
|
||||
if (superuser != null && user.getName().equals(username))
|
||||
throw new UnauthorizedException("You aren't allowed to alter your own superuser status");
|
||||
|
||||
if (superuser != null && !user.isSuper())
|
||||
throw new UnauthorizedException("Only superusers are allowed to alter superuser status");
|
||||
|
||||
if (!user.isSuper() && !user.getName().equals(username))
|
||||
throw new UnauthorizedException("You aren't allowed to alter this user");
|
||||
|
||||
if (!user.isSuper())
|
||||
{
|
||||
for (IAuthenticator.Option option : opts.getOptions().keySet())
|
||||
{
|
||||
if (!DatabaseDescriptor.getAuthenticator().alterableOptions().contains(option))
|
||||
throw new UnauthorizedException(String.format("You aren't allowed to alter %s option", option));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ResultMessage execute(ClientState state) throws InvalidRequestException
|
||||
{
|
||||
if (!opts.isEmpty())
|
||||
DatabaseDescriptor.getAuthenticator().alter(username, opts.getOptions());
|
||||
if (superuser != null)
|
||||
Auth.insertUser(username, superuser.booleanValue());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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.cql3.statements;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cassandra.cql3.CQLStatement;
|
||||
import org.apache.cassandra.db.ConsistencyLevel;
|
||||
import org.apache.cassandra.exceptions.*;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
import org.apache.cassandra.service.QueryState;
|
||||
import org.apache.cassandra.transport.messages.ResultMessage;
|
||||
|
||||
public abstract class AuthenticationStatement extends ParsedStatement implements CQLStatement
|
||||
{
|
||||
@Override
|
||||
public Prepared prepare()
|
||||
{
|
||||
return new Prepared(this);
|
||||
}
|
||||
|
||||
public int getBoundsTerms()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public ResultMessage execute(ConsistencyLevel cl, QueryState state, List<ByteBuffer> variables)
|
||||
throws RequestExecutionException, RequestValidationException
|
||||
{
|
||||
return execute(state.getClientState());
|
||||
}
|
||||
|
||||
public abstract ResultMessage execute(ClientState state) throws RequestExecutionException, RequestValidationException;
|
||||
|
||||
public ResultMessage executeInternal(QueryState state)
|
||||
{
|
||||
// executeInternal is for local query only, thus altering users doesn't make sense and is not supported
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -41,15 +41,13 @@ public abstract class AuthorizationStatement extends ParsedStatement implements
|
|||
return 0;
|
||||
}
|
||||
|
||||
public void checkAccess(ClientState state) throws InvalidRequestException, UnauthorizedException
|
||||
{}
|
||||
|
||||
public ResultMessage execute(ConsistencyLevel cl, QueryState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException
|
||||
public ResultMessage execute(ConsistencyLevel cl, QueryState state, List<ByteBuffer> variables)
|
||||
throws RequestValidationException, RequestExecutionException
|
||||
{
|
||||
return execute(state.getClientState(), variables);
|
||||
return execute(state.getClientState());
|
||||
}
|
||||
|
||||
public abstract ResultMessage execute(ClientState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException;
|
||||
public abstract ResultMessage execute(ClientState state) throws RequestValidationException, RequestExecutionException;
|
||||
|
||||
public ResultMessage executeInternal(QueryState state)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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.cql3.statements;
|
||||
|
||||
import org.apache.cassandra.auth.Auth;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.cql3.UserOptions;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
import org.apache.cassandra.transport.messages.ResultMessage;
|
||||
|
||||
public class CreateUserStatement extends AuthenticationStatement
|
||||
{
|
||||
private final String username;
|
||||
private final UserOptions opts;
|
||||
private final boolean superuser;
|
||||
|
||||
public CreateUserStatement(String username, UserOptions opts, boolean superuser)
|
||||
{
|
||||
this.username = username;
|
||||
this.opts = opts;
|
||||
this.superuser = superuser;
|
||||
}
|
||||
|
||||
public void validate(ClientState state) throws InvalidRequestException
|
||||
{
|
||||
if (username.isEmpty())
|
||||
throw new InvalidRequestException("Username can't be an empty string");
|
||||
opts.validate();
|
||||
if (Auth.isExistingUser(username))
|
||||
throw new InvalidRequestException(String.format("User %s already exists", username));
|
||||
}
|
||||
|
||||
public void checkAccess(ClientState state) throws UnauthorizedException
|
||||
{
|
||||
state.validateLogin();
|
||||
|
||||
if (!state.getUser().isSuper())
|
||||
throw new UnauthorizedException("Only superusers are allowed to perfrom CREATE USER queries");
|
||||
}
|
||||
|
||||
public ResultMessage execute(ClientState state) throws InvalidRequestException
|
||||
{
|
||||
DatabaseDescriptor.getAuthenticator().create(username, opts.getOptions());
|
||||
Auth.insertUser(username, superuser);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.cql3.statements;
|
||||
|
||||
import org.apache.cassandra.auth.Auth;
|
||||
import org.apache.cassandra.auth.AuthenticatedUser;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
import org.apache.cassandra.transport.messages.ResultMessage;
|
||||
|
||||
public class DropUserStatement extends AuthenticationStatement
|
||||
{
|
||||
private final String username;
|
||||
|
||||
public DropUserStatement(String username)
|
||||
{
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public void validate(ClientState state) throws InvalidRequestException
|
||||
{
|
||||
if (!Auth.isExistingUser(username))
|
||||
throw new InvalidRequestException(String.format("User %s doesn't exists", username));
|
||||
|
||||
AuthenticatedUser user = state.getUser();
|
||||
if (user != null && user.getName().equals(username))
|
||||
throw new InvalidRequestException("Users aren't allowed to DROP themselves");
|
||||
}
|
||||
|
||||
public void checkAccess(ClientState state) throws UnauthorizedException
|
||||
{
|
||||
state.validateLogin();
|
||||
if (!state.getUser().isSuper())
|
||||
throw new UnauthorizedException("Only superusers are allowed to perfrom DROP USER queries");
|
||||
}
|
||||
|
||||
public ResultMessage execute(ClientState state) throws InvalidRequestException
|
||||
{
|
||||
// clean up permissions after the dropped user.
|
||||
DatabaseDescriptor.getAuthorizer().revokeAll(username);
|
||||
Auth.deleteUser(username);
|
||||
DatabaseDescriptor.getAuthenticator().drop(username);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -18,12 +18,11 @@
|
|||
*/
|
||||
package org.apache.cassandra.cql3.statements;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cassandra.auth.IResource;
|
||||
import org.apache.cassandra.auth.Permission;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
|
|
@ -36,9 +35,9 @@ public class GrantStatement extends PermissionAlteringStatement
|
|||
super(permissions, resource, username);
|
||||
}
|
||||
|
||||
public ResultMessage execute(ClientState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException
|
||||
public ResultMessage execute(ClientState state) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
state.grantPermission(permissions, resource, username);
|
||||
DatabaseDescriptor.getAuthorizer().grant(state.getUser(), permissions, resource, username);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@
|
|||
*/
|
||||
package org.apache.cassandra.cql3.statements;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.cassandra.auth.*;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.cql3.ColumnIdentifier;
|
||||
import org.apache.cassandra.cql3.ColumnSpecification;
|
||||
import org.apache.cassandra.cql3.ResultSet;
|
||||
|
|
@ -32,7 +32,7 @@ import org.apache.cassandra.transport.messages.ResultMessage;
|
|||
|
||||
public class ListPermissionsStatement extends AuthorizationStatement
|
||||
{
|
||||
private static final String KS = "auth"; // virtual keyspace to use for now.
|
||||
private static final String KS = Auth.AUTH_KS;
|
||||
private static final String CF = "permissions"; // virtual cf to use for now.
|
||||
|
||||
private static final List<ColumnSpecification> metadata;
|
||||
|
|
@ -59,9 +59,12 @@ public class ListPermissionsStatement extends AuthorizationStatement
|
|||
this.recursive = recursive;
|
||||
}
|
||||
|
||||
// TODO: user existence check (when IAuthenticator rewrite is done)
|
||||
public void validate(ClientState state) throws InvalidRequestException
|
||||
{
|
||||
// a check to ensure the existence of the user isn't being leaked by user existence check.
|
||||
if (username != null && !Auth.isExistingUser(username))
|
||||
throw new InvalidRequestException(String.format("User %s doesn't exist", username));
|
||||
|
||||
if (resource != null)
|
||||
{
|
||||
resource = maybeCorrectResource(resource, state);
|
||||
|
|
@ -70,19 +73,24 @@ public class ListPermissionsStatement extends AuthorizationStatement
|
|||
}
|
||||
}
|
||||
|
||||
public void checkAccess(ClientState state) throws UnauthorizedException
|
||||
{
|
||||
state.ensureNotAnonymous();
|
||||
}
|
||||
|
||||
// TODO: Create a new ResultMessage type (?). Rows will do for now.
|
||||
public ResultMessage execute(ClientState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException
|
||||
public ResultMessage execute(ClientState state) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
List<PermissionDetails> details = new ArrayList<PermissionDetails>();
|
||||
|
||||
if (resource != null && recursive)
|
||||
{
|
||||
for (IResource r : Resources.chain(resource))
|
||||
details.addAll(state.listPermissions(permissions, r, username));
|
||||
details.addAll(list(state, r));
|
||||
}
|
||||
else
|
||||
{
|
||||
details.addAll(state.listPermissions(permissions, resource, username));
|
||||
details.addAll(list(state, resource));
|
||||
}
|
||||
|
||||
Collections.sort(details);
|
||||
|
|
@ -103,4 +111,9 @@ public class ListPermissionsStatement extends AuthorizationStatement
|
|||
}
|
||||
return new ResultMessage.Rows(result);
|
||||
}
|
||||
|
||||
private Set<PermissionDetails> list(ClientState state, IResource resource) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
return DatabaseDescriptor.getAuthorizer().list(state.getUser(), permissions, resource, username);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.cql3.statements;
|
||||
|
||||
import org.apache.cassandra.auth.Auth;
|
||||
import org.apache.cassandra.cql3.QueryProcessor;
|
||||
import org.apache.cassandra.db.ConsistencyLevel;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.RequestExecutionException;
|
||||
import org.apache.cassandra.exceptions.RequestValidationException;
|
||||
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
import org.apache.cassandra.service.QueryState;
|
||||
import org.apache.cassandra.transport.messages.ResultMessage;
|
||||
|
||||
public class ListUsersStatement extends AuthenticationStatement
|
||||
{
|
||||
public void validate(ClientState state)
|
||||
{
|
||||
}
|
||||
|
||||
public void checkAccess(ClientState state) throws UnauthorizedException
|
||||
{
|
||||
state.ensureNotAnonymous();
|
||||
}
|
||||
|
||||
public ResultMessage execute(ClientState state) throws RequestValidationException, RequestExecutionException
|
||||
{
|
||||
return QueryProcessor.process(String.format("SELECT * FROM %s.%s", Auth.AUTH_KS, Auth.USERS_CF),
|
||||
ConsistencyLevel.ONE,
|
||||
new QueryState(new ClientState(true)));
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ package org.apache.cassandra.cql3.statements;
|
|||
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cassandra.auth.Auth;
|
||||
import org.apache.cassandra.auth.DataResource;
|
||||
import org.apache.cassandra.auth.IResource;
|
||||
import org.apache.cassandra.auth.Permission;
|
||||
|
|
@ -39,7 +40,7 @@ public abstract class PermissionAlteringStatement extends AuthorizationStatement
|
|||
this.username = username;
|
||||
}
|
||||
|
||||
public void checkAccess(ClientState state) throws InvalidRequestException, UnauthorizedException
|
||||
public void checkAccess(ClientState state) throws UnauthorizedException
|
||||
{
|
||||
// check that the user has AUTHORIZE permission on the resource or its parents, otherwise reject GRANT/REVOKE.
|
||||
state.ensureHasPermission(Permission.AUTHORIZE, resource);
|
||||
|
|
@ -48,9 +49,11 @@ public abstract class PermissionAlteringStatement extends AuthorizationStatement
|
|||
state.ensureHasPermission(p, resource);
|
||||
}
|
||||
|
||||
// TODO: user existence check (when IAuthenticator rewrite is done)
|
||||
public void validate(ClientState state) throws InvalidRequestException
|
||||
{
|
||||
if (!Auth.isExistingUser(username))
|
||||
throw new InvalidRequestException(String.format("User %s doesn't exist", username));
|
||||
|
||||
// if a keyspace is omitted when GRANT/REVOKE ON TABLE <table>, we need to correct the resource.
|
||||
resource = maybeCorrectResource(resource, state);
|
||||
if (!resource.exists())
|
||||
|
|
|
|||
|
|
@ -18,12 +18,11 @@
|
|||
*/
|
||||
package org.apache.cassandra.cql3.statements;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cassandra.auth.IResource;
|
||||
import org.apache.cassandra.auth.Permission;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
|
|
@ -36,9 +35,9 @@ public class RevokeStatement extends PermissionAlteringStatement
|
|||
super(permissions, resource, username);
|
||||
}
|
||||
|
||||
public ResultMessage execute(ClientState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException
|
||||
public ResultMessage execute(ClientState state) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
state.revokePermission(permissions, resource, username);
|
||||
DatabaseDescriptor.getAuthorizer().revoke(state.getUser(), permissions, resource, username);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.exceptions;
|
||||
|
||||
public class AuthenticationException extends RequestValidationException
|
||||
{
|
||||
public AuthenticationException(String msg)
|
||||
{
|
||||
super(ExceptionCode.BAD_CREDENTIALS, msg);
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,8 @@ public enum ExceptionCode
|
|||
SERVER_ERROR (0x0000),
|
||||
PROTOCOL_ERROR (0x000A),
|
||||
|
||||
BAD_CREDENTIALS (0x0100),
|
||||
|
||||
// 1xx: problem during request execution
|
||||
UNAVAILABLE (0x1000),
|
||||
OVERLOADED (0x1001),
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import org.apache.log4j.PropertyConfigurator;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.apache.cassandra.auth.Auth;
|
||||
import org.apache.cassandra.config.CFMetaData;
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
|
|
@ -219,11 +220,8 @@ public class CassandraDaemon
|
|||
System.exit(100);
|
||||
}
|
||||
|
||||
// TODO: setup authenticator
|
||||
// setup Authorizer.
|
||||
DatabaseDescriptor.getAuthorizer().setup();
|
||||
// register a custom MigrationListener for permissions cleanup after dropped keyspaces/cfs.
|
||||
MigrationManager.instance.register(new org.apache.cassandra.auth.MigrationListener());
|
||||
// setup Authenticator and Authorizer.
|
||||
Auth.setup();
|
||||
|
||||
// clean up debris in the rest of the tables
|
||||
for (String table : Schema.instance.getTables())
|
||||
|
|
|
|||
|
|
@ -28,15 +28,13 @@ import org.apache.cassandra.config.DatabaseDescriptor;
|
|||
import org.apache.cassandra.config.Schema;
|
||||
import org.apache.cassandra.db.SystemTable;
|
||||
import org.apache.cassandra.db.Table;
|
||||
import org.apache.cassandra.exceptions.AuthenticationException;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||
import org.apache.cassandra.thrift.AuthenticationException;
|
||||
import org.apache.cassandra.utils.SemanticVersion;
|
||||
|
||||
/**
|
||||
* State related to a client connection.
|
||||
*
|
||||
* TODO: Kill thrift exceptions
|
||||
*/
|
||||
public class ClientState
|
||||
{
|
||||
|
|
@ -57,12 +55,12 @@ public class ClientState
|
|||
for (String cf : cfs)
|
||||
READABLE_SYSTEM_RESOURCES.add(DataResource.columnFamily(Table.SYSTEM_KS, cf));
|
||||
|
||||
PROTECTED_AUTH_RESOURCES.addAll(DatabaseDescriptor.getAuthenticator().protectedResources());
|
||||
PROTECTED_AUTH_RESOURCES.addAll(DatabaseDescriptor.getAuthorizer().protectedResources());
|
||||
// TODO: the same with IAuthenticator once it's done.
|
||||
}
|
||||
|
||||
// Current user for the session
|
||||
private AuthenticatedUser user;
|
||||
private volatile AuthenticatedUser user;
|
||||
private String keyspace;
|
||||
|
||||
private SemanticVersion cqlVersion = DEFAULT_CQL_VERSION;
|
||||
|
|
@ -82,7 +80,8 @@ public class ClientState
|
|||
public ClientState(boolean internalCall)
|
||||
{
|
||||
this.internalCall = internalCall;
|
||||
this.user = DatabaseDescriptor.getAuthenticator().defaultUser();
|
||||
if (!DatabaseDescriptor.getAuthenticator().requireAuthentication())
|
||||
this.user = AuthenticatedUser.ANONYMOUS_USER;
|
||||
}
|
||||
|
||||
public String getRawKeyspace()
|
||||
|
|
@ -107,9 +106,15 @@ public class ClientState
|
|||
/**
|
||||
* Attempts to login this client with the given credentials map.
|
||||
*/
|
||||
public void login(Map<? extends CharSequence,? extends CharSequence> credentials) throws AuthenticationException
|
||||
public void login(Map<String, String> credentials) throws AuthenticationException
|
||||
{
|
||||
this.user = DatabaseDescriptor.getAuthenticator().authenticate(credentials);
|
||||
AuthenticatedUser user = DatabaseDescriptor.getAuthenticator().authenticate(credentials);
|
||||
|
||||
if (!user.isAnonymous() && !Auth.isExistingUser(user.getName()))
|
||||
throw new AuthenticationException(String.format("User %s doesn't exist - create it with CREATE USER query first",
|
||||
user.getName()));
|
||||
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public void hasAllKeyspacesAccess(Permission perm) throws UnauthorizedException, InvalidRequestException
|
||||
|
|
@ -154,7 +159,7 @@ public class ClientState
|
|||
return;
|
||||
}
|
||||
throw new UnauthorizedException(String.format("User %s has no %s permission on %s or any of its parents",
|
||||
user.username,
|
||||
user.getName(),
|
||||
perm,
|
||||
resource));
|
||||
}
|
||||
|
|
@ -165,15 +170,17 @@ public class ClientState
|
|||
throw new UnauthorizedException(keyspace + " keyspace is not user-modifiable.");
|
||||
}
|
||||
|
||||
public boolean isLogged()
|
||||
{
|
||||
return user != null;
|
||||
}
|
||||
|
||||
private void validateLogin() throws InvalidRequestException
|
||||
public void validateLogin() throws UnauthorizedException
|
||||
{
|
||||
if (user == null)
|
||||
throw new InvalidRequestException("You have not logged in");
|
||||
throw new UnauthorizedException("You have not logged in");
|
||||
}
|
||||
|
||||
public void ensureNotAnonymous() throws UnauthorizedException
|
||||
{
|
||||
validateLogin();
|
||||
if (user.isAnonymous())
|
||||
throw new UnauthorizedException("You have to be logged in to perform this query");
|
||||
}
|
||||
|
||||
private static void validateKeyspace(String keyspace) throws InvalidRequestException
|
||||
|
|
@ -214,6 +221,11 @@ public class ClientState
|
|||
StringUtils.join(getCQLSupportedVersion(), ", ")));
|
||||
}
|
||||
|
||||
public AuthenticatedUser getUser()
|
||||
{
|
||||
return user;
|
||||
}
|
||||
|
||||
public SemanticVersion getCQLVersion()
|
||||
{
|
||||
return cqlVersion;
|
||||
|
|
@ -227,26 +239,8 @@ public class ClientState
|
|||
return new SemanticVersion[]{ cql, cql3 };
|
||||
}
|
||||
|
||||
public Set<Permission> authorize(IResource resource)
|
||||
private Set<Permission> authorize(IResource resource)
|
||||
{
|
||||
return DatabaseDescriptor.getAuthorizer().authorize(user, resource);
|
||||
|
||||
}
|
||||
public void grantPermission(Set<Permission> permissions, IResource resource, String to)
|
||||
throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
DatabaseDescriptor.getAuthorizer().grant(user, permissions, resource, to);
|
||||
}
|
||||
|
||||
public void revokePermission(Set<Permission> permissions, IResource resource, String from)
|
||||
throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
DatabaseDescriptor.getAuthorizer().revoke(user, permissions, resource, from);
|
||||
}
|
||||
|
||||
public Set<PermissionDetails> listPermissions(Set<Permission> permissions, IResource resource, String of)
|
||||
throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
return DatabaseDescriptor.getAuthorizer().listPermissions(user, permissions, resource, of);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1238,7 +1238,14 @@ public class CassandraServer implements Cassandra.Iface
|
|||
|
||||
public void login(AuthenticationRequest auth_request) throws AuthenticationException, AuthorizationException, TException
|
||||
{
|
||||
state().login(auth_request.getCredentials());
|
||||
try
|
||||
{
|
||||
state().login(auth_request.getCredentials());
|
||||
}
|
||||
catch (org.apache.cassandra.exceptions.AuthenticationException e)
|
||||
{
|
||||
throw ThriftConversion.toThrift(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -83,6 +83,11 @@ public class ThriftConversion
|
|||
return new UnavailableException();
|
||||
}
|
||||
|
||||
public static AuthenticationException toThrift(org.apache.cassandra.exceptions.AuthenticationException e)
|
||||
{
|
||||
return new AuthenticationException(e.getMessage());
|
||||
}
|
||||
|
||||
public static TimedOutException toThrift(RequestTimeoutException e)
|
||||
{
|
||||
TimedOutException toe = new TimedOutException();
|
||||
|
|
|
|||
|
|
@ -23,11 +23,10 @@ import java.util.Map;
|
|||
import org.jboss.netty.buffer.ChannelBuffer;
|
||||
import org.jboss.netty.buffer.ChannelBuffers;
|
||||
|
||||
import org.apache.cassandra.exceptions.AuthenticationException;
|
||||
import org.apache.cassandra.service.QueryState;
|
||||
import org.apache.cassandra.transport.CBUtil;
|
||||
import org.apache.cassandra.transport.Message;
|
||||
import org.apache.cassandra.transport.ServerConnection;
|
||||
import org.apache.cassandra.thrift.AuthenticationException;
|
||||
|
||||
/**
|
||||
* Message to indicate that the server is ready to receive requests.
|
||||
|
|
|
|||
|
|
@ -57,6 +57,9 @@ public class ErrorMessage extends Message.Response
|
|||
case PROTOCOL_ERROR:
|
||||
te = new ProtocolException(msg);
|
||||
break;
|
||||
case BAD_CREDENTIALS:
|
||||
te = new AuthenticationException(msg);
|
||||
break;
|
||||
case UNAVAILABLE:
|
||||
{
|
||||
ConsistencyLevel cl = CBUtil.readConsistencyLevel(body);
|
||||
|
|
|
|||
|
|
@ -100,10 +100,10 @@ public class StartupMessage extends Message.Request
|
|||
}
|
||||
}
|
||||
|
||||
if (cState.isLogged())
|
||||
return new ReadyMessage();
|
||||
else
|
||||
if (DatabaseDescriptor.getAuthenticator().requireAuthentication())
|
||||
return new AuthenticateMessage(DatabaseDescriptor.getAuthenticator().getClass().getName());
|
||||
else
|
||||
return new ReadyMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Reference in New Issue