diff --git a/build.gradle b/build.gradle index b593dfb9..a849cd98 100644 --- a/build.gradle +++ b/build.gradle @@ -59,6 +59,7 @@ subprojects { dependencies { dependency 'com.fasterxml.jackson.core:jackson-databind:2.4.6' dependency 'com.samskivert:jmustache:1.10' + dependency 'commons-codec:commons-codec:1.10' dependency 'javax.servlet:javax.servlet-api:3.1.0' dependency 'javax.validation:validation-api:1.1.0.Final' dependency 'junit:junit:4.12' diff --git a/spring-restdocs-core/build.gradle b/spring-restdocs-core/build.gradle index 5f5be34f..2de27164 100644 --- a/spring-restdocs-core/build.gradle +++ b/spring-restdocs-core/build.gradle @@ -31,6 +31,7 @@ dependencies { jarjar 'com.googlecode.jarjar:jarjar:1.3' jmustache 'com.samskivert:jmustache@jar' optional 'javax.validation:validation-api' + optional 'commons-codec:commons-codec' testCompile 'org.mockito:mockito-core' testCompile 'org.hamcrest:hamcrest-core' testCompile 'org.hamcrest:hamcrest-library' diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/curl/CurlRequestSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/curl/CurlRequestSnippet.java index 018cb0f1..0802bdf4 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/curl/CurlRequestSnippet.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/curl/CurlRequestSnippet.java @@ -23,18 +23,21 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.restdocs.operation.Operation; import org.springframework.restdocs.operation.OperationRequest; import org.springframework.restdocs.operation.OperationRequestPart; import org.springframework.restdocs.snippet.Snippet; import org.springframework.restdocs.snippet.TemplatedSnippet; +import org.springframework.util.Base64Utils; import org.springframework.util.StringUtils; /** * A {@link Snippet} that documents the curl command for a request. * * @author Andy Wilkinson + * @author Paul-Christian Volkmer * @see CurlDocumentation#curlRequest() * @see CurlDocumentation#curlRequest(Map) */ @@ -72,9 +75,10 @@ public class CurlRequestSnippet extends TemplatedSnippet { private String getOptions(Operation operation) { StringWriter command = new StringWriter(); PrintWriter printer = new PrintWriter(command); - writeOptionToIncludeHeadersInOutput(printer); + writeIncludeHeadersInOutputOption(printer); + HttpHeaders headers = writeUserOptionIfNecessary(operation.getRequest(), printer); writeHttpMethodIfNecessary(operation.getRequest(), printer); - writeHeaders(operation.getRequest(), printer); + writeHeaders(headers, printer); writePartsIfNecessary(operation.getRequest(), printer); writeContent(operation.getRequest(), printer); @@ -82,18 +86,36 @@ public class CurlRequestSnippet extends TemplatedSnippet { return command.toString(); } - private void writeOptionToIncludeHeadersInOutput(PrintWriter writer) { + private void writeIncludeHeadersInOutputOption(PrintWriter writer) { writer.print("-i"); } + private HttpHeaders writeUserOptionIfNecessary(OperationRequest request, + PrintWriter writer) { + HttpHeaders headers = new HttpHeaders(); + headers.putAll(request.getHeaders()); + String authorization = headers.getFirst(HttpHeaders.AUTHORIZATION); + if (isAuthorizationBasicHeader(authorization)) { + String credentials = new String(Base64Utils.decodeFromString(authorization + .substring(5).trim())); + writer.print(String.format(" -u '%s'", credentials)); + headers.remove(HttpHeaders.AUTHORIZATION); + } + return headers; + } + + private boolean isAuthorizationBasicHeader(String header) { + return header != null && header.startsWith("Basic"); + } + private void writeHttpMethodIfNecessary(OperationRequest request, PrintWriter writer) { if (!HttpMethod.GET.equals(request.getMethod())) { writer.print(String.format(" -X %s", request.getMethod())); } } - private void writeHeaders(OperationRequest request, PrintWriter writer) { - for (Entry> entry : request.getHeaders().entrySet()) { + private void writeHeaders(HttpHeaders headers, PrintWriter writer) { + for (Entry> entry : headers.entrySet()) { for (String header : entry.getValue()) { writer.print(String.format(" -H '%s: %s'", entry.getKey(), header)); } @@ -142,4 +164,4 @@ public class CurlRequestSnippet extends TemplatedSnippet { || HttpMethod.POST.equals(request.getMethod()); } -} \ No newline at end of file +} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/curl/CurlRequestSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/curl/CurlRequestSnippetTests.java index 96d6927a..2e15a116 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/curl/CurlRequestSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/curl/CurlRequestSnippetTests.java @@ -36,6 +36,7 @@ import org.springframework.restdocs.templates.TemplateResourceResolver; import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; import org.springframework.restdocs.test.ExpectedSnippet; import org.springframework.restdocs.test.OperationBuilder; +import org.springframework.util.Base64Utils; /** * Tests for {@link CurlRequestSnippet} @@ -44,6 +45,7 @@ import org.springframework.restdocs.test.OperationBuilder; * @author Yann Le Guern * @author Dmitriy Mayboroda * @author Jonathan Pearlin + * @author Paul-Christian Volkmer */ public class CurlRequestSnippetTests { @@ -257,4 +259,17 @@ public class CurlRequestSnippetTests { .request("http://localhost/foo").build()); } + @Test + public void basicAuthCredentialsAreSuppliedUsingUserOption() throws IOException { + this.snippet.expectCurlRequest("basic-auth").withContents( + codeBlock("bash").content( + "$ curl 'http://localhost/foo' -i -u 'user:secret'")); + new CurlRequestSnippet().document(new OperationBuilder("basic-auth", this.snippet + .getOutputDirectory()) + .request("http://localhost/foo") + .header(HttpHeaders.AUTHORIZATION, + "Basic " + Base64Utils.encodeToString("user:secret".getBytes())) + .build()); + } + }