You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by rh...@apache.org on 2012/01/19 14:51:14 UTC
svn commit: r1233377 - in /db/derby/code/trunk/java:
engine/org/apache/derby/ engine/org/apache/derby/catalog/
engine/org/apache/derby/iapi/jdbc/ engine/org/apache/derby/iapi/reference/
engine/org/apache/derby/iapi/services/monitor/ engine/org/apache/d...
Author: rhillegas
Date: Thu Jan 19 13:51:13 2012
New Revision: 1233377
URL: http://svn.apache.org/viewvc?rev=1233377&view=rev
Log:
First version of NATIVE authentication service, including first tranche of tests for it.
Added:
db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/NativeAuthenticationServiceImpl.java (with props)
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/NativeAuthenticationServiceTest.java (with props)
Modified:
db/derby/code/trunk/java/engine/org/apache/derby/catalog/SystemProcedures.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/AuthenticationService.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/monitor/ModuleFactory.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/property/PropertyUtil.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/AuthenticationServiceBase.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/SpecificAuthenticationServiceImpl.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/BaseMonitor.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
db/derby/code/trunk/java/engine/org/apache/derby/modules.properties
db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/_Suite.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DatabaseChangeSetup.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DriverManagerConnector.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DropDatabaseSetup.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/NetworkServerTestSetup.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/SystemPropertyTestSetup.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/TestConfiguration.java
Modified: db/derby/code/trunk/java/engine/org/apache/derby/catalog/SystemProcedures.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/catalog/SystemProcedures.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/catalog/SystemProcedures.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/catalog/SystemProcedures.java Thu Jan 19 13:51:13 2012
@@ -2051,10 +2051,26 @@ public class SystemProcedures {
)
throws SQLException
{
+ LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
+ TransactionController tc = lcc.getTransactionExecute();
+
+ addUser( userName, password, tc );
+ }
+ /**
+ * Create a new user (this entry is called when bootstrapping the credentials of the DBO
+ * at database creation time.
+ */
+ public static void addUser
+ (
+ String userName,
+ String password,
+ TransactionController tc
+ )
+ throws SQLException
+ {
try {
LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
DataDictionary dd = lcc.getDataDictionary();
- TransactionController tc = lcc.getTransactionExecute();
/*
** Inform the data dictionary that we are about to write to it.
@@ -2067,7 +2083,7 @@ public class SystemProcedures {
*/
dd.startWriting(lcc);
- UserDescriptor userDescriptor = makeUserDescriptor( lcc, userName, password );
+ UserDescriptor userDescriptor = makeUserDescriptor( dd, tc, userName, password );
dd.addDescriptor( userDescriptor, null, DataDictionary.SYSUSERS_CATALOG_NUM, false, tc );
@@ -2075,15 +2091,15 @@ public class SystemProcedures {
}
private static UserDescriptor makeUserDescriptor
(
- LanguageConnectionContext lcc,
+ DataDictionary dd,
+ TransactionController tc,
String userName,
String password
)
throws StandardException
{
- DataDictionary dd = lcc.getDataDictionary();
DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();
- PasswordHasher hasher = dd.makePasswordHasher( lcc.getTransactionExecute().getProperties() );
+ PasswordHasher hasher = dd.makePasswordHasher( tc.getProperties() );
if ( hasher == null )
{
@@ -2127,7 +2143,7 @@ public class SystemProcedures {
*/
dd.startWriting(lcc);
- UserDescriptor userDescriptor = makeUserDescriptor( lcc, userName, password );
+ UserDescriptor userDescriptor = makeUserDescriptor( dd, tc, userName, password );
dd.updateUser( userDescriptor, tc );
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/AuthenticationService.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/AuthenticationService.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/AuthenticationService.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/AuthenticationService.java Thu Jan 19 13:51:13 2012
@@ -40,11 +40,20 @@ public interface AuthenticationService
public static final String MODULE =
"org.apache.derby.iapi.jdbc.AuthenticationService";
/**
- * Authenticate a User inside JBMS.
+ * Authenticate a User inside Derby.
*
* @param info Connection properties info.
* failure.
*/
public boolean authenticate(String databaseName, Properties info)
throws SQLException;
+
+ /**
+ * <p>
+ * Get the name of the credentials database used to authenticate system-wide operations.
+ * This returns null for all implementations except NATIVE authentication.
+ * </p>
+ */
+ public String getSystemCredentialsDatabaseName();
+
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java Thu Jan 19 13:51:13 2012
@@ -797,6 +797,9 @@ public interface Property {
// These are the different built-in providers Derby supports
+ public static final String AUTHENTICATION_PROVIDER_NATIVE =
+ "NATIVE:";
+
public static final String AUTHENTICATION_PROVIDER_BUILTIN =
"BUILTIN";
@@ -806,6 +809,15 @@ public interface Property {
public static final String AUTHENTICATION_SERVER_PARAMETER =
"derby.authentication.server";
+ // this suffix on the NATIVE authentication provider means that
+ // database operations should be authenticated locally
+ public static final String AUTHENTICATION_PROVIDER_LOCAL_SUFFIX =
+ ":LOCAL";
+
+ // when local native authentication is enabled, we store this value for derby.authentication.provider
+ public static final String AUTHENTICATION_PROVIDER_NATIVE_LOCAL =
+ AUTHENTICATION_PROVIDER_NATIVE + AUTHENTICATION_PROVIDER_LOCAL_SUFFIX;
+
/**
* Property that specifies the name of the hash algorithm to use with
* the configurable hash authentication scheme.
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/monitor/ModuleFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/monitor/ModuleFactory.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/monitor/ModuleFactory.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/monitor/ModuleFactory.java Thu Jan 19 13:51:13 2012
@@ -227,6 +227,13 @@ public interface ModuleFactory
/**
+ Canonicalize a service name, mapping different user-specifications of a database name
+ onto a single, standard name.
+ */
+ public String getCanonicalServiceName( String userSpecifiedName )
+ throws StandardException;
+
+ /**
Find a service.
<BR>
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/property/PropertyUtil.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/property/PropertyUtil.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/property/PropertyUtil.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/property/PropertyUtil.java Thu Jan 19 13:51:13 2012
@@ -562,6 +562,39 @@ public class PropertyUtil {
return false;
}
+ /**
+ Return true if NATIVE authentication has been enabled in the passed-in properties.
+ */
+ public static boolean nativeAuthenticationEnabled( Properties properties )
+ {
+ String authenticationProvider = getPropertyFromSet
+ (
+ properties,
+ Property.AUTHENTICATION_PROVIDER_PARAMETER
+ );
+
+ if ( authenticationProvider == null ) { return false; }
+
+ return StringUtil.SQLToUpperCase( authenticationProvider ).startsWith( Property.AUTHENTICATION_PROVIDER_NATIVE );
+ }
+
+ /**
+ Return true if the passed-in properties specify NATIVE authentication using LOCAL credentials.
+ */
+ public static boolean localNativeAuthenticationEnabled( Properties properties )
+ {
+ if ( ! nativeAuthenticationEnabled( properties ) ) { return false; }
+
+ String authenticationProvider = getPropertyFromSet
+ (
+ properties,
+ Property.AUTHENTICATION_PROVIDER_PARAMETER
+ );
+
+ return StringUtil.SQLToUpperCase( authenticationProvider ).endsWith
+ ( Property.AUTHENTICATION_PROVIDER_LOCAL_SUFFIX );
+ }
+
/**
* Return true if username is defined as a system property
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java Thu Jan 19 13:51:13 2012
@@ -1692,6 +1692,17 @@ public interface DataDictionary
public void updateUser( UserDescriptor newDescriptor,TransactionController tc )
throws StandardException;
+ /**
+ * Return the credentials descriptor for the named user.
+ *
+ * @param userName Name of the user whose credentials we want.
+ * @param tc The TransactionController to use
+ *
+ * @exception StandardException Thrown on failure
+ */
+ public UserDescriptor getUser( String userName, TransactionController tc )
+ throws StandardException;
+
/**
* Drop a User from the DataDictionary
*
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java Thu Jan 19 13:51:13 2012
@@ -397,9 +397,11 @@ public abstract class EmbedConnection im
addWarning(SQLWarningFactory.newSQLWarning(SQLState.DATABASE_EXISTS, getDBName()));
} else {
- // check for user's credential and authenticate the user
- // with system level authentication service.
- checkUserCredentials(null, info);
+ //
+ // Check for user's credential and authenticate the user
+ // with the system level authentication service.
+ //
+ checkUserCredentials( true, null, info );
// Process with database creation
database = createDatabase(tr.getDBName(), info);
@@ -417,7 +419,7 @@ public abstract class EmbedConnection im
// the database
//
try {
- checkUserCredentials(tr.getDBName(), info);
+ checkUserCredentials( false, tr.getDBName(), info );
} catch (SQLException sqle) {
if (isStartSlaveBoot && !slaveDBAlreadyBooted) {
// Failing credentials check on a previously
@@ -1170,7 +1172,7 @@ public abstract class EmbedConnection im
//
// Check passed-in user's credentials.
//
- private void checkUserCredentials(String dbname,
+ private void checkUserCredentials( boolean creatingDatabase, String dbname,
Properties userInfo)
throws SQLException
{
@@ -1209,7 +1211,31 @@ public abstract class EmbedConnection im
throw newSQLException(SQLState.LOGIN_FAILED, failedString);
}
- if (dbname != null) {
+ //
+ // We must handle the special case when the system uses NATIVE
+ // authentication for system-wide operations but we are being
+ // asked to create the system-wide credentials database. In this situation,
+ // the database holding the credentials does not exist yet. In this situation,
+ // we are supposed to create the credentials database and store the
+ // creation credentials in that database as the credentials of the system administrator.
+ //
+ if (
+ creatingDatabase &&
+ compareDatabaseNames( getDBName(), authenticationService.getSystemCredentialsDatabaseName() )
+ )
+ {
+ //
+ // NATIVE authentication using a system-wide credentials database
+ // which is being created now. Allow this to succeed.
+ //
+ return;
+ }
+
+ //
+ // If we are creating a database, we set the dbname
+ //
+
+ if (dbname != null) {
checkUserIsNotARole();
}
@@ -1232,6 +1258,23 @@ public abstract class EmbedConnection im
usingNoneAuth = true;
}
+ /**
+ * Compare two user-specified database names to see if they identify
+ * the same database.
+ */
+ private boolean compareDatabaseNames( String leftDBName, String rightDBName )
+ throws SQLException
+ {
+ try {
+ String leftCanonical = Monitor.getMonitor().getCanonicalServiceName( leftDBName );
+ String rightCanonical = Monitor.getMonitor().getCanonicalServiceName( rightDBName );
+
+ if ( leftCanonical == null ) { return false; }
+ else { return leftCanonical.equals( rightCanonical ); }
+
+ } catch (StandardException se) { throw Util.generateCsSQLException(se); }
+ }
+
/**
* If applicable, check that we don't connect with a user name
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/AuthenticationServiceBase.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/AuthenticationServiceBase.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/AuthenticationServiceBase.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/AuthenticationServiceBase.java Thu Jan 19 13:51:13 2012
@@ -260,6 +260,8 @@ public abstract class AuthenticationServ
);
}
+ public String getSystemCredentialsDatabaseName() { return null; }
+
/**
* Returns a property if it was set at the database or
* system level. Treated as SERVICE property by default.
@@ -273,11 +275,7 @@ public abstract class AuthenticationServ
try {
- if (store != null)
- {
- tc = store.getTransaction(
- ContextService.getFactory().getCurrentContextManager());
- }
+ tc = getTransaction();
propertyValue =
PropertyUtil.getServiceProperty(tc,
@@ -295,6 +293,32 @@ public abstract class AuthenticationServ
return propertyValue;
}
+ /**
+ * <p>
+ * Get a transaction for performing authentication at the database level.
+ * </p>
+ */
+ protected TransactionController getTransaction()
+ throws StandardException
+ {
+ if ( store == null ) { return null; }
+ else
+ {
+ return store.getTransaction( ContextService.getFactory().getCurrentContextManager() );
+ }
+ }
+
+ /**
+ * <p>
+ * Get the name of the database if we are performing authentication at the database level.
+ * </p>
+ */
+ protected String getServiceName()
+ {
+ if ( store == null ) { return null; }
+ else { return Monitor.getServiceName( store ); }
+ }
+
public String getDatabaseProperty(String key) {
String propertyValue = null;
@@ -406,7 +430,12 @@ public abstract class AuthenticationServ
properties,
org.apache.derby.iapi.reference.Property.REQUIRE_AUTHENTICATION_PARAMETER
);
- return Boolean.valueOf(requireAuthentication).booleanValue();
+ if ( Boolean.valueOf(requireAuthentication).booleanValue() ) { return true; }
+
+ //
+ // NATIVE authentication does not require that you set REQUIRE_AUTHENTICATION_PARAMETER.
+ //
+ return PropertyUtil.nativeAuthenticationEnabled( properties );
}
/**
Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/NativeAuthenticationServiceImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/NativeAuthenticationServiceImpl.java?rev=1233377&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/NativeAuthenticationServiceImpl.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/NativeAuthenticationServiceImpl.java Thu Jan 19 13:51:13 2012
@@ -0,0 +1,473 @@
+/*
+
+ Derby - Class org.apache.derby.impl.jdbc.authentication.NativeAuthenticationServiceImpl
+
+ 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.derby.impl.jdbc.authentication;
+
+import java.util.Properties;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Arrays;
+import javax.sql.DataSource;
+
+import org.apache.derby.catalog.SystemProcedures;
+import org.apache.derby.iapi.db.Database;
+import org.apache.derby.iapi.reference.Property;
+import org.apache.derby.iapi.services.info.JVMInfo;
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;
+import org.apache.derby.iapi.sql.dictionary.PasswordHasher;
+import org.apache.derby.iapi.sql.dictionary.UserDescriptor;
+import org.apache.derby.iapi.reference.Attribute;
+import org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.authentication.UserAuthenticator;
+import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.services.monitor.Monitor;
+import org.apache.derby.iapi.services.property.PropertyUtil;
+import org.apache.derby.iapi.services.sanity.SanityManager;
+import org.apache.derby.iapi.store.access.TransactionController;
+import org.apache.derby.iapi.util.IdUtil;
+import org.apache.derby.iapi.util.StringUtil;
+import org.apache.derby.impl.jdbc.Util;
+
+/**
+ * <p>
+ * This authentication service supports Derby NATIVE authentication.
+ * </p>
+ *
+ * <p>
+ * To activate this service, set the derby.authentication.provider database
+ * or system property to a value beginning with the token "NATIVE:".
+ * </p>
+ *
+ * <p>
+ * This service instantiates and calls the basic User authentication scheme at runtime.
+ * </p>
+ *
+ * <p>
+ * User credentials are defined in the SYSUSERS table.
+ * </p>
+ *
+ */
+public final class NativeAuthenticationServiceImpl
+ extends AuthenticationServiceBase implements UserAuthenticator
+{
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // CONSTANTS
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // STATE
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ // temporary, used when bootstrapping a locally authenticated database
+ private boolean _creatingCredentialsDB = false;
+
+ private String _credentialsDB;
+ private boolean _authenticateDatabaseOperationsLocally;
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // ModuleControl BEHAVIOR
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Check if we should activate this authentication service.
+ */
+ public boolean canSupport(Properties properties)
+ {
+ if (!requireAuthentication(properties)) { return false; }
+
+ if ( PropertyUtil.nativeAuthenticationEnabled( properties ) )
+ {
+ parseNativeSpecification( properties );
+
+ return true;
+ }
+ else { return false; }
+ }
+
+ /**
+ * <p>
+ * Parse the specification of NATIVE authentication. It can take 3 forms:
+ * </p>
+ *
+ * <ul>
+ * <li><i>NATIVE:$credentialsDB</i> - Here $credentialsDB is the name of a Derby database.
+ * This means that all authentication should take place in $credentialsDB.</li>
+ * <li><i>NATIVE:$credentialsDB:LOCAL</i>- This means that system-wide operations (like engine shutdown)
+ * are authenticated in $credentialsDB but connections to existing databases are authenticated
+ * in those databases.</li>
+ * <li><i>NATIVE::LOCAL</i> - This means that connections to a given database are authenticated
+ * in that database.</li>
+ * </ul>
+ */
+ private void parseNativeSpecification( Properties properties )
+ {
+ // If we get here, we already know that the authentication provider property
+ // begins with the NATIVE: token
+ String authenticationProvider = PropertyUtil.getPropertyFromSet
+ (
+ properties,
+ Property.AUTHENTICATION_PROVIDER_PARAMETER
+ );
+
+ _authenticateDatabaseOperationsLocally = PropertyUtil.localNativeAuthenticationEnabled( properties );
+
+ // Everything between the first colon and the last colon is the name of a database
+ int dbNameStartIdx = authenticationProvider.indexOf( ":" ) + 1;
+ int dbNameEndIdx = _authenticateDatabaseOperationsLocally ?
+ authenticationProvider.lastIndexOf( ":" )
+ : authenticationProvider.length();
+
+ if ( dbNameEndIdx > dbNameStartIdx )
+ {
+ _credentialsDB = authenticationProvider.substring( dbNameStartIdx, dbNameEndIdx );
+
+ if ( _credentialsDB.length() == 0 ) { _credentialsDB = null; }
+ }
+ }
+
+ /**
+ * <p>
+ * Return true if AUTHENTICATION_PROVIDER_PARAMETER was well formatted.
+ * The property must have designated some database as the authentication authority.
+ * </p>
+ */
+ private boolean validAuthenticationProvider()
+ {
+ return (_credentialsDB != null) || _authenticateDatabaseOperationsLocally;
+ }
+
+ /**
+ * @see org.apache.derby.iapi.services.monitor.ModuleControl#boot
+ * @exception StandardException upon failure to load/boot the expected
+ * authentication service.
+ */
+ public void boot(boolean create, Properties properties)
+ throws StandardException
+ {
+ // first perform the initialization in our superclass
+ super.boot( create, properties );
+
+ if ( !validAuthenticationProvider() )
+ {
+ throw StandardException.newException( SQLState.BAD_NATIVE_AUTH_SPEC );
+ }
+
+ // Initialize the MessageDigest class engine here
+ // (we don't need to do that ideally, but there is some
+ // overhead the first time it is instantiated.
+ try {
+ MessageDigest digestAlgorithm = MessageDigest.getInstance("SHA-1");
+ digestAlgorithm.reset();
+
+ } catch (NoSuchAlgorithmException nsae) {
+ throw Monitor.exceptionStartingModule(nsae);
+ }
+
+ // bootstrap the creation of the initial username/password when the dbo creates a credentials db
+ if ( create && authenticatingInThisService( getCanonicalServiceName() ) ) { _creatingCredentialsDB = true; }
+ else { _creatingCredentialsDB = false; }
+
+ // Set ourselves as being ready, having loaded the proper
+ // authentication scheme for this service
+ //
+ this.setAuthenticationService(this);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // UserAuthenticator BEHAVIOR
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ /** Override behavior in superclass */
+ public String getSystemCredentialsDatabaseName() { return _credentialsDB; }
+ /** Override behavior in superclass */
+
+ /**
+ * Authenticate the passed-in user's credentials.
+ *
+ * @param userName The user's name used to connect to JBMS system
+ * @param userPassword The user's password used to connect to JBMS system
+ * @param databaseName The database which the user wants to connect to.
+ * @param info Additional jdbc connection info.
+ */
+ public boolean authenticateUser
+ (
+ String userName,
+ String userPassword,
+ String databaseName,
+ Properties info
+ )
+ throws SQLException
+ {
+ try {
+ // No "guest" user
+ if ( userName == null ) { return false; }
+
+ //
+ // We must handle these cases:
+ //
+ // 1) Database name is null. This means that we are authenticating a system-wide
+ // operation. The authentication must be done by the system-wide credentials database.
+ //
+ // 2) Database name is not null and authentication is NOT specified as local.
+ // This means that we are authenticating a database-specific operation
+ // in the system-wide credentials database. There are two subcases:
+ //
+ // 2a) The current database is NOT the credentials database. This reduces to case (1) above:
+ // authentication must be performed in another database.
+ //
+ // 2b) The current database IS the credentials database. This reduces to case (3) below:
+ // authentication must be performed in this database.
+ //
+ // 3) Database name is not null and authentication IS being performed locally in this database.
+ // This means that we are authenticating a database-specific operation and performing the
+ // authentication in this database.
+ //
+
+ if ( (databaseName == null) || !authenticatingInThisDatabase( databaseName ) )
+ {
+ return authenticateRemotely( userName, userPassword, databaseName );
+ }
+ else
+ {
+ return authenticateLocally( userName, userPassword, databaseName );
+ }
+ }
+ catch (StandardException se)
+ {
+ throw Util.generateCsSQLException(se);
+ }
+ }
+
+ /**
+ * <p>
+ * Return true if we are authenticating in this database.
+ * </p>
+ */
+ private boolean authenticatingInThisDatabase( String userVisibleDatabaseName )
+ throws StandardException
+ {
+ return authenticatingInThisService( Monitor.getMonitor().getCanonicalServiceName( userVisibleDatabaseName ) );
+ }
+
+ /**
+ * <p>
+ * Return true if we are authenticating in this service.
+ * </p>
+ */
+ private boolean authenticatingInThisService( String canonicalDatabaseName )
+ throws StandardException
+ {
+ if ( _authenticateDatabaseOperationsLocally ) { return true; }
+ else { return isCredentialsService( canonicalDatabaseName ); }
+ }
+
+ /**
+ * <p>
+ * Return true if the passed in service is the credentials database.
+ * </p>
+ */
+ private boolean isCredentialsService( String canonicalDatabaseName )
+ throws StandardException
+ {
+ String canonicalCredentialsDBName = getCanonicalServiceName( _credentialsDB );
+
+ String canonicalDB = Monitor.getMonitor().getCanonicalServiceName( canonicalDatabaseName );
+
+ if ( canonicalCredentialsDBName == null ) { return false; }
+ else { return canonicalCredentialsDBName.equals( canonicalDatabaseName ); }
+ }
+
+ /** Get the canonical name of the current database service */
+ private String getCanonicalServiceName()
+ throws StandardException
+ {
+ return getCanonicalServiceName( getServiceName() );
+ }
+
+ /** Turn a service name into its normalized, standard form */
+ private String getCanonicalServiceName( String rawName )
+ throws StandardException
+ {
+ return Monitor.getMonitor().getCanonicalServiceName( rawName );
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // AUTHENTICATE REMOTELY
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Authenticate the passed-in credentials against another Derby database. This is done
+ * by getting a connection to the credentials database using the supplied username
+ * and password. If the connection attempts succeeds, then authentication succeeds.
+ *
+ * @param userName The user's name used to connect to JBMS system
+ * @param userPassword The user's password used to connect to JBMS system
+ * @param databaseName The database which the user wants to connect to.
+ */
+ private boolean authenticateRemotely
+ (
+ String userName,
+ String userPassword,
+ String databaseName
+ )
+ throws StandardException
+ {
+ // this catches the case when someone specifies derby.authentication.provider=NATIVE::LOCAL
+ // at the system level
+ if ( _credentialsDB == null )
+ {
+ throw StandardException.newException( SQLState.BAD_NATIVE_AUTH_SPEC );
+ }
+
+ String dataSourceName = JVMInfo.J2ME ?
+ "org.apache.derby.jdbc.EmbeddedSimpleDataSource" :
+ "org.apache.derby.jdbc.EmbeddedDataSource";
+
+ try {
+ DataSource dataSource = (DataSource) Class.forName( dataSourceName ).newInstance();
+
+ callDataSourceSetter( dataSource, "setDatabaseName", _credentialsDB );
+ callDataSourceSetter( dataSource, "setUser", userName );
+ callDataSourceSetter( dataSource, "setPassword", userPassword );
+
+ Connection conn = dataSource.getConnection();
+ conn.close();
+ }
+ catch (ClassNotFoundException cnfe) { throw wrap( cnfe ); }
+ catch (InstantiationException ie) { throw wrap( ie ); }
+ catch (IllegalAccessException ie) { throw wrap( ie ); }
+ catch (SQLException se)
+ {
+ String sqlState = se.getSQLState();
+
+ if ( SQLState.LOGIN_FAILED.equals( sqlState ) ) { return false; }
+ else if ( SQLState.DATABASE_NOT_FOUND.startsWith( sqlState ) )
+ {
+ throw StandardException.newException( SQLState.MISSING_CREDENTIALS_DB, _credentialsDB );
+ }
+ else { throw wrap( se ); }
+ }
+
+ // If we get here, then we successfully connected to the credentials database. Hooray.
+ return true;
+ }
+ /** Call a setter method on a DataSource via reflection */
+ private void callDataSourceSetter( DataSource ds, String methodName, String value )
+ throws StandardException
+ {
+ try {
+ ds.getClass().getMethod( methodName, new Class[] { String.class } ).invoke( ds, new Object[] { value } );
+ } catch (Exception e) { throw wrap( e ); }
+ }
+ private StandardException wrap( Throwable t ) { return StandardException.plainWrapException( t ); }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // AUTHENTICATE LOCALLY
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Authenticate the passed-in credentials against the local database.
+ *
+ * @param userName The user's name used to connect to JBMS system
+ * @param userPassword The user's password used to connect to JBMS system
+ * @param databaseName The database which the user wants to connect to.
+ */
+ private boolean authenticateLocally
+ (
+ String userName,
+ String userPassword,
+ String databaseName
+ )
+ throws StandardException, SQLException
+ {
+ userName = IdUtil.getUserAuthorizationId( userName ) ;
+
+ TransactionController tc = getTransaction();
+ try {
+ //
+ // Special bootstrap code. If we are creating a credentials database, then
+ // we store the DBO's initial credentials in it. We also turn on NATIVE LOCAL authentication
+ // forever.
+ //
+ if ( _creatingCredentialsDB )
+ {
+ _creatingCredentialsDB = false;
+
+ SystemProcedures.addUser( userName, userPassword, tc );
+
+ tc.setProperty
+ ( Property.AUTHENTICATION_PROVIDER_PARAMETER, Property.AUTHENTICATION_PROVIDER_NATIVE_LOCAL, true );
+
+ return true;
+ }
+
+ //
+ // we expect to find a data dictionary
+ //
+ DataDictionary dd = (DataDictionary) Monitor.getServiceModule( this, DataDictionary.MODULE );
+
+ //
+ // NATIVE authentication is only available if the database is at version 10.9 or later
+ //
+ dd.checkVersion( DataDictionary.DD_VERSION_DERBY_10_9, "NATIVE AUTHENTICATION" );
+
+ UserDescriptor userDescriptor = dd.getUser( userName, tc );
+
+ if ( userDescriptor == null ) { return false; }
+
+ PasswordHasher hasher = new PasswordHasher( userDescriptor.getHashingScheme() );
+ char[] candidatePassword = hasher.hashPasswordIntoString( userName, userPassword ).toCharArray();
+ char[] actualPassword = userDescriptor.getAndZeroPassword();
+
+ if ( (candidatePassword == null) || (actualPassword == null)) { return false; }
+ if ( candidatePassword.length != actualPassword.length ) { return false; }
+
+ for ( int i = 0; i < candidatePassword.length; i++ )
+ {
+ if ( candidatePassword[ i ] != actualPassword[ i ] ) { return false; }
+ }
+
+ Arrays.fill( candidatePassword, (char) 0 );
+ Arrays.fill( actualPassword, (char) 0 );
+
+ return true;
+ }
+ finally
+ {
+ tc.commit();
+ }
+ }
+
+}
Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/NativeAuthenticationServiceImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/SpecificAuthenticationServiceImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/SpecificAuthenticationServiceImpl.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/SpecificAuthenticationServiceImpl.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/SpecificAuthenticationServiceImpl.java Thu Jan 19 13:51:13 2012
@@ -68,6 +68,12 @@ public class SpecificAuthenticationServi
if (!requireAuthentication(properties))
return false;
+ //
+ // Don't treat the NATIVE authentication specification as a user-supplied
+ // class which should be instantiated.
+ //
+ if ( PropertyUtil.nativeAuthenticationEnabled( properties ) ) { return false; }
+
specificAuthenticationScheme = PropertyUtil.getPropertyFromSet(
properties,
org.apache.derby.iapi.reference.Property.AUTHENTICATION_PROVIDER_PARAMETER);
@@ -78,7 +84,7 @@ public class SpecificAuthenticationServi
(!((StringUtil.SQLEqualsIgnoreCase(specificAuthenticationScheme,
org.apache.derby.iapi.reference.Property.AUTHENTICATION_PROVIDER_BUILTIN)) ||
(specificAuthenticationScheme.equalsIgnoreCase(
- org.apache.derby.iapi.reference.Property.AUTHENTICATION_PROVIDER_LDAP)) ))))
+ org.apache.derby.iapi.reference.Property.AUTHENTICATION_PROVIDER_LDAP)) ))))
return true;
else
return false;
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/BaseMonitor.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/BaseMonitor.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/BaseMonitor.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/BaseMonitor.java Thu Jan 19 13:51:13 2012
@@ -392,6 +392,17 @@ abstract class BaseMonitor
bootPersistentServices( );
}
+ public String getCanonicalServiceName( String userSpecifiedName )
+ throws StandardException
+ {
+ if ( userSpecifiedName == null ) { return null; }
+
+ PersistentService correspondingService = findProviderForCreate( userSpecifiedName );
+
+ if ( correspondingService == null ) { return null; }
+ else { return correspondingService.getCanonicalServiceName( userSpecifiedName ); }
+ }
+
public Object findService(String factoryInterface, String serviceName) {
if (serviceName == null)
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java Thu Jan 19 13:51:13 2012
@@ -522,7 +522,7 @@ public final class DataDictionaryImpl
throws StandardException
{
softwareVersion = new DD_Version(this, DataDictionary.DD_VERSION_DERBY_10_9);
-
+
startupParameters = startParams;
uuidFactory = Monitor.getMonitor().getUUIDFactory();
@@ -807,16 +807,22 @@ public final class DataDictionaryImpl
DataDictionary.CREATE_DATA_DICTIONARY_VERSION,
dictionaryVersion, true);
- // If SqlAuthorization is set as system property during database
- // creation, set it as database property also, so it gets persisted.
- if (PropertyUtil.getSystemBoolean(Property.SQL_AUTHORIZATION_PROPERTY))
+ boolean nativeAuthenticationEnabled = PropertyUtil.nativeAuthenticationEnabled( startParams );
+
+ //
+ // If SqlAuthorization is set as a system property during database
+ // creation, set it as a database property also, so that it gets persisted.
+ //
+ // We also turn on SqlAuthorization if NATIVE authentication has been specified.
+ //
+ if (PropertyUtil.getSystemBoolean(Property.SQL_AUTHORIZATION_PROPERTY) || nativeAuthenticationEnabled)
{
bootingTC.setProperty(Property.SQL_AUTHORIZATION_PROPERTY,"true",true);
usesSqlAuthorization=true;
}
// Set default hash algorithm used to protect passwords stored
- // in the database for BUILTIN authentication.
+ // in the database for BUILTIN and NATIVE authentication.
bootingTC.setProperty(
Property.AUTHENTICATION_BUILTIN_ALGORITHM,
findDefaultBuiltinAlgorithm(),
@@ -7920,6 +7926,28 @@ public final class DataDictionaryImpl
);
}
+ public UserDescriptor getUser( String userName, TransactionController tc )
+ throws StandardException
+ {
+ ExecIndexRow keyRow;
+ TabInfoImpl ti = getNonCoreTI( SYSUSERS_CATALOG_NUM );
+
+ /* Set up the start/stop position for the scan */
+ keyRow = (ExecIndexRow) exFactory.getIndexableRow(1);
+ keyRow.setColumn( 1, new SQLVarchar( userName ) );
+
+ return (UserDescriptor) getDescriptorViaIndex
+ (
+ SYSUSERSRowFactory.SYSUSERS_INDEX1_ID,
+ keyRow,
+ (ScanQualifier [][]) null,
+ ti,
+ (TupleDescriptor) null,
+ (List) null,
+ false
+ );
+ }
+
public void dropUser( String userName, TransactionController tc )
throws StandardException
{
Modified: db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml Thu Jan 19 13:51:13 2012
@@ -1239,6 +1239,17 @@ Guide.
</msg>
<msg>
+ <name>4251H</name>
+ <text>Invalid NATIVE authentication specification. Please set derby.authentication.provider to a value of the form NATIVE:$credentialsDB or NATIVE:$credentialsDB:LOCAL (at the system level) or NATIVE::LOCAL (at the database level).</text>
+ </msg>
+
+ <msg>
+ <name>4251I</name>
+ <text>Authentication cannot be performed because the credentials database '{0}' does not exist.</text>
+ <arg>databaseName</arg>
+ </msg>
+
+ <msg>
<name>42601</name>
<text>In an ALTER TABLE statement, the column '{0}' has been specified as NOT NULL and either the DEFAULT clause was not specified or was specified as DEFAULT NULL.</text>
<arg>columnName</arg>
Modified: db/derby/code/trunk/java/engine/org/apache/derby/modules.properties
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/modules.properties?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/modules.properties (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/modules.properties Thu Jan 19 13:51:13 2012
@@ -205,6 +205,9 @@ cloudscape.config.NoneAuthentication=all
# Authentication Service - Various Authentication Services/Schemes
# (activated by derby.connection.requireAuthentication)
#
+derby.module.nativeAuthentication=org.apache.derby.impl.jdbc.authentication.NativeAuthenticationServiceImpl
+cloudscape.config.nativeAuthentication=derby
+
derby.module.basicAuthentication=org.apache.derby.impl.jdbc.authentication.BasicAuthenticationServiceImpl
cloudscape.config.basicAuthentication=derby
Modified: db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java (original)
+++ db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java Thu Jan 19 13:51:13 2012
@@ -780,6 +780,8 @@ public interface SQLState {
String HIDDEN_COLUMN = "4251E";
String CANT_DROP_DBO = "4251F";
String WEAK_AUTHENTICATION = "4251G";
+ String BAD_NATIVE_AUTH_SPEC = "4251H";
+ String MISSING_CREDENTIALS_DB = "4251I";
String LANG_DB2_NOT_NULL_COLUMN_INVALID_DEFAULT = "42601";
String LANG_DB2_INVALID_HEXADECIMAL_CONSTANT = "42606";
Modified: db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java (original)
+++ db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java Thu Jan 19 13:51:13 2012
@@ -684,10 +684,17 @@ public class EmptyDictionary implements
// TODO Auto-generated method stub
}
- public void dropUser( String userName, TransactionController tc )
+ public void dropUser( String userName,TransactionController tc )
+ throws StandardException
+ {
+ // TODO Auto-generated method stub
+ }
+
+ public UserDescriptor getUser( String userName, TransactionController tc )
throws StandardException
{
// TODO Auto-generated method stub
+ return null;
}
public int getEngineType() {
Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/NativeAuthenticationServiceTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/NativeAuthenticationServiceTest.java?rev=1233377&view=auto
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/NativeAuthenticationServiceTest.java (added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/NativeAuthenticationServiceTest.java Thu Jan 19 13:51:13 2012
@@ -0,0 +1,386 @@
+/*
+
+ Derby - Class org.apache.derbyTesting.functionTests.tests.lang.NativeAuthenticationServiceTest
+
+ 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.derbyTesting.functionTests.tests.lang;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.derbyTesting.junit.BaseJDBCTestCase;
+import org.apache.derbyTesting.junit.DatabaseChangeSetup;
+import org.apache.derbyTesting.junit.JDBC;
+import org.apache.derbyTesting.junit.TestConfiguration;
+import org.apache.derbyTesting.junit.SystemPropertyTestSetup;
+
+/**
+ * <p>
+ * Tests for the NATIVE authentication service introduced by DERBY-866.
+ * </p>
+ */
+public class NativeAuthenticationServiceTest extends GeneratedColumnsHelper
+{
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // CONSTANTS
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ // fruits are legal users. nuts are not
+ private static final String DBO = "KIWI";
+ private static final String APPLE_USER = "APPLE";
+ private static final String PEAR_USER = "PEAR";
+
+ private static final String WALNUT_USER = "WALNUT";
+
+ private static final String CREDENTIALS_DB = "credDB";
+ private static final String SECOND_DB = "secondDB";
+ private static final String THIRD_DB = "thirdDB";
+
+ private static final String PROVIDER_PROPERTY = "derby.authentication.provider";
+
+ private static final String CREDENTIALS_DB_DOES_NOT_EXIST = "4251I";
+ private static final String INVALID_AUTHENTICATION = "08004";
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // STATE
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ private final boolean _nativeAuthentication;
+ private final boolean _localAuthentication;
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // CONSTRUCTOR
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ public NativeAuthenticationServiceTest
+ (
+ boolean nativeAuthentication,
+ boolean localAuthentication
+ )
+ {
+ super( "testAll" );
+
+ _nativeAuthentication = nativeAuthentication;
+ _localAuthentication = localAuthentication;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // SETUP BEHAVIOR
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * <p>
+ * Return the system properties to be used in a particular test run.
+ * </p>
+ */
+ private Properties systemProperties( String physicalDatabaseName )
+ {
+ if ( !_nativeAuthentication ) { return null; }
+
+ String authenticationProvider = "NATIVE:" + physicalDatabaseName;
+ if ( _localAuthentication ) { authenticationProvider = authenticationProvider + ":LOCAL"; }
+
+ Properties result = new Properties();
+ result.put( PROVIDER_PROPERTY, authenticationProvider );
+
+ return result;
+ }
+
+ /**
+ * <p>
+ * Construct the name of this test (useful for error messages).
+ * </p>
+ */
+ private String nameOfTest()
+ {
+ String authType = _nativeAuthentication ?
+ "NATIVE authentication on, " :
+ "Authentication off, ";
+ String local = _localAuthentication ?
+ "LOCAL authentication ON" :
+ "LOCAL authentication OFF";
+
+ return "[ " + authType + local + " ]";
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // JUnit BEHAVIOR
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Construct top level suite in this JUnit test
+ */
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite();
+
+ suite.addTest( allConfigurations( false ) );
+ if ( !JDBC.vmSupportsJSR169() ) { suite.addTest( allConfigurations( true ) ); }
+
+ return suite;
+ }
+
+ /**
+ * <p>
+ * Create a suite of all test configurations.
+ * </p>
+ */
+ private static Test allConfigurations( boolean clientServer )
+ {
+ TestSuite suite = new TestSuite();
+
+ suite.addTest( decorate( new NativeAuthenticationServiceTest( false, false ), clientServer ) );
+ suite.addTest( decorate( new NativeAuthenticationServiceTest( true, true ), clientServer ) );
+ suite.addTest( decorate( new NativeAuthenticationServiceTest( true, false ), clientServer ) );
+
+ return suite;
+ }
+
+ /**
+ * <p>
+ * Wrap base test with standard decorators in order to setup system
+ * properties and allow for the creation of multiple databases with
+ * stored properties that can't be removed at tearDown time.
+ * </p>
+ */
+ private static Test decorate( NativeAuthenticationServiceTest nast, boolean clientServer )
+ {
+ String credentialsDBPhysicalName = TestConfiguration.generateUniqueDatabaseName();
+
+ Test result = nast;
+
+ //
+ // Putting the clientServer decorator on the inside allows the server-side
+ // embedded driver to be re-registered after engine shutdown. If you put
+ // this decorator outside the SystemProperty decorator, then engine shutdown
+ // unregisters the server-side embedded driver and it can't be found by
+ // the next test.
+ //
+ if ( clientServer ) { result = TestConfiguration.clientServerDecorator( result ); }
+
+ //
+ // Turn on the property which enables NATIVE authentication. This will trigger
+ // an engine shutdown at the end of the test. We want to shutdown the engine
+ // before deleting the physical databases. This is because we need one of the
+ // databases (the credentials db) in order to authenticate engine shutdown.
+ //
+ Properties systemProperties = nast.systemProperties( credentialsDBPhysicalName );
+ if ( systemProperties != null )
+ {
+ result = new SystemPropertyTestSetup( result, systemProperties, true );
+ }
+
+ //
+ // Register temporary databases, where the test will do its work.
+ // We can't use the default, re-usable database because NATIVE authentication stores
+ // persistent properties which cannot be turned off.
+ //
+ result = TestConfiguration.additionalDatabaseDecoratorNoShutdown
+ ( result, CREDENTIALS_DB, credentialsDBPhysicalName );
+ result = TestConfiguration.additionalDatabaseDecoratorNoShutdown( result, SECOND_DB );
+ result = TestConfiguration.additionalDatabaseDecoratorNoShutdown( result, THIRD_DB );
+
+ result = TestConfiguration.changeUserDecorator( result, DBO, getPassword( DBO ) );
+
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // TESTS
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * <p>
+ * Entry point for tests.
+ * </p>
+ */
+ public void testAll() throws Exception
+ {
+ println( nameOfTest() );
+
+ // can't create any database until the credentials db has been created
+ Connection secondDBConn = createDB
+ ( _nativeAuthentication, SECOND_DB, APPLE_USER, CREDENTIALS_DB_DOES_NOT_EXIST );
+
+ // create the credentials database
+ Connection sysadminConn = openConnection( CREDENTIALS_DB, DBO );
+
+ // add another legal user
+ addUser( sysadminConn, APPLE_USER );
+
+ //
+ // Creating the credentials db should have stored the following information in it:
+ //
+ // 1) The DBO's credentials should have been stored in SYSUSERS.
+ // 2) The authentication provider should have been set to NATIVE::LOCAL
+ // 3) SQL authorization should have been turned on.
+ //
+ String[][] legalUsers = _nativeAuthentication ? new String[][] { { APPLE_USER }, { DBO } } : new String[][] { { APPLE_USER } };
+ assertResults
+ (
+ sysadminConn,
+ "select username from sys.sysusers order by username",
+ legalUsers,
+ false
+ );
+ String[][] authenticationProvider = _nativeAuthentication ? new String[][] { { "NATIVE::LOCAL" } } : new String[][] { { null } };
+ assertResults
+ (
+ sysadminConn,
+ "values ( syscs_util.syscs_get_database_property( 'derby.authentication.provider' ) )",
+ authenticationProvider,
+ false
+ );
+ String[][] sqlAuthorization = _nativeAuthentication ? new String[][] { { "true" } } : new String[][] { { null } };
+ assertResults
+ (
+ sysadminConn,
+ "values ( syscs_util.syscs_get_database_property( 'derby.database.sqlAuthorization' ) )",
+ sqlAuthorization,
+ false
+ );
+
+ // Sanity-check that the creator of the credentials db is the DBO
+ String[][] dboName = new String[][] { { DBO } };
+ assertResults
+ (
+ sysadminConn,
+ "select authorizationID from sys.sysschemas where schemaName = 'SYS'",
+ dboName,
+ false
+ );
+
+ // Databases can't be created by users who don't have credentials stored in the credentials database
+ Connection thirdDBConn = createDB
+ ( _nativeAuthentication, THIRD_DB, WALNUT_USER, INVALID_AUTHENTICATION );
+
+ // Now let the other valid user create a database
+ if ( secondDBConn == null )
+ {
+ secondDBConn = createDB( false, SECOND_DB, APPLE_USER, null );
+ }
+
+ // verify that the other valid user is the dbo in the database he just created
+ assertResults
+ (
+ secondDBConn,
+ "select authorizationID from sys.sysschemas where schemaName = 'SYS'",
+ new String[][] { { APPLE_USER } },
+ false
+ );
+
+ // NATIVE authentication turns on SQL authorization in the second database
+ assertResults
+ (
+ secondDBConn,
+ "values ( syscs_util.syscs_get_database_property( 'derby.database.sqlAuthorization' ) )",
+ sqlAuthorization,
+ false
+ );
+
+ //
+ // If LOCAL authentication was specified...
+ //
+ // 1) It will be turned on in the second database too.
+ // 2) The other legal user's credentials (as the database dbo) will be stored.
+ //
+ authenticationProvider = _localAuthentication ? new String[][] { { "NATIVE::LOCAL" } } : new String[][] { { null } };
+ assertResults
+ (
+ secondDBConn,
+ "values ( syscs_util.syscs_get_database_property( 'derby.authentication.provider' ) )",
+ authenticationProvider,
+ false
+ );
+ legalUsers = _localAuthentication ? new String[][] { { APPLE_USER } } : new String[][] {};
+ assertResults
+ (
+ secondDBConn,
+ "select username from sys.sysusers order by username",
+ legalUsers,
+ false
+ );
+
+ }
+
+ private Connection createDB( boolean shouldFail, String dbName, String user, String expectedSQLState )
+ throws Exception
+ {
+ Connection conn = null;
+
+ println( user + " attempting to create database " + dbName );
+
+ try {
+ conn = openConnection( dbName, user );
+
+ if ( shouldFail ) { fail( tagError( "Connection to " + dbName + " should have failed." ) ); }
+ }
+ catch (SQLException se)
+ {
+ if ( shouldFail ) { assertSQLState( expectedSQLState, se ); }
+ else { fail( tagError( "Connection to " + dbName + " unexpectedly succeeded." ) );}
+ }
+
+ return conn;
+ }
+
+ private void addUser( Connection conn, String user ) throws Exception
+ {
+ String password = getPassword( user );
+ String statement = "call syscs_util.syscs_create_user( '" + user + "', '" + password + "' )";
+
+ goodStatement( conn, statement );
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+ // MINIONS
+ //
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ /** Open a connection to a database using the supplied credentials */
+ private Connection openConnection( String logicalDBName, String user )
+ throws SQLException
+ {
+ return getTestConfiguration().openConnection( logicalDBName, user, getPassword( user ) );
+ }
+
+ /** Get the password for a user */
+ private static String getPassword( String user ) { return user + "_password"; }
+
+ /** Tag an error with the name of the test configuration */
+ private String tagError( String text ) { return nameOfTest() + ": " + text; }
+
+}
Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/NativeAuthenticationServiceTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/_Suite.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/_Suite.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/_Suite.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/_Suite.java Thu Jan 19 13:51:13 2012
@@ -221,6 +221,7 @@ public class _Suite extends BaseTestCase
suite.addTest(Derby5005Test.suite());
suite.addTest(AutoIncrementTest.suite());
suite.addTest(HalfCreatedDatabaseTest.suite());
+ suite.addTest(NativeAuthenticationServiceTest.suite());
suite.addTest(NativeAuthProcs.suite());
return suite;
}
Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java Thu Jan 19 13:51:13 2012
@@ -260,7 +260,7 @@ public abstract class BaseTestCase
*
* @param name name of the property
*/
- protected static void removeSystemProperty(final String name)
+ public static void removeSystemProperty(final String name)
{
AccessController.doPrivileged
Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DatabaseChangeSetup.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DatabaseChangeSetup.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DatabaseChangeSetup.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DatabaseChangeSetup.java Thu Jan 19 13:51:13 2012
@@ -26,7 +26,7 @@ import junit.framework.Test;
* Previous configuration is restored on tearDown.
*
*/
-final class DatabaseChangeSetup extends ChangeConfigurationSetup {
+public final class DatabaseChangeSetup extends ChangeConfigurationSetup {
private final String logicalDbName;
private final String dbName;
@@ -42,4 +42,7 @@ final class DatabaseChangeSetup extends
TestConfiguration getNewConfiguration(TestConfiguration old) {
return new TestConfiguration(old, logicalDbName, dbName, defaultDb);
}
+
+ public String physicalDatabaseName() { return dbName; }
+
}
Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DriverManagerConnector.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DriverManagerConnector.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DriverManagerConnector.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DriverManagerConnector.java Thu Jan 19 13:51:13 2012
@@ -80,7 +80,7 @@ public class DriverManagerConnector impl
try {
return DriverManager.getConnection(url, connectionAttributes);
} catch (SQLException e) {
-
+
// Expected state for database not found.
// For the client the generic 08004 is returned,
// will just retry on that.
@@ -95,6 +95,7 @@ public class DriverManagerConnector impl
Properties attributes = new Properties(connectionAttributes);
attributes.setProperty("create", "true");
+
return DriverManager.getConnection(url, attributes);
}
}
@@ -117,7 +118,7 @@ public class DriverManagerConnector impl
*/
public void shutEngine() throws SQLException {
- getConnectionByAttributes("jdbc:derby:", "shutdown", "true");
+ getConnectionByAttributes("jdbc:derby:", "shutdown", "true");
}
/**
@@ -129,7 +130,7 @@ public class DriverManagerConnector impl
throws SQLException
{
Properties attributes = new Properties();
-
+
attributes.setProperty("user", config.getUserName());
attributes.setProperty("password", config.getUserPassword());
attributes.setProperty(key, value);
Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DropDatabaseSetup.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DropDatabaseSetup.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DropDatabaseSetup.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DropDatabaseSetup.java Thu Jan 19 13:51:13 2012
@@ -54,12 +54,13 @@ class DropDatabaseSetup extends BaseTest
config.openConnection(logicalDBName).close();
shutdown = true;
} catch (SQLException e) {
+ String sqlState = e.getSQLState();
// If the database cannot be booted due
// to some restrictions such as authentication
// or encrypted (ie here we don't know the
// correct authentication tokens, then it's
// ok since we just want it shutdown anyway!
- if ("XJ040".equals(e.getSQLState()))
+ if ( "XJ040".equals( sqlState ) || "08004".equals( sqlState ) || "4251I".equals( sqlState ) )
{
shutdown = false;
}
Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/NetworkServerTestSetup.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/NetworkServerTestSetup.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/NetworkServerTestSetup.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/NetworkServerTestSetup.java Thu Jan 19 13:51:13 2012
@@ -31,6 +31,7 @@ import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
+import java.sql.SQLException;
import java.util.ArrayList;
import junit.framework.Test;
import org.apache.derby.drda.NetworkServerControl;
@@ -464,9 +465,20 @@ final public class NetworkServerTestSetu
if (failedShutdown != null)
{
if (failedShutdown instanceof Exception)
- throw (Exception) failedShutdown;
-
- throw (Error) failedShutdown;
+ {
+ // authentication failure is ok.
+ if (
+ !(failedShutdown instanceof SQLException) ||
+ !( "4251I".equals( ((SQLException) failedShutdown).getSQLState() ) )
+ )
+ {
+ throw (Exception) failedShutdown;
+ }
+ }
+ else
+ {
+ throw (Error) failedShutdown;
+ }
}
}
Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/SystemPropertyTestSetup.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/SystemPropertyTestSetup.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/SystemPropertyTestSetup.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/SystemPropertyTestSetup.java Thu Jan 19 13:51:13 2012
@@ -77,10 +77,14 @@ public class SystemPropertyTestSetup ext
protected void setUp()
throws java.lang.Exception
{
- setProperties(newValues);
// shutdown engine so static properties take effect
+ // shutdown the engine before setting the properties. this
+ // is because the properties may change authentication settings
+ // to NATIVE authentication and we may be missing a credentials DB.
if (staticProperties)
- TestConfiguration.getCurrent().shutdownEngine();
+ { TestConfiguration.getCurrent().shutdownEngine(); }
+
+ setProperties(newValues);
}
/**
Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/TestConfiguration.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/TestConfiguration.java?rev=1233377&r1=1233376&r2=1233377&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/TestConfiguration.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/TestConfiguration.java Thu Jan 19 13:51:13 2012
@@ -669,7 +669,7 @@ public final class TestConfiguration {
/**
* Generate the unique database name for single use.
*/
- private static synchronized String generateUniqueDatabaseName()
+ public static synchronized String generateUniqueDatabaseName()
{
// Forward slash is ok, Derby treats database names
// as URLs and translates forward slash to the local
@@ -766,7 +766,7 @@ public final class TestConfiguration {
* the database in openConnection(String logicalDatabaseName) method calls.
* @return decorated test.
*/
- public static TestSetup additionalDatabaseDecorator(Test test, String logicalDbName)
+ public static DatabaseChangeSetup additionalDatabaseDecorator(Test test, String logicalDbName)
{
return new DatabaseChangeSetup(new DropDatabaseSetup(test, logicalDbName),
logicalDbName,
@@ -786,7 +786,7 @@ public final class TestConfiguration {
* method calls.
* @return decorated test.
*/
- public static TestSetup additionalDatabaseDecoratorNoShutdown(
+ public static DatabaseChangeSetup additionalDatabaseDecoratorNoShutdown(
Test test,
String logicalDbName)
{
@@ -804,6 +804,36 @@ public final class TestConfiguration {
}
/**
+ * Similar to additionalDatabaseDecorator except the database will
+ * not be shutdown, only deleted. It is the responsibility of the
+ * test to shut it down.
+ *
+ * @param test Test to be decorated
+ * @param logicalDbName The logical database name. This name is
+ * used to identify the database in
+ * openConnection(String logicalDatabaseName)
+ * method calls.
+ * @param physicalDbName - Real database name on disk.
+ * @return decorated test.
+ */
+ public static DatabaseChangeSetup additionalDatabaseDecoratorNoShutdown(
+ Test test,
+ String logicalDbName, String physicalDbName )
+ {
+ return new DatabaseChangeSetup(
+ new DropDatabaseSetup(test, logicalDbName)
+ {
+ protected void tearDown() throws Exception {
+ // the test is responsible for shutdown
+ removeDatabase();
+ }
+ },
+ logicalDbName,
+ physicalDbName,
+ false);
+ }
+
+ /**
* Decorate a test changing the default user name and password.
* Typically used along with DatabasePropertyTestSetup.builtinAuthentication.
* The tearDown method resets the default user and password value to
@@ -1381,8 +1411,7 @@ public final class TestConfiguration {
public JDBCClient getJDBCClient() {
return jdbcClient;
}
-
-
+
/**
* <p>
* Return the jdbc url for connecting to the default database.
@@ -1597,14 +1626,43 @@ public final class TestConfiguration {
* @return connection to specified database.
*/
Connection openConnection(String logicalDatabaseName)
- throws SQLException {
- String databaseName = getPhysicalDatabaseName(logicalDatabaseName);
- if (usedDbNames.contains(databaseName))
- return connector.openConnection(databaseName);
+ throws SQLException
+ {
+ return connector.openConnection( getAndVetPhysicalDatabaseName( logicalDatabaseName ) );
+ }
+ private String getAndVetPhysicalDatabaseName( String logicalDatabaseName )
+ throws SQLException
+ {
+ String databaseName = getPhysicalDatabaseName( logicalDatabaseName );
+
+ if ( usedDbNames.contains(databaseName) ) { return databaseName; }
else
+ {
throw new SQLException("Database name \"" + logicalDatabaseName
+ "\" is not in a list of used databases."
+ "Use method TestConfiguration.additionalDatabaseDecorator first.");
+ }
+ }
+
+ /**
+ * Open connection to the specified database using the supplied username and password.
+ * If the database does not exist, it will be created.
+ * Requires that the test has been decorated with
+ * additionalDatabaseDecorator with the matching name.
+ * The physical database name may differ.
+ * @param logicalDatabaseName A logical database name as passed
+ * to <code>additionalDatabaseDecorator</code> function.
+ * @return connection to specified database.
+ */
+ public Connection openConnection( String logicalDatabaseName, String user, String password )
+ throws SQLException
+ {
+ return connector.openConnection
+ (
+ getAndVetPhysicalDatabaseName( logicalDatabaseName ),
+ user,
+ password
+ );
}
/**