diff --git a/.circleci/config.yml b/.circleci/config.yml index 081e3e7..3383340 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -146,7 +146,7 @@ jobs: - v1-integration-{{ checksum "build.gradle" }} - v1-integration - run: | - make integration-test remote-integration-test + make integration-test no-auth-integration-test remote-integration-test - save_cache: paths: - ~/.gradle @@ -284,4 +284,4 @@ workflows: - jumpcloud-credentials <<: *pr_filter requires: - - validate_yamls \ No newline at end of file + - validate_yamls diff --git a/.gitignore b/.gitignore index 0037146..8feb971 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ checkstyle.xml codenarc.xml logs-intg.txt logs-intg-remote.txt +logs-intg-no-auth.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index c3c115c..486719e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,24 +1,16 @@ # Changelog ## [Unreleased] +### Added +- [#164](https://github.com/devatherock/ldap-search-api/issues/164): Support for anonymous access + ### Changed -- chore(deps): update plugin io.micronaut.application to v4.4.3 -- fix(deps): update dependency ch.qos.logback:logback-classic to v1.5.9 -- fix(deps): update dependency net.bytebuddy:byte-buddy to v1.15.4 -- fix(deps): update dependency ch.qos.logback:logback-classic to v1.5.10 -- fix(deps): update dependency ch.qos.logback:logback-classic to v1.5.11 -- fix(deps): update dependency net.bytebuddy:byte-buddy to v1.15.5 - fix(deps): update dependency ch.qos.logback:logback-classic to v1.5.12 -- fix(deps): update dependency net.bytebuddy:byte-buddy to v1.15.7 -- fix(deps): update dependency net.bytebuddy:byte-buddy to v1.15.8 -- fix(deps): update dependency net.bytebuddy:byte-buddy to v1.15.9 - chore(deps): update plugin io.micronaut.application to v4.4.4 - fix(deps): update dependency net.bytebuddy:byte-buddy to v1.15.10 - fix(deps): update dependency org.apache.groovy:groovy-json to v4.0.24 - fix(deps): update dependency org.projectlombok:lombok to v1.18.36 -- chore(deps): update dependency gradle to v8.11 - chore(deps): update dependency gradle to v8.11.1 -- chore(deps): update plugin org.sonarqube to v6 - chore(deps): update plugin org.sonarqube to v6.0.1.5171 - fix(deps): update dependency com.unboundid:unboundid-ldapsdk to v7.0.2 - chore(deps): update alpine docker tag to v3.21.0 @@ -133,4 +125,4 @@ with `LOGGER_LEVELS` prefix supported out of the box by micronaut - Initial version. REST endpoint to query a LDAP server ### Changed -- The response to a `List` of maps from a `Map` \ No newline at end of file +- The response to a `List` of maps from a `Map` diff --git a/Makefile b/Makefile index 662e0ca..8626da5 100644 --- a/Makefile +++ b/Makefile @@ -10,12 +10,18 @@ integration-test: docker logs -f ldap-search-api-intg > logs-intg.txt & ./gradlew integrationTest --tests '*ControllerIntegrationSpec*' docker-compose down +no-auth-integration-test: + rm -rf logs-intg-no-auth.txt + DOCKER_TAG=$(docker_tag) docker compose -f docker-compose-no-auth.yml up --wait + docker logs -f ldap-search-api-intg-no-auth > logs-intg-no-auth.txt & + ./gradlew integrationTest --tests '*LdapSearchControllerNoAuthIntegrationSpec*' + docker compose -f docker-compose-no-auth.yml down remote-integration-test: rm -rf logs-intg-remote.txt DOCKER_TAG=$(docker_tag) docker compose -f docker-compose-remote.yml up --wait docker logs -f ldap-search-api-intg-remote > logs-intg-remote.txt & ./gradlew integrationTest --tests '*RemoteUrlsIntegrationSpec*' - docker-compose down + docker compose -f docker-compose-remote.yml down build-all: ./gradlew build fast-build: diff --git a/README.md b/README.md index b5f861c..0efcdf6 100644 --- a/README.md +++ b/README.md @@ -21,24 +21,25 @@ docker run --rm \ ### Configurable properties -| Environment Variable Name | YAML Variable Name | Required | Default | Description | -|---------------------------------------|---------------------------------------|----------|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| LDAP_HOST | ldap.host | true | (None) | The full host name of the LDAP server. Example: `ldaps://ldap.jumpcloud.com:636` | -| LDAP_USERNAME | ldap.username | true | (None) | The LDAP bind username. Could be a simple username like `devatherock` or a DN like `uid=devatherock,ou=Users,dc=jumpcloud,dc=com` depending on how the LDAP server is configured | -| LDAP_PASSWORD | ldap.password | true | (None) | The LDAP bind password | -| LDAP_BASE_DN | ldap.base-dn | false | (None) | The default base DN to search against | -| LDAP_BINARY_ATTRIBUTES | ldap.binary-attributes | false | (None) | Attributes in the search result that have binary values | -| LDAP_READ_TIMEOUT_MILLIS | ldap.read-timeout-millis | false | 10000 | Read timeout for the search, in milliseconds. Defaults to 10 seconds | -| LDAP_CONNECTION_POOL_ENABLED | ldap.connection-pool.enabled | false | true | Indicates if a connection pool should be used | -| LDAP_CONNECTION_POOL_CORE_SIZE | ldap.connection-pool.core-size | false | 8 | Initial size of the connection pool | -| LDAP_CONNECTION_POOL_MAX_SIZE | ldap.connection-pool.max-size | false | 8 | Maximum size of the connection pool | -| LDAP_CONNECTION_POOL_TIME_TO_LIVE_MILLIS | ldap.connection-pool.timeToLiveMillis | false | 1,800,000 | The total time a connection in the pool will be kept open, in milliseconds. Defaults to 30 minutes | -| LOGGER_LEVELS_ROOT | (None) | false | INFO | [SLF4J](http://www.slf4j.org/api/org/apache/commons/logging/Log.html) log level, for all(framework and custom) code | -| LOGGER_LEVELS_IO_GITHUB_DEVATHEROCK | (None) | false | INFO | [SLF4J](http://www.slf4j.org/api/org/apache/commons/logging/Log.html) log level, for custom code | -| MICRONAUT_SERVER_PORT | micronaut.server.port | false | 8080 | Port in which the app listens on | -| MICRONAUT_CONFIG_FILES | (None) | false | (None) | Path to YAML config files. The YAML files can be used to specify complex, object and array properties | -| JACKSON_SERIALIZATION_INDENT_OUTPUT | jackson.serialization.indent-output | false | (None) | Set to `true` to enable JSON pretty-print of response | -| LOGBACK_CONFIGURATION_FILE | (None) | false | (None) | Path to logback configuration file | +| Environment Variable Name | YAML Variable Name | Required | Default | Description | +|------------------------------------------|---------------------------------------|----------|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| LDAP_HOST | ldap.host | true | (None) | The full host name of the LDAP server. Example: `ldaps://ldap.jumpcloud.com:636` | +| LDAP_USERNAME | ldap.username | false | (None) | The LDAP bind username. Could be a simple username like `devatherock` or a DN like `uid=devatherock,ou=Users,dc=jumpcloud,dc=com` depending on how the LDAP server is configured | +| LDAP_PASSWORD | ldap.password | false | (None) | The LDAP bind password | +| LDAP_AUTH_TYPE | ldap.auth-type | false | simple | Value for the `java.naming.security.authentication` property. Supported values are `none`, `simple`. Defaults to `simple` | +| LDAP_BASE_DN | ldap.base-dn | false | (None) | The default base DN to search against | +| LDAP_BINARY_ATTRIBUTES | ldap.binary-attributes | false | (None) | Attributes in the search result that have binary values | +| LDAP_READ_TIMEOUT_MILLIS | ldap.read-timeout-millis | false | 10000 | Read timeout for the search, in milliseconds. Defaults to 10 seconds | +| LDAP_CONNECTION_POOL_ENABLED | ldap.connection-pool.enabled | false | true | Indicates if a connection pool should be used | +| LDAP_CONNECTION_POOL_CORE_SIZE | ldap.connection-pool.core-size | false | 8 | Initial size of the connection pool | +| LDAP_CONNECTION_POOL_MAX_SIZE | ldap.connection-pool.max-size | false | 8 | Maximum size of the connection pool | +| LDAP_CONNECTION_POOL_TIME_TO_LIVE_MILLIS | ldap.connection-pool.timeToLiveMillis | false | 1,800,000 | The total time a connection in the pool will be kept open, in milliseconds. Defaults to 30 minutes | +| LOGGER_LEVELS_ROOT | (None) | false | INFO | [SLF4J](http://www.slf4j.org/api/org/apache/commons/logging/Log.html) log level, for all(framework and custom) code | +| LOGGER_LEVELS_IO_GITHUB_DEVATHEROCK | (None) | false | INFO | [SLF4J](http://www.slf4j.org/api/org/apache/commons/logging/Log.html) log level, for custom code | +| MICRONAUT_SERVER_PORT | micronaut.server.port | false | 8080 | Port in which the app listens on | +| MICRONAUT_CONFIG_FILES | (None) | false | (None) | Path to YAML config files. The YAML files can be used to specify complex, object and array properties | +| JACKSON_SERIALIZATION_INDENT_OUTPUT | jackson.serialization.indent-output | false | (None) | Set to `true` to enable JSON pretty-print of response | +| LOGBACK_CONFIGURATION_FILE | (None) | false | (None) | Path to logback configuration file | ### API spec When the app is running, detailed API documentation can be accessed at `{host}/swagger-ui` or `{host}/swagger/ldap-search-api-{version}.yml`. The available endpoints are listed below for reference: @@ -80,4 +81,4 @@ and set the environment variable `LOGBACK_CONFIGURATION_FILE` to `/path/to/custo ### JSON logs -Refer [logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder) documentation to customize the field names and formats in the log. To output logs as JSON, set the environment variable `LOGBACK_CONFIGURATION_FILE` to `logback-json.xml` \ No newline at end of file +Refer [logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder) documentation to customize the field names and formats in the log. To output logs as JSON, set the environment variable `LOGBACK_CONFIGURATION_FILE` to `logback-json.xml` diff --git a/docker-compose-no-auth.yml b/docker-compose-no-auth.yml new file mode 100644 index 0000000..a3ae62c --- /dev/null +++ b/docker-compose-no-auth.yml @@ -0,0 +1,28 @@ +version: '3.7' +services: + + ldap-search-api: + image: devatherock/ldap-search-api:${DOCKER_TAG:-latest} + container_name: ldap-search-api-intg-no-auth + network_mode: "host" + environment: + LDAP_HOST: ldap://localhost:33389 + LDAP_AUTH_TYPE: none + LOGBACK_CONFIGURATION_FILE: logback-json.xml + JACKSON_SERIALIZATION_INDENT_OUTPUT: true + + health: + image: alpine:3.21.0 + network_mode: "host" + depends_on: + - ldap-search-api + healthcheck: + test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:8080/health"] + interval: 2s + timeout: 60s + retries: 30 + command: | + sh -c ' + wget -q -O - http://localhost:8080/health + sleep 120 + ' diff --git a/src/integrationTest/groovy/io.github.devatherock/ldapsearch.controllers/LdapSearchControllerIntegrationSpec.groovy b/src/integrationTest/groovy/io.github.devatherock/ldapsearch.controllers/LdapSearchControllerIntegrationSpec.groovy index 3b7f3df..25d8179 100644 --- a/src/integrationTest/groovy/io.github.devatherock/ldapsearch.controllers/LdapSearchControllerIntegrationSpec.groovy +++ b/src/integrationTest/groovy/io.github.devatherock/ldapsearch.controllers/LdapSearchControllerIntegrationSpec.groovy @@ -1,13 +1,18 @@ package io.github.devatherock.ldapsearch.controllers +import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig import io.micronaut.test.extensions.spock.annotation.MicronautTest /** - * Integration test for {@link LdapSearchController} + * Integration test for {@link LdapSearchController} with auth */ @MicronautTest(propertySources = 'classpath:application-integration.yml', startApplication = false) class LdapSearchControllerIntegrationSpec extends LdapSearchControllerSpec { + protected void customizeLdapConfig(InMemoryDirectoryServerConfig config) { + config.addAdditionalBindCredentials('cn=Directory Manager', 'testpwd') + } + protected String getExpectedJson() { ''' [ diff --git a/src/integrationTest/groovy/io.github.devatherock/ldapsearch.controllers/LdapSearchControllerNoAuthIntegrationSpec.groovy b/src/integrationTest/groovy/io.github.devatherock/ldapsearch.controllers/LdapSearchControllerNoAuthIntegrationSpec.groovy new file mode 100644 index 0000000..fcb5f35 --- /dev/null +++ b/src/integrationTest/groovy/io.github.devatherock/ldapsearch.controllers/LdapSearchControllerNoAuthIntegrationSpec.groovy @@ -0,0 +1,29 @@ +package io.github.devatherock.ldapsearch.controllers + +import io.micronaut.test.extensions.spock.annotation.MicronautTest + +/** + * Integration test for {@link LdapSearchController} without auth + */ +@MicronautTest(propertySources = 'classpath:application-integration.yml', startApplication = false) +class LdapSearchControllerNoAuthIntegrationSpec extends LdapSearchControllerSpec { + + protected String getExpectedJson() { + ''' + [ + { + "userPassword" : "YWJjZGU=", + "uid" : "sclaus", + "objectClass" : [ + "top", + "person", + "organizationalPerson", + "inetOrgPerson" + ], + "sn" : "Claus", + "cn" : "Santa Claus" + } + ] + '''.stripIndent().trim() + } +} diff --git a/src/main/java/io/github/devatherock/ldapsearch/config/LdapProperties.java b/src/main/java/io/github/devatherock/ldapsearch/config/LdapProperties.java index 2f80f41..8606947 100644 --- a/src/main/java/io/github/devatherock/ldapsearch/config/LdapProperties.java +++ b/src/main/java/io/github/devatherock/ldapsearch/config/LdapProperties.java @@ -6,7 +6,9 @@ import io.micronaut.context.annotation.ConfigurationProperties; import io.micronaut.context.annotation.Context; import io.micronaut.core.annotation.Introspected; +import io.micronaut.core.util.StringUtils; import jakarta.annotation.PostConstruct; +import jakarta.validation.constraints.AssertTrue; import jakarta.validation.constraints.NotBlank; import lombok.AccessLevel; import lombok.Getter; @@ -37,15 +39,19 @@ public class LdapProperties { * or a DN like {@code uid=devatherock,ou=Users,dc=jumpcloud,dc=com} depending * on how the LDAP server is configured */ - @NotBlank(message = "ldap.username not specified") private String username; /** * The LDAP bind password */ - @NotBlank(message = "ldap.password not specified") private String password; + /** + * Value for the {@code java.naming.security.authentication} property. Defaults + * to {@code simple} + */ + private AuthenticationType authType = AuthenticationType.SIMPLE; + /** * The default base DN to search against */ @@ -65,6 +71,12 @@ public class LdapProperties { private LdapConnectionPoolProperties connectionPool = new LdapConnectionPoolProperties(); + @AssertTrue(message = "ldap.username or ldap.password not specified") + public boolean isValidLdapCredentials() { + return AuthenticationType.NONE.equals(authType) || + (StringUtils.hasText(username) && StringUtils.hasText(password)); + } + /** * Connection pool configuration * @@ -97,6 +109,18 @@ public static class LdapConnectionPoolProperties { private long timeToLiveMillis = 1_800_000; } + @Getter + public enum AuthenticationType { + NONE("none"), + SIMPLE("simple"); + + private final String code; + + AuthenticationType(String code) { + this.code = code; + } + } + @PostConstruct public void init() { combinedBinaryAttributes = String.join(" ", binaryAttributes); diff --git a/src/main/java/io/github/devatherock/ldapsearch/service/LdapSearchService.java b/src/main/java/io/github/devatherock/ldapsearch/service/LdapSearchService.java index 68bcf95..23ade7c 100644 --- a/src/main/java/io/github/devatherock/ldapsearch/service/LdapSearchService.java +++ b/src/main/java/io/github/devatherock/ldapsearch/service/LdapSearchService.java @@ -18,6 +18,7 @@ import javax.naming.ldap.LdapContext; import io.github.devatherock.ldapsearch.config.LdapProperties; +import io.github.devatherock.ldapsearch.config.LdapProperties.AuthenticationType; import io.micronaut.core.util.CollectionUtils; import jakarta.inject.Singleton; @@ -98,9 +99,13 @@ private Hashtable initializeLdapEnvironment() { Hashtable ldapEnv = new Hashtable(); ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); ldapEnv.put(Context.PROVIDER_URL, config.getHost()); - ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple"); - ldapEnv.put(Context.SECURITY_PRINCIPAL, config.getUsername()); - ldapEnv.put(Context.SECURITY_CREDENTIALS, config.getPassword()); + ldapEnv.put(Context.SECURITY_AUTHENTICATION, config.getAuthType().getCode()); + + if (AuthenticationType.SIMPLE.equals(config.getAuthType())) { + ldapEnv.put(Context.SECURITY_PRINCIPAL, config.getUsername()); + ldapEnv.put(Context.SECURITY_CREDENTIALS, config.getPassword()); + } + ldapEnv.put("com.sun.jndi.ldap.connect.pool", String.valueOf(config.getConnectionPool().isEnabled())); ldapEnv.put("com.sun.jndi.ldap.read.timeout", String.valueOf(config.getReadTimeoutMillis())); @@ -197,4 +202,4 @@ private Object formatAttributeValue(Object attributeValue) { return attributeValue; } } -} \ No newline at end of file +} diff --git a/src/main/resources/logback-json.xml b/src/main/resources/logback-json.xml index 1fe5c67..af78837 100644 --- a/src/main/resources/logback-json.xml +++ b/src/main/resources/logback-json.xml @@ -7,6 +7,7 @@ - + diff --git a/src/test/groovy/io/github/devatherock/ldapsearch/config/LdapPropertiesSpec.groovy b/src/test/groovy/io/github/devatherock/ldapsearch/config/LdapPropertiesSpec.groovy index ad40afa..674d74f 100644 --- a/src/test/groovy/io/github/devatherock/ldapsearch/config/LdapPropertiesSpec.groovy +++ b/src/test/groovy/io/github/devatherock/ldapsearch/config/LdapPropertiesSpec.groovy @@ -2,6 +2,8 @@ package io.github.devatherock.ldapsearch.config import org.slf4j.LoggerFactory +import io.github.devatherock.ldapsearch.config.LdapProperties.AuthenticationType + import ch.qos.logback.classic.Level import spock.lang.Specification import spock.lang.Subject @@ -36,4 +38,27 @@ class LdapPropertiesSpec extends Specification { logger.setLevel(Level.INFO) System.clearProperty(connectionPoolDebugProperty) } + + void 'test is valid ldap credentials'() { + given: + config.authType = authType + config.username = username + config.password = password + + when: + boolean actual = config.isValidLdapCredentials() + + then: + actual == expected + + where: + username | password | authType || expected + 'dummy' | 'dummy' | AuthenticationType.SIMPLE || true + '' | '' | AuthenticationType.SIMPLE || false + 'dummy' | null | AuthenticationType.SIMPLE || false + null | 'dummy' | AuthenticationType.SIMPLE || false + '' | '' | AuthenticationType.NONE || true + 'dummy' | 'dummy' | AuthenticationType.NONE || true + null | null | AuthenticationType.NONE || true + } } diff --git a/src/test/groovy/io/github/devatherock/ldapsearch/controllers/LdapSearchControllerNoAuthTestSpec.groovy b/src/test/groovy/io/github/devatherock/ldapsearch/controllers/LdapSearchControllerNoAuthTestSpec.groovy new file mode 100644 index 0000000..7ed9d1c --- /dev/null +++ b/src/test/groovy/io/github/devatherock/ldapsearch/controllers/LdapSearchControllerNoAuthTestSpec.groovy @@ -0,0 +1,14 @@ +package io.github.devatherock.ldapsearch.controllers + +import io.micronaut.test.extensions.spock.annotation.MicronautTest + +/** + * Unit test for {@link LdapSearchController} without auth + */ +@MicronautTest(propertySources = 'classpath:application-test_no_auth.yml') +class LdapSearchControllerNoAuthTestSpec extends LdapSearchControllerSpec { + + protected String getExpectedJson() { + '[{"userPassword":"YWJjZGU=","uid":"sclaus","objectClass":["top","person","organizationalPerson","inetOrgPerson"],"sn":"Claus","cn":"Santa Claus"}]' + } +} diff --git a/src/test/groovy/io/github/devatherock/ldapsearch/controllers/LdapSearchControllerSpec.groovy b/src/test/groovy/io/github/devatherock/ldapsearch/controllers/LdapSearchControllerSpec.groovy index 7559d1d..8fb523f 100644 --- a/src/test/groovy/io/github/devatherock/ldapsearch/controllers/LdapSearchControllerSpec.groovy +++ b/src/test/groovy/io/github/devatherock/ldapsearch/controllers/LdapSearchControllerSpec.groovy @@ -1,6 +1,7 @@ package io.github.devatherock.ldapsearch.controllers import groovy.json.JsonSlurper +import groovy.util.logging.Slf4j import com.unboundid.ldap.listener.InMemoryDirectoryServer import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig @@ -17,6 +18,7 @@ import spock.lang.Specification /** * Test class for {@link LdapSearchController} */ +@Slf4j abstract class LdapSearchControllerSpec extends Specification { @Shared @@ -31,10 +33,10 @@ abstract class LdapSearchControllerSpec extends Specification { void setupSpec() { InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig('dc=example,dc=com') - config.addAdditionalBindCredentials('cn=Directory Manager', 'testpwd') config.setListenerConfigs( new InMemoryListenerConfig('testListener', null, 33389, null, null, null)) + customizeLdapConfig(config) directoryServer = new InMemoryDirectoryServer(config) directoryServer.importFromLDIF(true, @@ -42,6 +44,10 @@ abstract class LdapSearchControllerSpec extends Specification { directoryServer.startListening() } + protected void customizeLdapConfig(InMemoryDirectoryServerConfig config) { + log.debug("Customizing ldap config {}", config) // To suppress codeNarc unused method parameter violation + } + void cleanupSpec() { directoryServer.shutDown(true) } diff --git a/src/test/groovy/io/github/devatherock/ldapsearch/controllers/LdapSearchControllerTestSpec.groovy b/src/test/groovy/io/github/devatherock/ldapsearch/controllers/LdapSearchControllerTestSpec.groovy index 9a6a531..91fdcec 100644 --- a/src/test/groovy/io/github/devatherock/ldapsearch/controllers/LdapSearchControllerTestSpec.groovy +++ b/src/test/groovy/io/github/devatherock/ldapsearch/controllers/LdapSearchControllerTestSpec.groovy @@ -1,13 +1,18 @@ package io.github.devatherock.ldapsearch.controllers +import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig import io.micronaut.test.extensions.spock.annotation.MicronautTest /** - * Unit test for {@link LdapSearchController} + * Unit test for {@link LdapSearchController} with auth */ @MicronautTest class LdapSearchControllerTestSpec extends LdapSearchControllerSpec { + protected void customizeLdapConfig(InMemoryDirectoryServerConfig config) { + config.addAdditionalBindCredentials('cn=Directory Manager', 'testpwd') + } + protected String getExpectedJson() { '[{"userPassword":"YWJjZGU=","uid":"sclaus","objectClass":["top","person","organizationalPerson","inetOrgPerson"],"sn":"Claus","cn":"Santa Claus"}]' } diff --git a/src/test/resources/application-test_no_auth.yml b/src/test/resources/application-test_no_auth.yml new file mode 100644 index 0000000..f04dd41 --- /dev/null +++ b/src/test/resources/application-test_no_auth.yml @@ -0,0 +1,9 @@ +ldap: + host: ldap://localhost:33389 + authType: none +micronaut: + http.client.read-timeout: 30 + server.port: 8091 +test: + server: + url: http://localhost:8091