Как протестировать отозванный сертификат в соединениях на основе SSL с использованием LDAP-сервера в памяти (из UnboundID LDAP SDK для Java)? - PullRequest
0 голосов
/ 05 марта 2020

В своем приложении я использую библиотеку UnboundID LDAP SDK для Java (https://ldap.com/unboundid-ldap-sdk-for-java/).

Я пытаюсь написать тест, который будет точно проверять, как LDAPS / Соединения StartTLS работают для сертификатов, которые были правильно настроены, но когда запрос к серверу LDAP выполнен, они больше не действительны (например, ключи были скомпрометированы, и мы заменили его другим).

Я хотел проверить это поведение, используя интеграционные тесты с использованием сервера в памяти из UnboundID LDAP SDK - InMemoryDirectoryServer (https://docs.ldap.com/ldap-sdk/docs/javadoc/com/unboundid/ldap/listener/InMemoryDirectoryServer.html)

Я постараюсь описать в Пошаговое описание процесса производства:

  1. Мы создаем пул подключений к нашему серверу, используя действующий сертификат. У нас есть рабочий и настроенный объект LDAPConnectionPool (https://docs.ldap.com/ldap-sdk/docs/javadoc/com/unboundid/ldap/sdk/LDAPConnectionPool.html). Соединение работает через порт 636 с использованием протокола LDAPS или через порт 389 с использованием протокола StartTLS.
  2. На стороне сервера LDAP мы отзываем сертификат, потому что, например, ключи были скомпрометированы. Мы создаем новый сертификат. Соединение с LDAP на стороне нашего приложения все еще активно.
  3. Когда мы выполняем простой поиск из нашего приложения - ldapConnectionPool.search(someSearchRequest());, мы должны получить LDAPSearchException, сообщающий нам, что наше соединение недействительно.

Допустим, опция имитации LDAPConnectionPool не учитывается.

Конфигурация моего сервера в памяти выглядит следующим образом:

class InMemoryLDAPServer {

    private final static String LDAPS_LISTENER_NAME = "LDAPS";
    private final static String TLS_LISTENER_NAME = "TLS";
    private final static String BASE_DN = "dc=example,dc=com";
    private final static String LDIF_FILENAME = "ldap.ldif";

    private final InMemoryDirectoryServer directoryServer;

    private InMemoryLDAPServer(InMemoryDirectoryServer directoryServer) {
        this.directoryServer = directoryServer;
    }

    static InMemoryLDAPServer newSecureServer(final InMemoryOperationInterceptor interceptor) throws Exception {
        InMemoryDirectoryServerConfig config = createServerConfig(interceptor);
        return getEmbeddedLdapServer(config);
    }

    private static InMemoryLDAPServer getEmbeddedLdapServer(final InMemoryDirectoryServerConfig config) throws LDAPException {
        InMemoryDirectoryServer directoryServer = new InMemoryDirectoryServer(config);
        directoryServer.importFromLDIF(true, ldifFilename());
        directoryServer.startListening();
        return new InMemoryLDAPServer(directoryServer);
    }

    private static InMemoryDirectoryServerConfig createServerConfig(final InMemoryOperationInterceptor interceptor) throws LDAPException, GeneralSecurityException {
        final SSLUtil serverSSLUtil = new SSLUtil(KeyStoreStub.keyStores(), KeyStoreStub.trustStores());
        final SSLUtil clientSSLUtil = new SSLUtil(KeyStoreStub.trustStores());

        InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(BASE_DN);
        config.setSchema(null);
        config.setListenerConfigs(InMemoryListenerConfig.createLDAPSConfig(LDAPS_LISTENER_NAME,
                InetAddress.getLoopbackAddress(), 0, serverSSLUtil.createSSLServerSocketFactory(),
                clientSSLUtil.createSSLSocketFactory()), InMemoryListenerConfig.createLDAPConfig(TLS_LISTENER_NAME, InetAddress.getLoopbackAddress(), 0, serverSSLUtil.createSSLSocketFactory()));
        config.addInMemoryOperationInterceptor(interceptor);
        return config;
    }

    private static String ldifFilename() {
        return getPath(LDIF_FILENAME);
    }

    private static String getPath(final String relativePath) {
        File resourcesDirectory = new File(PATH_NAME + relativePath);
        return resourcesDirectory.getAbsolutePath();
    }
}

Моя идея такова изменить возвращенный результат, используя InMemoryOperationInterceptor (https://docs.ldap.com/ldap-sdk/docs/javadoc/com/unboundid/ldap/listener/interceptor/InMemoryOperationInterceptor.html). К сожалению, это не совсем работает.

Образец теста:

@Test
void shouldReturnEmptyResultWhenConfigurationIsBroken() throws Exception {
    //given
    TestOperationInterceptor testOperationInterceptor = new TestOperationInterceptor();
    InMemoryLDAPServer.newSecureServer(testOperationInterceptor);
    final LdapTestConfiguration testConfiguration = createTestLDAPConfiguration();

    //when
    final List<UserResult> results = ldapSearchService.searchByUsername(testConfiguration.getUsername());

    //then
    assertTrue(results.isEmpty());
}

Образец перехватчика:

class TestOperationInterceptor extends InMemoryOperationInterceptor {

    @Override
    public void processSearchResult(final InMemoryInterceptedSearchResult result) {
        final LDAPSearchException searchException = new LDAPSearchException(ResultCode.NO_SUCH_OBJECT, "Provided keystore is revoked or expired");
        final SearchResult searchResult = new SearchResult(searchException);
        result.setResult(searchResult);
    }
}

У вас есть другие идеи, как решить эту проблему?

...