File: LDAPAuthenticationTest.java

package info (click to toggle)
derby 10.14.2.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 78,896 kB
  • sloc: java: 691,930; sql: 42,686; xml: 20,511; sh: 3,373; sed: 96; makefile: 60
file content (362 lines) | stat: -rw-r--r-- 16,289 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
/*

   Derby - Class 
       org.apache.derbyTesting.functionTests.tests.jdbcapi.LDAPAuthenticationTest

   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.jdbcapi;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import junit.framework.Test;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.DatabasePropertyTestSetup;
import org.apache.derbyTesting.junit.JDBC;
import org.apache.derbyTesting.junit.JDBCDataSource;
import org.apache.derbyTesting.junit.SecurityManagerSetup;
import org.apache.derbyTesting.junit.TestConfiguration;

// tests that appropriate and invalid connections can be made to 
// an LDAPServer when property authenticationProvider is set to 'LDAP'.
// This test assumes at least one valid user (to be passed in) and one
// additional user (kathy / kathyS) to be setup in ou=People on the
// LDAPServer.
public class LDAPAuthenticationTest extends BaseJDBCTestCase {

    private static String ldapServer;
    private static String ldapPort;
    private static String dnString;
    private static String ldapUser; // existing valid user on ldap server
    private static String ldapPassword; // password for existing valid user on ldap server
    private static String ldapContextFactory; // optional initial context factory
        // if not passed in with -DderbyTesting.ldapContextFactory, uses sun's

    // create own policy file, so we can connect to the ldap server
    private static final String POLICY_FILE_NAME =
        "org/apache/derbyTesting/functionTests/tests/jdbcapi/LDAPTests.policy";
    
    /** Creates a new instance of the Test */
    public LDAPAuthenticationTest(String name) {
        super(name);
    }

    /**
     * Ensure all connections are not in auto commit mode.
     */
    protected void initializeConnection(Connection conn) throws SQLException {
        conn.setAutoCommit(false);
    }

    public static Test suite() {
        if (JDBC.vmSupportsJSR169())
            return new BaseTestSuite(
                "cannot run with JSR169 - missing functionality" +
                " for org.apache.derby.iapi.jdbc.AuthenticationService");

        ldapUser=getSystemProperty("derbyTesting.ldapUser");

        if (ldapUser == null || ldapUser.length() < 1)
            return new BaseTestSuite(
                "LDAPAuthenticationTest requires property " +
                "derbyTesting.ldapUser set to a valid user set up on the " +
                "ldapServer, eg: -DderbyTesting.ldapUser=CharliesPwd. In addition," +
                "test requires a user 'kathy', pwd 'kathyS' to be set up");

        ldapPassword=getSystemProperty("derbyTesting.ldapPassword");

        if (ldapPassword == null || ldapPassword.length() < 1)
            return new BaseTestSuite(
                "LDAPAuthenticationTest requires property " +
                "derbyTesting.ldapPassword set the password of a valid user set" +
                " up on the ldapServer, eg: -DderbyTesting.ldapPassword=Charlie");

        ldapServer=getSystemProperty("derbyTesting.ldapServer");

        if (ldapServer == null || ldapServer.length() < 1)
            return new BaseTestSuite(
                "LDAPAuthenticationTest requires property " +
                "derbyTesting.ldapServer set, eg: " +
                "-DderbyTesting.ldapServer=myldapserver.myorg.org");

        ldapPort=getSystemProperty("derbyTesting.ldapPort");

        if (ldapPort == null || ldapPort.length() < 1)
            return new BaseTestSuite(
                "LDAPAuthenticationTest requires property " +
                "derbyTesting.ldapPort set, eg: -DderbyTesting.ldapPort=333");

        dnString=getSystemProperty("derbyTesting.dnString");

        if (dnString == null || dnString.length() < 1)
            return new BaseTestSuite(
                "LDAPAuthenticationTest requires property " +
                "derbyTesting.dnString for setting o=, eg: " +
                "-DderbyTesting.dnString=myJNDIstring");

        ldapContextFactory=getSystemProperty("derbyTesting.ldapContextFactory");

        BaseTestSuite suite = new BaseTestSuite("LDAPAuthenticationTest");
        suite.addTest(baseSuite("LDAPAuthenticationTest:embedded",
            "testLDAPConnection"));
        suite.addTest(TestConfiguration.clientServerDecorator(
            baseSuite("LDAPAuthenticationTest:client", "testLDAPConnection")));

        // Grant ALL FILES execute, and getPolicy permissions, as well as
        // resolve/connect for the LDAP server identified with the property.
        return new SecurityManagerSetup(suite, POLICY_FILE_NAME);
    }

    public static Test baseSuite(String name, String fixture) {
        BaseTestSuite suite = new BaseTestSuite(name);
        Test test = new LDAPAuthenticationTest(fixture);
        setBaseProps(suite, test);

        // This test needs to run in a new single use database as we're setting
        // a number of properties
        return TestConfiguration.singleUseDatabaseDecorator(suite);
    }

    protected static void setBaseProps(BaseTestSuite suite, Test test)
    {
        // set some debugging at database level properties
        Properties props = new Properties();
        props.setProperty("derby.infolog.append", "true");
        props.setProperty("derby.debug.true", "AuthenticationTrace");
        // add some users. these should not be defined on the ldap server
        props.setProperty("derby.user.system", "manager");
        props.setProperty("derby.user.Jamie", "theHooligan");
        suite.addTest(new DatabasePropertyTestSetup (test, props, true));
    }

    protected void tearDown() throws Exception {
        removeSystemProperty("derby.connection.requireAuthentication");
        super.tearDown();
    }

    protected void setDatabaseProperty(
            String propertyName, String value, Connection conn) 
    throws SQLException {
        CallableStatement setDBP =  conn.prepareCall(
        "CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY(?, ?)");
        setDBP.setString(1, propertyName);
        setDBP.setString(2, value);
        setDBP.execute();
        setDBP.close();
    }

    protected String getDatabaseProperty(
            String propertyName, Connection conn) 
    throws SQLException {
        PreparedStatement getDBP =  conn.prepareStatement(
        "VALUES SYSCS_UTIL.SYSCS_GET_DATABASE_PROPERTY(?)");
        getDBP.setString(1, propertyName);
        ResultSet rs = getDBP.executeQuery();
        rs.next();
        String value = rs.getString(1);
        rs.close();
        return value;
    }    
     
    public void testLDAPConnection() throws SQLException {
        // setup 
        String dbName = TestConfiguration.getCurrent().getDefaultDatabaseName();
        DataSource ds = JDBCDataSource.getDataSource();
        Connection conn = ds.getConnection("system", "admin");
        // set the ldap properties
        setDatabaseProperty("derby.connection.requireAuthentication", "true", conn);
        setDatabaseProperty("derby.authentication.provider", "LDAP", conn);
        setDatabaseProperty("derby.authentication.server", ldapServer, conn);
        setDatabaseProperty("derby.authentication.ldap.searchBase", "o=" + dnString, conn);
        setDatabaseProperty("derby.authentication.ldap.searchFilter","(&(objectClass=inetOrgPerson)(uid=%USERNAME%))", conn);
        // java.naming.factory.initial is Context.INITIAL_CONTEXT_FACTORY
        // but using literal string here to avoid unnecessary import.
        // If the initial context factory is not provided it'll default to 
        // com.sun.jndi.ldap.LdapCtxFactory in LDAPAuthenticationSchemeImpl.
        if ((ldapContextFactory != null) && (ldapContextFactory.length() > 0))
            setDatabaseProperty("java.naming.factory.initial", ldapContextFactory, conn);
        commit();
        // shutdown the database as system, so the properties take effect
        TestConfiguration.getCurrent().shutdownDatabase();
        conn.close();

        // actual test. 
        // first attempt simple connection that should succeed
        ds = JDBCDataSource.getDataSource(dbName);
        assertLDAPDSConnectionOK(ds, ldapUser, ldapPassword);
        assertLDAPDrvMgrConnectionOK(dbName, ldapUser, ldapPassword);
        // try to get a connection for a user that has been added, but who
        // should *not* be on the ldap server; should fail
        assertInvalidLDAPDSConnectionFails(ds, "Jamie", "theHooligan");
        assertInvalidLDAPDrvMgrConnectionFails(dbName, "Jamie", "thHooligan");
        // try to get a connection using the valid ldapuser, but incorrect pwd
        assertInvalidLDAPDSConnectionFails(ds, ldapUser, ldapPassword + "ish");
        assertInvalidLDAPDrvMgrConnectionFails(dbName, ldapUser, ldapPassword + "ish");

        // set the users DN locally
        Connection conn3 = ds.getConnection(ldapUser, ldapPassword);
        String tmpString1 = "derby.user." + ldapUser;
        String tmpString2 = "uid=" + ldapUser + ",ou=People,o=" + dnString; 
        setDatabaseProperty(tmpString1, tmpString2, conn3);
        conn3.commit();
        // restart to let setting take effect
        JDBCDataSource.setBeanProperty(ds, "shutdownDatabase", "shutdown");
        // shutdown really only happens on next attempt to connect
        try {
            ds.getConnection(ldapUser, ldapPassword);
            fail("expected system shutdown resulting in 08006 error.");
        } catch (SQLException e) {
            assertSQLState("08006", e);
        }
        conn3.close();
        
        // It is now required to re-establish the name
        // of the database...
        dbName = TestConfiguration.getCurrent().getDefaultDatabaseName();
        ds = JDBCDataSource.getDataSource(dbName);
        // test again
        // connect using a valid ldap user/pwd
        assertLDAPDSConnectionOK(ds, ldapUser, ldapPassword);
        assertLDAPDrvMgrConnectionOK(dbName, ldapUser, ldapPassword);
        // try to use a not specified elsewhere, but hopefully valid ldapUser:
        // as no local DN is cached, look-up will be performed with the default
        // search filter.
        assertLDAPDSConnectionOK(ds, "kathy", "kathyS");
        assertLDAPDrvMgrConnectionOK(dbName, "kathy", "kathyS");
        // again try to get a connection for a user that has been added, but
        // should *not* be on the ldap server; should fail
        assertInvalidLDAPDSConnectionFails(ds, "Jamie", "theHooligan");
        assertInvalidLDAPDrvMgrConnectionFails(dbName, "Jamie", "theHooligan");
        // try to get a connection using the valid ldapuser, but incorrect pwd
        assertInvalidLDAPDSConnectionFails(ds, ldapUser, ldapPassword + "ish");
        assertInvalidLDAPDrvMgrConnectionFails(dbName, ldapUser, ldapPassword + "ish");
        
        assertLDAPDSConnectionOK(ds, ldapUser, ldapPassword);
        // as we don't have requireAuthentication, or the authentication provider,
        // set on system level, we can shutdown the system with any user.
        assertDSSystemShutdownOK("someuser", "somestring");
        
        // cleanup. Can't use default teardown mechanism yet as it uses default
        // user/password and we're set to LDAP Authentication at this point.
        cleanup(ds);
    }

    // cleanup gets a conn using the ldap user & pwd, so we can unset the
    // requireAuthentication so the default teardown & db cleanup can happen
    protected void cleanup(DataSource ds) throws SQLException {
        Connection conn2 = ds.getConnection(ldapUser, ldapPassword);
        setDatabaseProperty("derby.connection.requireAuthentication", "false", conn2);
        conn2.commit();
        JDBCDataSource.setBeanProperty(ds, "shutdownDatabase", "shutdown");
        // shutdown only really happens on attempting to get a connection;
        // this exercise is needed so the DatabasePropertySetup.teardown() can work.
        // Getting a 08006 is not really part of the test, but would be good
        // to be warned of if it happens.
        try {
            ds.getConnection(ldapUser, ldapPassword);
            fail("expected system shutdown resulting in 08006 error.");
        } catch (SQLException e) {
            assertSQLState("08006", e);
        }
        conn2.close();
    }

    public void assertLDAPDSConnectionOK(
            DataSource ds, String user, String password)
    throws SQLException {
        Connection conn = ds.getConnection(user, password);
        assertNotNull(conn);
        conn.close();
    }

    protected void assertLDAPDrvMgrConnectionOK(
            String dbName, String user, String password)
    throws SQLException
    {
        String url = TestConfiguration.getCurrent().getJDBCUrl(dbName);
        Connection conn = DriverManager.getConnection(url, user, password);
        assertNotNull(conn);
        conn.close();
    }

    public void assertInvalidLDAPDSConnectionFails(
            DataSource ds, String user, String password)
    throws SQLException {
        try {
            ds.getConnection(user, password);
            fail("expected invalid connection error (for DataSource)");
        } catch (SQLException se) {
            assertSQLState("08004", se);
        }
    }

    protected void assertInvalidLDAPDrvMgrConnectionFails(
            String dbName, String user, String password)
    throws SQLException
    {
        String url = TestConfiguration.getCurrent().getJDBCUrl(dbName);
        try {
            DriverManager.getConnection(url, user, password).close();
            fail("expected invalid connection error (for DataSource)");
        } catch (SQLException se) {
            assertSQLState("08004", se);
        }
    }

    protected void assertDSSystemShutdownOK(
            String user, String password)
    throws SQLException {
        DataSource ds;
        if (usingEmbedded())
        {
            // we cannot use JDBCDataSource.getDataSource() (which uses the
            // default database name), unless we specifically clear the 
            // databaseName. Otherwise, only the database will be shutdown.
            // The alternative is to use jDBCDataSource.getDatasource(dbName),
            // where dbName is an empty string - this will in the current code
            // be interpreted as a system shutdown.
            ds = JDBCDataSource.getDataSource();
            JDBCDataSource.clearStringBeanProperty(ds, "databaseName");
        }
        else 
        {
            // With client, we cannot user clearStringBeanProperty on the  
            // databaseName, that will result in error 08001 - 
            // Required DataSource property databaseName not set.
            // So, we pass an empty string as databaseName, which the current
            // code interprets as a system shutdown.
            ds = JDBCDataSource.getDataSource("");
        }

        JDBCDataSource.setBeanProperty(ds, "shutdownDatabase", "shutdown");
        try {
            ds.getConnection(user, password);
            fail("expected system shutdown resulting in XJ015 error");
        } catch (SQLException e) {
            // expect XJ015, system shutdown, on successful shutdown
            assertSQLState("XJ015", e);
        }
    }
}