Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/main/java/com/amihaiemil/docker/Auth.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2018, Mihai Emil Andronache
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1)Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2)Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3)Neither the name of docker-java-api nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package com.amihaiemil.docker;

/**
* Authentication for Docker API.
* @author George Aristy (george.aristy@gmail.com)
* @version $Id$
* @see <a href="https://docs.docker.com/engine/api/v1.35/#section/Authentication">Authentication</a>
* @since 0.0.1
* @todo #99:30min Implement a new auth named 'Token' that will hold the user's
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@llorllale Why do we need the Token implementation and how would it be used?

Copy link
Contributor Author

@llorllale llorllale May 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amihaiemil the token would be for the identity token alternative described here

* identity token from the auth endpoint. Implement some operation that would
* call the /auth endpoint and obtain a token.
*/
public interface Auth {
/**
* This {@link Auth} as a Base-64 encoded string.
* @return This auth as a base64-encoded string.
*/
String encoded();
}
144 changes: 144 additions & 0 deletions src/main/java/com/amihaiemil/docker/AuthHttpClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/**
* Copyright (c) 2018, Mihai Emil Andronache
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1)Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2)Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3)Neither the name of docker-java-api nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package com.amihaiemil.docker;

import java.io.IOException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;

/**
* An authenticated HttpClient.
* @author George Aristy (george.aristy@gmail.com)
* @version $Id$
* @since 0.0.1
* @todo #99:30min Start decorating our internal HttpClients with this one
* for calls that require authentication headers.
*/
final class AuthHttpClient implements HttpClient {
/**
* The decorated HttpClient.
*/
private final HttpClient origin;
/**
* The authentication to inject into requests.
*/
private final Auth authentication;

/**
* Ctor.
* @param origin The HttpClient to decorate.
* @param authentication The authentication to inject into requests.
*/
AuthHttpClient(final HttpClient origin, final Auth authentication) {
this.origin = origin;
this.authentication = authentication;
}

@Override
public HttpParams getParams() {
throw new UnsupportedOperationException();
}

@Override
public ClientConnectionManager getConnectionManager() {
throw new UnsupportedOperationException();
}

@Override
public HttpResponse execute(final HttpUriRequest request)
throws IOException, ClientProtocolException {
final String header = "X-Registry-Auth";
if (!request.containsHeader(header)) {
request.setHeader(header, this.authentication.encoded());
}
return this.origin.execute(request);
}

@Override
public HttpResponse execute(
final HttpUriRequest request, final HttpContext context
) throws IOException, ClientProtocolException {
throw new UnsupportedOperationException();
}

@Override
public HttpResponse execute(
final HttpHost target, final HttpRequest request
) throws IOException, ClientProtocolException {
throw new UnsupportedOperationException();
}

@Override
public HttpResponse execute(
final HttpHost target, final HttpRequest request,
final HttpContext context
) throws IOException, ClientProtocolException {
throw new UnsupportedOperationException();
}

@Override
public <T> T execute(
final HttpUriRequest request,
final ResponseHandler<? extends T> responseHandler
) throws IOException, ClientProtocolException {
throw new UnsupportedOperationException();
}

@Override
public <T> T execute(
final HttpUriRequest request,
final ResponseHandler<? extends T> responseHandler,
final HttpContext context
) throws IOException, ClientProtocolException {
throw new UnsupportedOperationException();
}

@Override
public <T> T execute(
final HttpHost target, final HttpRequest request,
final ResponseHandler<? extends T> responseHandler
) throws IOException, ClientProtocolException {
throw new UnsupportedOperationException();
}

// @checkstyle ParameterNumber (5 lines)
@Override
public <T> T execute(
final HttpHost target, final HttpRequest request,
final ResponseHandler<? extends T> responseHandler,
final HttpContext context
) throws IOException, ClientProtocolException {
throw new UnsupportedOperationException();
}
}
73 changes: 73 additions & 0 deletions src/main/java/com/amihaiemil/docker/Credentials.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* Copyright (c) 2018, Mihai Emil Andronache
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1)Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2)Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3)Neither the name of docker-java-api nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package com.amihaiemil.docker;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.function.Supplier;
import javax.json.Json;

