From 4f2fd94b27c9989729a43c968ee3c68d3d8b1789 Mon Sep 17 00:00:00 2001 From: Dan Vargas Date: Tue, 20 Jun 2023 16:27:24 -0600 Subject: [PATCH 1/2] add support for NTLM authentication --- README.md | 28 ++++++++ .../java/com/intuit/karate/core/Config.java | 64 +++++++++++++++++++ .../intuit/karate/http/ApacheHttpClient.java | 12 ++++ .../karate/core/FeatureRuntimeTest.java | 7 +- .../karate/core/ntlm-authentication.feature | 10 +++ 5 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 karate-core/src/test/java/com/intuit/karate/core/ntlm-authentication.feature diff --git a/README.md b/README.md index d19f53ec8..7e74c14c9 100755 --- a/README.md +++ b/README.md @@ -2270,6 +2270,7 @@ You can adjust configuration settings for the HTTP client used by Karate using t `pauseIfNotPerf` | boolean | defaults to `false`, relevant only for performance-testing, see [`karate.pause()`](#karate-pause) and [`karate-gatling`](karate-gatling#think-time) `xmlNamespaceAware` | boolean | defaults to `false`, to handle XML namespaces in [some special circumstances](https://github.com/karatelabs/karate/issues/1587) `abortSuiteOnFailure` | boolean | defaults to `false`, to not attempt to run any more tests upon a failure +`ntlmAuthentication` | JSON | See [NTLM Authentication](#ntlm-authentication) Examples: ```cucumber @@ -2408,6 +2409,33 @@ karate.configure('ssl', { trustAll: true }); For end-to-end examples in the Karate demos, look at the files in [this folder](karate-demo/src/test/java/ssl). +### NTLM Authentication +Karate provides support for NTLM authentication using the Apache NTLMEngine implementation. + +| Key | Type | Required? | Description | +|---------------|--------|-----------|----------------------------------------------------------------| +| `username` | string | required | NTLM username | +| `password` | string | required | NTLM password | +| `workstation` | string | optional | The workstation the authentication request is originating from | +| `domain` | string | optional | The domain to authenticate within | + +Example: +```cucumber +# enable NTLM authentication for the remaining scenario requests +* configure ntlmAuthentication = { username: 'admin', password: 'secret', domain: 'my.domain', workstation: 'my-pc' } + +# enable NTLM authentication with only credentials +* configure ntlmAuthentication = { username: 'admin', password: 'secret' } + +# disable NTLM authentication +* configure ntlmAuthentication = null +``` + +```js +// enable NTLM authentication within js +karate.confgure('ntlmAuthentication', { username: 'admin', password: 'secret', domain: 'my.domain', workstation: 'my-pc' }) +``` + # Payload Assertions ## Prepare, Mutate, Assert. Now it should be clear how Karate makes it easy to express JSON or XML. If you [read from a file](#reading-files), the advantage is that multiple scripts can re-use the same data. diff --git a/karate-core/src/main/java/com/intuit/karate/core/Config.java b/karate-core/src/main/java/com/intuit/karate/core/Config.java index 612a9069f..624a7c5c5 100644 --- a/karate-core/src/main/java/com/intuit/karate/core/Config.java +++ b/karate-core/src/main/java/com/intuit/karate/core/Config.java @@ -108,6 +108,13 @@ public class Config { // image comparison config private Map imageComparisonOptions; + // ntlm authentication + private boolean ntlmEnabled = false; + private String ntlmUsername; + private String ntlmPassword; + private String ntlmDomain; + private String ntlmWorkstation; + public Config() { // zero arg constructor } @@ -299,6 +306,18 @@ public boolean configure(String key, Variable value) { // TODO use enum case "localAddress": localAddress = value.getAsString(); return true; + case "ntlmAuthentication": + if (value.isNull()) { + ntlmEnabled = false; + } else { + Map map = value.getValue(); + ntlmEnabled = true; + ntlmUsername = (String) map.get("username"); + ntlmPassword = (String) map.get("password"); + ntlmDomain = (String) map.get("domain"); + ntlmWorkstation = (String) map.get("workstation"); + } + return true; default: throw new RuntimeException("unexpected 'configure' key: '" + key + "'"); } @@ -352,6 +371,11 @@ public Config(Config parent) { continueAfterContinueOnStepFailure = parent.continueAfterContinueOnStepFailure; abortSuiteOnFailure = parent.abortSuiteOnFailure; imageComparisonOptions = parent.imageComparisonOptions; + ntlmEnabled = parent.ntlmEnabled; + ntlmUsername = parent.ntlmUsername; + ntlmPassword = parent.ntlmPassword; + ntlmDomain = parent.ntlmDomain; + ntlmWorkstation = parent.ntlmWorkstation; } public void setUrl(String url) { @@ -590,4 +614,44 @@ public Map getImageComparisonOptions() { return imageComparisonOptions; } + public boolean isNtlmEnabled() { + return ntlmEnabled; + } + + public void setNtlmEnabled(boolean ntlmEnabled) { + this.ntlmEnabled = ntlmEnabled; + } + + public String getNtlmUsername() { + return ntlmUsername; + } + + public void setNtlmUsername(String ntlmUsername) { + this.ntlmUsername = ntlmUsername; + } + + public String getNtlmPassword() { + return ntlmPassword; + } + + public void setNtlmPassword(String ntlmPassword) { + this.ntlmPassword = ntlmPassword; + } + + public String getNtlmDomain() { + return ntlmDomain; + } + + public void setNtlmDomain(String ntlmDomain) { + this.ntlmDomain = ntlmDomain; + } + + public String getNtlmWorkstation() { + return ntlmWorkstation; + } + + public void setNtlmWorkstation(String ntlmWorkstation) { + this.ntlmWorkstation = ntlmWorkstation; + } + } diff --git a/karate-core/src/main/java/com/intuit/karate/http/ApacheHttpClient.java b/karate-core/src/main/java/com/intuit/karate/http/ApacheHttpClient.java index 86cb7f632..baadcce02 100644 --- a/karate-core/src/main/java/com/intuit/karate/http/ApacheHttpClient.java +++ b/karate-core/src/main/java/com/intuit/karate/http/ApacheHttpClient.java @@ -55,10 +55,12 @@ import org.apache.http.HttpMessage; import org.apache.http.HttpRequestInterceptor; import org.apache.http.auth.AuthScope; +import org.apache.http.auth.NTCredentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.CookieStore; import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.config.AuthSchemes; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.EntityBuilder; import org.apache.http.client.methods.CloseableHttpResponse; @@ -192,6 +194,16 @@ private void configure(Config config) { logger.warn("failed to resolve local address: {} - {}", config.getLocalAddress(), e.getMessage()); } } + if (config.isNtlmEnabled()) { + List authSchemes = new ArrayList<>(); + authSchemes.add(AuthSchemes.NTLM); + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + NTCredentials ntCredentials = new NTCredentials( + config.getNtlmUsername(), config.getNtlmPassword(), config.getNtlmWorkstation(), config.getNtlmDomain()); + credentialsProvider.setCredentials(AuthScope.ANY, ntCredentials); + clientBuilder.setDefaultCredentialsProvider(credentialsProvider); + configBuilder.setTargetPreferredAuthSchemes(authSchemes); + } clientBuilder.setDefaultRequestConfig(configBuilder.build()); SocketConfig.Builder socketBuilder = SocketConfig.custom().setSoTimeout(config.getConnectTimeout()); clientBuilder.setDefaultSocketConfig(socketBuilder.build()); diff --git a/karate-core/src/test/java/com/intuit/karate/core/FeatureRuntimeTest.java b/karate-core/src/test/java/com/intuit/karate/core/FeatureRuntimeTest.java index 68ec54a75..a09eebd90 100644 --- a/karate-core/src/test/java/com/intuit/karate/core/FeatureRuntimeTest.java +++ b/karate-core/src/test/java/com/intuit/karate/core/FeatureRuntimeTest.java @@ -399,6 +399,11 @@ void testSortArray() { @Test void testTypeConv() { run("type-conv.feature"); - } + } + + @Test + void testConfigureNtlmAuthentication() { + run("ntlm-authentication.feature"); + } } diff --git a/karate-core/src/test/java/com/intuit/karate/core/ntlm-authentication.feature b/karate-core/src/test/java/com/intuit/karate/core/ntlm-authentication.feature new file mode 100644 index 000000000..f393dc135 --- /dev/null +++ b/karate-core/src/test/java/com/intuit/karate/core/ntlm-authentication.feature @@ -0,0 +1,10 @@ +Feature: ntlm authentication + + Scenario: various ways to configure ntlm authentication + * configure ntlmAuthentication = { username: 'admin', password: 'secret', domain: 'my.domain', workstation: 'ws' } + * configure ntlmAuthentication = { username: 'admin', password: 'secret' } + * configure ntlmAuthentication = null + * eval + """ + karate.configure('ntlmAuthentication', { username: 'admin', password: 'secret' }) + """ From 36e492837844a723e559b0b5a4d9e7d4932e9189 Mon Sep 17 00:00:00 2001 From: Dan Vargas Date: Wed, 21 Jun 2023 14:44:07 -0600 Subject: [PATCH 2/2] shorten NTLM configure key to ntlmAuth --- README.md | 10 +++++----- .../src/main/java/com/intuit/karate/core/Config.java | 2 +- .../com/intuit/karate/core/ntlm-authentication.feature | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7e74c14c9..070b5d411 100755 --- a/README.md +++ b/README.md @@ -2270,7 +2270,7 @@ You can adjust configuration settings for the HTTP client used by Karate using t `pauseIfNotPerf` | boolean | defaults to `false`, relevant only for performance-testing, see [`karate.pause()`](#karate-pause) and [`karate-gatling`](karate-gatling#think-time) `xmlNamespaceAware` | boolean | defaults to `false`, to handle XML namespaces in [some special circumstances](https://github.com/karatelabs/karate/issues/1587) `abortSuiteOnFailure` | boolean | defaults to `false`, to not attempt to run any more tests upon a failure -`ntlmAuthentication` | JSON | See [NTLM Authentication](#ntlm-authentication) +`ntlmAuth` | JSON | See [NTLM Authentication](#ntlm-authentication) Examples: ```cucumber @@ -2422,18 +2422,18 @@ Karate provides support for NTLM authentication using the Apache NTLMEngine impl Example: ```cucumber # enable NTLM authentication for the remaining scenario requests -* configure ntlmAuthentication = { username: 'admin', password: 'secret', domain: 'my.domain', workstation: 'my-pc' } +* configure ntlmAuth = { username: 'admin', password: 'secret', domain: 'my.domain', workstation: 'my-pc' } # enable NTLM authentication with only credentials -* configure ntlmAuthentication = { username: 'admin', password: 'secret' } +* configure ntlmAuth = { username: 'admin', password: 'secret' } # disable NTLM authentication -* configure ntlmAuthentication = null +* configure ntlmAuth = null ``` ```js // enable NTLM authentication within js -karate.confgure('ntlmAuthentication', { username: 'admin', password: 'secret', domain: 'my.domain', workstation: 'my-pc' }) +karate.confgure('ntlmAuth', { username: 'admin', password: 'secret', domain: 'my.domain', workstation: 'my-pc' }) ``` # Payload Assertions diff --git a/karate-core/src/main/java/com/intuit/karate/core/Config.java b/karate-core/src/main/java/com/intuit/karate/core/Config.java index 624a7c5c5..2a8e710d7 100644 --- a/karate-core/src/main/java/com/intuit/karate/core/Config.java +++ b/karate-core/src/main/java/com/intuit/karate/core/Config.java @@ -306,7 +306,7 @@ public boolean configure(String key, Variable value) { // TODO use enum case "localAddress": localAddress = value.getAsString(); return true; - case "ntlmAuthentication": + case "ntlmAuth": if (value.isNull()) { ntlmEnabled = false; } else { diff --git a/karate-core/src/test/java/com/intuit/karate/core/ntlm-authentication.feature b/karate-core/src/test/java/com/intuit/karate/core/ntlm-authentication.feature index f393dc135..c89cc701e 100644 --- a/karate-core/src/test/java/com/intuit/karate/core/ntlm-authentication.feature +++ b/karate-core/src/test/java/com/intuit/karate/core/ntlm-authentication.feature @@ -1,10 +1,10 @@ Feature: ntlm authentication Scenario: various ways to configure ntlm authentication - * configure ntlmAuthentication = { username: 'admin', password: 'secret', domain: 'my.domain', workstation: 'ws' } - * configure ntlmAuthentication = { username: 'admin', password: 'secret' } - * configure ntlmAuthentication = null + * configure ntlmAuth = { username: 'admin', password: 'secret', domain: 'my.domain', workstation: 'my-pc' } + * configure ntlmAuth = { username: 'admin', password: 'secret' } + * configure ntlmAuth = null * eval """ - karate.configure('ntlmAuthentication', { username: 'admin', password: 'secret' }) + karate.configure('ntlmAuth', { username: 'admin', password: 'secret' }) """