Skip to content

Commit

Permalink
Add support for BasicAuth + alwaysSendBasicAuthHeaders in JsonService…
Browse files Browse the repository at this point in the history
…Client
  • Loading branch information
mythz committed Jan 4, 2016
1 parent 7c60dcd commit 80d0597
Show file tree
Hide file tree
Showing 6 changed files with 1,776 additions and 1,430 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

public class HttpHeaders {
public static final String Accept = "Accept";
public static final String ContentType = "Content-Type";
public static final String Authorization = "Authorization";
public static final String ContentLength = "Content-Length";
public static final String ContentType = "Content-Type";
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ public class JsonServiceClient implements ServiceClient {
String baseUrl;
String replyUrl;

boolean alwaysSendBasicAuthHeaders;
String userName;
String password;

Integer timeoutMs;
public ConnectionFilter RequestFilter;
public ConnectionFilter ResponseFilter;
Expand Down Expand Up @@ -161,6 +165,10 @@ public HttpURLConnection createRequest(String requestUrl, String httpMethod, byt
req.setRequestProperty(HttpHeaders.ContentType, requestType);
}

if (alwaysSendBasicAuthHeaders) {
addBasicAuth(req, userName, password);
}

if (RequestFilter != null) {
RequestFilter.exec(req);
}
Expand All @@ -185,6 +193,11 @@ public HttpURLConnection createRequest(String requestUrl, String httpMethod, byt
}
}

private static void addBasicAuth(HttpURLConnection req, String userName, String password) {
req.setRequestProperty(HttpHeaders.Authorization,
"Basic " + Utils.toBase64String(userName + ":" + password));
}

public static RuntimeException createException(HttpURLConnection res, int responseCode){

WebServiceException webEx = null;
Expand Down Expand Up @@ -259,6 +272,17 @@ public HttpURLConnection createSendRequest(Object request) {
}
}

@Override
public void setAlwaysSendBasicAuthHeaders(boolean value) {
this.alwaysSendBasicAuthHeaders = value;
}

@Override
public void setCredentials(String userName, String password) {
this.userName = userName;
this.password = password;
}

@Override
public <TResponse> TResponse send(IReturn<TResponse> request) {
return send(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
import java.util.Map;

public interface ServiceClient {
void setAlwaysSendBasicAuthHeaders(boolean value);
void setCredentials(String userName, String password);

<TResponse> TResponse send(IReturn<TResponse> request);
void send(IReturnVoid request);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ public static boolean matchesContentType(String contentType, String matchesConte
}

public static String sanitizeVarName(String name){
return name.replaceAll("_","").toLowerCase();
return name.replaceAll("_", "").toLowerCase();
}

public static ResponseStatus createResponseStatus(Object obj) {
Expand Down Expand Up @@ -671,4 +671,115 @@ public static <K,V> HashMap<K,ArrayList<V>> createMap(ArrayList<V> xs, Function<

return to;
}

//From: http://iharder.sourceforge.net/current/java/base64/ (Public Domain)
public static String toBase64String(String source) {
return toBase64String(toUtf8Bytes(source));
}

public static String toBase64String(byte[] source) {
byte[] encoded = toBase64Bytes(source);
try {
return new String(encoded, "US-ASCII");
} catch (UnsupportedEncodingException e) {
return new String(encoded);
}
}

public static byte[] toBase64Bytes(byte[] source) {
return toBase64Bytes(source, 0, source.length);
}

public static byte[] toBase64Bytes(byte[] source, int off, int len) {
if (source == null)
throw new NullPointerException("Cannot serialize a null array.");

if (off < 0)
throw new IllegalArgumentException("Cannot have negative offset: " + off);

if (len < 0)
throw new IllegalArgumentException("Cannot have length offset: " + len);

if (off + len > source.length)
throw new IllegalArgumentException(String.format(
"Cannot have offset of %d and length of %d with array of length %d", off, len, source.length));

int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0); // Bytes needed for actual encoding
byte[] outBuff = new byte[encLen];

int d = 0;
int e = 0;
int len2 = len - 2;
for (; d < len2; d += 3, e += 4) {
encode3to4(source, d + off, 3, outBuff, e);
}

if (d < len) {
encode3to4(source, d + off, len - d, outBuff, e);
e += 4;
}

// Only resize array if we didn't guess it right.
if (e <= outBuff.length - 1) {
byte[] finalOut = new byte[e];
System.arraycopy(outBuff, 0, finalOut, 0, e);
return finalOut;
} else {
return outBuff;
}
}

private final static byte EQUALS_SIGN = (byte)'=';

private final static byte[] _STANDARD_ALPHABET = {
(byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
(byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
(byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
(byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
(byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
(byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
(byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
(byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
(byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
(byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
};

private static byte[] encode3to4(
byte[] source, int srcOffset, int numSigBytes,
byte[] destination, int destOffset) {

byte[] ALPHABET = _STANDARD_ALPHABET;

int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 )
| ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
| ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );

switch (numSigBytes)
{
case 3:
destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ];
return destination;

case 2:
destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
destination[ destOffset + 3 ] = EQUALS_SIGN;
return destination;

case 1:
destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
destination[ destOffset + 2 ] = EQUALS_SIGN;
destination[ destOffset + 3 ] = EQUALS_SIGN;
return destination;

default:
return destination;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package net.servicestack.client.tests;

import junit.framework.TestCase;

import net.servicestack.client.JsonServiceClient;
import net.servicestack.client.WebServiceException;

import net.servicestack.client.tests.testdtos.*;

/**
* Created by mythz on 1/3/2016.
*/
public class TestAuthTests extends TestCase {

JsonServiceClient client = new JsonServiceClient("http://test.servicestack.net");

public void test_AuthRequired_returns_401(){
try {
client.get(new TestAuth());
fail("should throw");
} catch (WebServiceException ex){
assertEquals(401, ex.getStatusCode());
assertEquals("Unauthorized", ex.getStatusDescription());
}
}

public void test_does_send_BasicAuthHeaders(){
client.setCredentials("test", "test");
client.setAlwaysSendBasicAuthHeaders(true);

TestAuthResponse response = client.get(new TestAuth());

assertEquals("1", response.getUserId());
assertEquals("test", response.getUserName());
assertEquals("test DisplayName", response.getDisplayName());
assertNotNull(response.getSessionId());
}
}
Loading

0 comments on commit 80d0597

Please sign in to comment.