/**
* An {@link Auth} supporting bare user credentials.
* @author George Aristy (george.aristy@gmail.com)
* @version $Id$
* @see <a href="https://docs.docker.com/engine/api/v1.35/#section/Authentication">Authentication</a>
* @since 0.0.1
*/
public final class Credentials implements Auth {
/**
* The base64-encoded JSON structure holding the credentials.
*/
private final Supplier<String> encoded;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@llorllale Why did you make this a Supplier? As it is now, it doesn't make much sense. Maybe you wanted to add another ctor and let the clients specify a Supplier?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amihaiemil just wanted to coalesce all those parameters into one single instance attribute

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@llorllale I see, ok


/**
* Ctor.
* @param user The username.
* @param pwd The user's password.
* @param email The user's email address.
* @param server Domain/IP without a protocol.
* @checkstyle ParameterNumber (4 lines)
*/
public Credentials(
final String user, final String pwd,
final String email, final String server
) {
this.encoded = () -> Base64.getEncoder().encodeToString(
Json.createObjectBuilder()
.add("username", user)
.add("password", pwd)
.add("email", email)
.add("serveraddress", server)
.build().toString()
.getBytes(StandardCharsets.UTF_8)
);
}

@Override
public String encoded() {
return this.encoded.get();
}
}
5 changes: 0 additions & 5 deletions src/main/java/com/amihaiemil/docker/RtImages.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,6 @@ public Iterable<Image> iterate() throws IOException {
}
}

// @todo #83:30min Several API calls required an authentication header as
// explained here:
// https://docs.docker.com/engine/api/v1.35/#section/Authentication
// (including Images.create()). Find a way to make a reusable object from
// that action and introduce it here.
// @checkstyle ParameterNumber (4 lines)
@Override
public Images create(
Expand Down
92 changes: 92 additions & 0 deletions src/test/java/com/amihaiemil/docker/AuthHttpClientTestCase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* Copyright (c) 2018, Mihai Emil Andronache
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1)Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2)Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3)Neither the name of docker-java-api nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package com.amihaiemil.docker;

import org.apache.http.Header;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;

/**
* Unit tests for {@link AuthHttpClient}.
* @author George Aristy (george.aristy@gmail.com)
* @version $Id$
* @since 0.0.1
*/
public final class AuthHttpClientTestCase {
/**
* Mock HttpClient that does nothing.
*/
private static HttpClient noOpClient;

/**
* Setup the mock http client.
* @throws Exception If something does wrong.
*/
@BeforeClass
public static void setup() throws Exception {
noOpClient = Mockito.mock(HttpClient.class);
Mockito.when(noOpClient.execute(Mockito.any(HttpUriRequest.class)))
.thenReturn(null);
}

/**
* Must inject the X-Registry-Auth header if absent and set it to the
* auth's value.
* @throws Exception If something goes wrong.
*/
@Test
public void injectsHeaderIfAbsent() throws Exception {
final HttpUriRequest request = new HttpGet();
new AuthHttpClient(noOpClient, () -> "123").execute(request);
MatcherAssert.assertThat(
request.getFirstHeader("X-Registry-Auth").getValue(),
Matchers.is("123")
);
}

/**
* Leaves the request's header instact if it exists.
* @throws Exception If something goes wrong.
*/
@Test
public void leavesExistingHeaderAlone() throws Exception {
final Header auth = new BasicHeader("X-Registry-Auth", "12356");
final HttpUriRequest request = new HttpGet();
request.setHeader(auth);
new AuthHttpClient(noOpClient, () -> "abc").execute(request);
MatcherAssert.assertThat(
request.getFirstHeader("X-Registry-Auth"),
Matchers.is(auth)
);
}
}
Loading