Added JsonPage and finished implementation of digest auth page
This commit is contained in:
parent
2c7a9a6eff
commit
26c09452f3
10 changed files with 244 additions and 52 deletions
|
|
@ -114,6 +114,7 @@ public class IOUtil {
|
||||||
while((line = in.readLine()) != null){
|
while((line = in.readLine()) != null){
|
||||||
str.append(line).append("\n");
|
str.append(line).append("\n");
|
||||||
}
|
}
|
||||||
|
if (str.length() > 0)
|
||||||
str.delete(str.length()-1, str.length()); // remove last new line
|
str.delete(str.length()-1, str.length()); // remove last new line
|
||||||
|
|
||||||
if (close) reader.close();
|
if (close) reader.close();
|
||||||
|
|
|
||||||
|
|
@ -32,12 +32,12 @@ import java.util.Iterator;
|
||||||
|
|
||||||
public class HttpHeader {
|
public class HttpHeader {
|
||||||
// HTTP info
|
// HTTP info
|
||||||
private boolean request;
|
private boolean request = true;
|
||||||
private String type;
|
private String type = "GET";
|
||||||
private String url;
|
private String url = "/";
|
||||||
private HashMap<String, String> urlAttributes;
|
private HashMap<String, String> urlAttributes;
|
||||||
private float version;
|
private float version = 1.0f;
|
||||||
private int httpCode;
|
private int httpCode = 200;
|
||||||
private InputStream in;
|
private InputStream in;
|
||||||
|
|
||||||
// Parameters
|
// Parameters
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ import zutil.parser.URLDecoder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|
@ -44,7 +44,7 @@ public class HttpHeaderParser {
|
||||||
|
|
||||||
|
|
||||||
private InputStream in;
|
private InputStream in;
|
||||||
private boolean readStatusLine;
|
private boolean skipStatusLine;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -54,7 +54,7 @@ public class HttpHeaderParser {
|
||||||
*/
|
*/
|
||||||
public HttpHeaderParser(InputStream in){
|
public HttpHeaderParser(InputStream in){
|
||||||
this.in = in;
|
this.in = in;
|
||||||
this.readStatusLine = true;
|
this.skipStatusLine = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -72,7 +72,7 @@ public class HttpHeaderParser {
|
||||||
String line;
|
String line;
|
||||||
|
|
||||||
// First line
|
// First line
|
||||||
if (readStatusLine) {
|
if (!skipStatusLine) {
|
||||||
if( (line=IOUtil.readLine(in)) != null && !line.isEmpty() )
|
if( (line=IOUtil.readLine(in)) != null && !line.isEmpty() )
|
||||||
parseStatusLine(header, line);
|
parseStatusLine(header, line);
|
||||||
else
|
else
|
||||||
|
|
@ -90,10 +90,10 @@ public class HttpHeaderParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param readStatusLine indicates if the stream contains http status lines. (default: true)
|
* @param skipStatusLine indicates if the parser should expect a http status lines. (default: true)
|
||||||
*/
|
*/
|
||||||
public void setReadStatusLine(boolean readStatusLine){
|
public void skipStatusLine(boolean skipStatusLine){
|
||||||
this.readStatusLine = readStatusLine;
|
this.skipStatusLine = skipStatusLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -153,9 +153,25 @@ public class HttpHeaderParser {
|
||||||
public static void parseCookieValues(Map<String,String> map, String cookieValue){
|
public static void parseCookieValues(Map<String,String> map, String cookieValue){
|
||||||
parseHeaderValues(map, cookieValue, ";");
|
parseHeaderValues(map, cookieValue, ";");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a header value string that contains key and value paired data and
|
* Parses a header value string that contains key and value paired data and
|
||||||
* stores them in a HashMap. If a pair only contain a key the the value
|
* stores them in a HashMap. If a pair only contain a key then the value
|
||||||
|
* will be set as a empty string.
|
||||||
|
*
|
||||||
|
* TODO: method is not quote aware
|
||||||
|
* @param headerValue the raw header value String that will be parsed.
|
||||||
|
* @param delimiter the delimiter that separates key and value pairs (e.g. ';' for Cookies or ',' for Cache-Control)
|
||||||
|
*/
|
||||||
|
public static HashMap<String, String> parseHeaderValues(String headerValue, String delimiter){
|
||||||
|
HashMap<String, String> map = new HashMap<>();
|
||||||
|
parseHeaderValues(map, headerValue, delimiter);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Parses a header value string that contains key and value paired data and
|
||||||
|
* stores them in a HashMap. If a pair only contain a key then the value
|
||||||
* will be set as a empty string.
|
* will be set as a empty string.
|
||||||
*
|
*
|
||||||
* TODO: method is not quote aware
|
* TODO: method is not quote aware
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ public class HttpPrintStream extends OutputStream{
|
||||||
*/
|
*/
|
||||||
public HttpPrintStream(OutputStream out, HttpMessageType type) {
|
public HttpPrintStream(OutputStream out, HttpMessageType type) {
|
||||||
this.out = new PrintStream(out);
|
this.out = new PrintStream(out);
|
||||||
this.httpVersion = "1.1";
|
this.httpVersion = "1.0";
|
||||||
this.message_type = type;
|
this.message_type = type;
|
||||||
this.res_status_code = 200;
|
this.res_status_code = 200;
|
||||||
this.headers = new HashMap<String, String>();
|
this.headers = new HashMap<String, String>();
|
||||||
|
|
|
||||||
|
|
@ -98,15 +98,18 @@ public class HttpServer extends ThreadedTCPNetworkServer{
|
||||||
private class SessionGarbageCollector extends TimerTask {
|
private class SessionGarbageCollector extends TimerTask {
|
||||||
public void run(){
|
public void run(){
|
||||||
Object[] keys = sessions.keySet().toArray();
|
Object[] keys = sessions.keySet().toArray();
|
||||||
|
int count = 0;
|
||||||
for(Object key : keys){
|
for(Object key : keys){
|
||||||
Map<String,Object> session = sessions.get(key);
|
Map<String,Object> session = sessions.get(key);
|
||||||
|
|
||||||
// Check if session is still valid
|
// Check if session is still valid
|
||||||
if((Long)session.get(SESSION_TTL_KEY) < System.currentTimeMillis()){
|
if((Long)session.get(SESSION_TTL_KEY) < System.currentTimeMillis()){
|
||||||
sessions.remove(key);
|
sessions.remove(key);
|
||||||
logger.fine("Removing Session: "+key);
|
++count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (count > 0)
|
||||||
|
logger.fine("Removed "+count+" old sessions");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,7 +144,6 @@ public class HttpServer extends ThreadedTCPNetworkServer{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int noOfConnections = 0;
|
|
||||||
/**
|
/**
|
||||||
* Internal class that handles all the requests
|
* Internal class that handles all the requests
|
||||||
*
|
*
|
||||||
|
|
@ -160,8 +162,6 @@ public class HttpServer extends ThreadedTCPNetworkServer{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run(){
|
public void run(){
|
||||||
//logger.finest("New Connection: "+socket.getInetAddress()+" (Ongoing connections: "+(++noOfConnections)+")");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//**************************** PARSE REQUEST *********************************
|
//**************************** PARSE REQUEST *********************************
|
||||||
long time = System.currentTimeMillis();
|
long time = System.currentTimeMillis();
|
||||||
|
|
@ -171,7 +171,6 @@ public class HttpServer extends ThreadedTCPNetworkServer{
|
||||||
logger.finer("No header received");
|
logger.finer("No header received");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String tmp = null;
|
|
||||||
|
|
||||||
//******* Read in the post data if available
|
//******* Read in the post data if available
|
||||||
if (header.getHeader("Content-Length") != null &&
|
if (header.getHeader("Content-Length") != null &&
|
||||||
|
|
@ -213,7 +212,6 @@ public class HttpServer extends ThreadedTCPNetworkServer{
|
||||||
out.setStatusCode(200);
|
out.setStatusCode(200);
|
||||||
out.setHeader("Server", SERVER_VERSION);
|
out.setHeader("Server", SERVER_VERSION);
|
||||||
out.setHeader("Content-Type", "text/html");
|
out.setHeader("Content-Type", "text/html");
|
||||||
//out.setHeader("Connection", "keep-alive");
|
|
||||||
out.setCookie(SESSION_ID_KEY, "" + session.get(SESSION_ID_KEY));
|
out.setCookie(SESSION_ID_KEY, "" + session.get(SESSION_ID_KEY));
|
||||||
|
|
||||||
if (header.getRequestURL() != null && pages.containsKey(header.getRequestURL())) {
|
if (header.getRequestURL() != null && pages.containsKey(header.getRequestURL())) {
|
||||||
|
|
@ -252,7 +250,6 @@ public class HttpServer extends ThreadedTCPNetworkServer{
|
||||||
out.close();
|
out.close();
|
||||||
in.close();
|
in.close();
|
||||||
socket.close();
|
socket.close();
|
||||||
//logger.finest("Connection Closed: "+socket.getInetAddress()+" (Ongoing connections: "+(--noOfConnections)+")");
|
|
||||||
} catch( Exception e ) {
|
} catch( Exception e ) {
|
||||||
logger.log(Level.WARNING, "Could not close connection", e);
|
logger.log(Level.WARNING, "Could not close connection", e);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
package zutil.net.http.page;
|
package zutil.net.http.page;
|
||||||
|
|
||||||
import zutil.Encrypter;
|
import sun.net.www.HeaderParser;
|
||||||
import zutil.Hasher;
|
import zutil.Hasher;
|
||||||
|
import zutil.log.LogUtil;
|
||||||
import zutil.net.http.HttpHeader;
|
import zutil.net.http.HttpHeader;
|
||||||
|
import zutil.net.http.HttpHeaderParser;
|
||||||
import zutil.net.http.HttpPage;
|
import zutil.net.http.HttpPage;
|
||||||
import zutil.net.http.HttpPrintStream;
|
import zutil.net.http.HttpPrintStream;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A abstract page that requires HTTP Digest authentication
|
* A abstract page that requires HTTP Digest authentication
|
||||||
|
|
@ -18,7 +22,9 @@ import java.util.Map;
|
||||||
* @author Ziver
|
* @author Ziver
|
||||||
*/
|
*/
|
||||||
public abstract class HttpDigestAuthPage implements HttpPage{
|
public abstract class HttpDigestAuthPage implements HttpPage{
|
||||||
private static final String DEAFULT_REALM = "Login";
|
private static final Logger logger = LogUtil.getLogger();
|
||||||
|
|
||||||
|
private static final String DEFAULT_REALM = "Login";
|
||||||
private static final String HTTP_AUTH_HEADER = "WWW-Authenticate";
|
private static final String HTTP_AUTH_HEADER = "WWW-Authenticate";
|
||||||
private static final String HTTP_CLIENT_HEADER = "Authorization";
|
private static final String HTTP_CLIENT_HEADER = "Authorization";
|
||||||
private static final String AUTH_TYPE = "Digest";
|
private static final String AUTH_TYPE = "Digest";
|
||||||
|
|
@ -29,9 +35,11 @@ public abstract class HttpDigestAuthPage implements HttpPage{
|
||||||
private static final String AUTH_USERNAME = "username";
|
private static final String AUTH_USERNAME = "username";
|
||||||
private static final String AUTH_URI = "uri";
|
private static final String AUTH_URI = "uri";
|
||||||
private static final String AUTH_RESPONSE = "response";
|
private static final String AUTH_RESPONSE = "response";
|
||||||
|
private static final String AUTH_DELIMITER = ",";
|
||||||
|
|
||||||
|
|
||||||
private String realm = DEAFULT_REALM;
|
private String realm = DEFAULT_REALM;
|
||||||
|
private HashMap<String,String> userMap = new HashMap<>();
|
||||||
private SecureRandom secRandom = new SecureRandom();
|
private SecureRandom secRandom = new SecureRandom();
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -40,6 +48,12 @@ public abstract class HttpDigestAuthPage implements HttpPage{
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addUser(String username, char[] password) {
|
||||||
|
userMap.put(username, new String(password)); // TODO: should use char[] for passwords
|
||||||
|
}
|
||||||
|
public void removeUser(String username) {
|
||||||
|
userMap.remove(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -49,17 +63,51 @@ public abstract class HttpDigestAuthPage implements HttpPage{
|
||||||
Map<String, String> cookie,
|
Map<String, String> cookie,
|
||||||
Map<String, String> request) throws IOException {
|
Map<String, String> request) throws IOException {
|
||||||
|
|
||||||
if (headers.getHeader(HTTP_CLIENT_HEADER) == null) {
|
if (headers.getHeader(HTTP_CLIENT_HEADER) == null || !session.containsKey(AUTH_NONCE)) {
|
||||||
session.put(AUTH_NONCE, generateNonce());
|
session.put(AUTH_NONCE, generateNonce());
|
||||||
out.setStatusCode(401);
|
out.setStatusCode(401);
|
||||||
out.setHeader(HTTP_AUTH_HEADER, generateAuthHeader((String) session.get(AUTH_NONCE)));
|
out.setHeader(HTTP_AUTH_HEADER, generateAuthHeader((String) session.get(AUTH_NONCE)));
|
||||||
|
out.println("401 Unauthorized");
|
||||||
|
}
|
||||||
|
else if ( ! headers.getHeader(HTTP_CLIENT_HEADER).startsWith(AUTH_TYPE)){
|
||||||
|
out.setStatusCode(501);
|
||||||
|
out.println("501 Not Implemented");
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
HashMap<String,String> authMap = HttpHeaderParser.parseHeaderValues(
|
||||||
|
headers.getHeader(HTTP_CLIENT_HEADER).substring(AUTH_TYPE.length()+1), // Skip auth type
|
||||||
|
AUTH_DELIMITER);
|
||||||
|
if (authenticate(
|
||||||
|
authMap.get(AUTH_USERNAME),
|
||||||
|
headers.getRequestURL(),
|
||||||
|
(String)session.get(AUTH_NONCE),
|
||||||
|
authMap.get(AUTH_RESPONSE))) {
|
||||||
|
// Safe area, user authenticated
|
||||||
|
logger.fine("User '"+authMap.get(AUTH_USERNAME)+"' has been authenticated for realm '"+realm+"'");
|
||||||
authRespond(out, headers, session, cookie, request);
|
authRespond(out, headers, session, cookie, request);
|
||||||
}
|
}
|
||||||
|
else{
|
||||||
|
out.setStatusCode(403);
|
||||||
|
out.println("403 Forbidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean authenticate(String username, String uri, String nonce, String clientResponse){
|
||||||
|
if (!userMap.containsKey(username)) // do user exist?
|
||||||
|
return false;
|
||||||
|
|
||||||
|
String generatedResponse = generateResponseHash(
|
||||||
|
generateH1(username, userMap.get(username), realm),
|
||||||
|
generateH2(uri),
|
||||||
|
nonce);
|
||||||
|
if (generatedResponse.equals(clientResponse)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private String generateAuthHeader(String nonce){
|
private String generateAuthHeader(String nonce){
|
||||||
StringBuilder str = new StringBuilder();
|
StringBuilder str = new StringBuilder();
|
||||||
str.append(AUTH_TYPE).append(' ');
|
str.append(AUTH_TYPE).append(' ');
|
||||||
|
|
@ -76,7 +124,7 @@ public abstract class HttpDigestAuthPage implements HttpPage{
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String generateH1(String username, String password, String realm) {
|
private static String generateH1(String username, String password, String realm) {
|
||||||
String ha1 = null;
|
String ha1;
|
||||||
// If the algorithm directive's value is "MD5" or unspecified, then HA1 is
|
// If the algorithm directive's value is "MD5" or unspecified, then HA1 is
|
||||||
// HA1=MD5(username:realm:password)
|
// HA1=MD5(username:realm:password)
|
||||||
ha1 = Hasher.MD5(username +":"+ realm +":"+ password);
|
ha1 = Hasher.MD5(username +":"+ realm +":"+ password);
|
||||||
|
|
@ -111,4 +159,5 @@ public abstract class HttpDigestAuthPage implements HttpPage{
|
||||||
Map<String, Object> session,
|
Map<String, Object> session,
|
||||||
Map<String, String> cookie,
|
Map<String, String> cookie,
|
||||||
Map<String, String> request) throws IOException;
|
Map<String, String> request) throws IOException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
35
src/zutil/net/http/page/HttpJsonPage.java
Executable file
35
src/zutil/net/http/page/HttpJsonPage.java
Executable file
|
|
@ -0,0 +1,35 @@
|
||||||
|
package zutil.net.http.page;
|
||||||
|
|
||||||
|
import zutil.net.http.HttpHeader;
|
||||||
|
import zutil.net.http.HttpPage;
|
||||||
|
import zutil.net.http.HttpPrintStream;
|
||||||
|
import zutil.parser.DataNode;
|
||||||
|
import zutil.parser.json.JSONWriter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Ziver on 2016-11-04.
|
||||||
|
*/
|
||||||
|
public abstract class HttpJsonPage implements HttpPage {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void respond(HttpPrintStream out,
|
||||||
|
HttpHeader headers,
|
||||||
|
Map<String, Object> session,
|
||||||
|
Map<String, String> cookie,
|
||||||
|
Map<String, String> request) throws IOException {
|
||||||
|
|
||||||
|
out.setHeader("Content-Type", "application/json");
|
||||||
|
JSONWriter writer = new JSONWriter(out);
|
||||||
|
writer.write(jsonRespond(headers, session, cookie, request));
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected abstract DataNode jsonRespond(HttpHeader headers,
|
||||||
|
Map<String, Object> session,
|
||||||
|
Map<String, String> cookie,
|
||||||
|
Map<String, String> request);
|
||||||
|
}
|
||||||
30
test/zutil/net/http/HttpTestUtil.java
Executable file
30
test/zutil/net/http/HttpTestUtil.java
Executable file
|
|
@ -0,0 +1,30 @@
|
||||||
|
package zutil.net.http;
|
||||||
|
|
||||||
|
import zutil.io.StringOutputStream;
|
||||||
|
import zutil.net.http.page.HttpDigestAuthPageTest;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class HttpTestUtil {
|
||||||
|
public static HashMap<String,Object> session = new HashMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a simple http request on the given page object
|
||||||
|
*/
|
||||||
|
public static HttpHeader makeRequest(HttpPage page) throws IOException {
|
||||||
|
return makeRequest(page, new HttpHeader());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Make a simple http request on the given page object
|
||||||
|
*/
|
||||||
|
public static HttpHeader makeRequest(HttpPage page, HttpHeader headers) throws IOException {
|
||||||
|
StringOutputStream buff = new StringOutputStream();
|
||||||
|
HttpPrintStream out = new HttpPrintStream(buff);
|
||||||
|
page.respond(
|
||||||
|
out, headers, session, new HashMap(), new HashMap());
|
||||||
|
out.flush();
|
||||||
|
HttpHeaderParser parser = new HttpHeaderParser(buff.toString());
|
||||||
|
return parser.read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
package zutil.net.http.page;
|
package zutil.net.http.page;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import zutil.Hasher;
|
import zutil.Hasher;
|
||||||
import zutil.io.StringOutputStream;
|
import zutil.io.IOUtil;
|
||||||
import zutil.net.http.HttpHeader;
|
import zutil.net.http.HttpHeader;
|
||||||
import zutil.net.http.HttpHeaderParser;
|
import zutil.net.http.HttpHeaderParser;
|
||||||
import zutil.net.http.HttpPrintStream;
|
import zutil.net.http.HttpPrintStream;
|
||||||
|
import zutil.net.http.HttpTestUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
@ -18,10 +23,22 @@ import static org.junit.Assert.*;
|
||||||
*/
|
*/
|
||||||
public class HttpDigestAuthPageTest {
|
public class HttpDigestAuthPageTest {
|
||||||
private static final String PAGE_CONTENT = "Hello World!";
|
private static final String PAGE_CONTENT = "Hello World!";
|
||||||
|
private static final String PAGE_USERNAME = "username";
|
||||||
|
private static final String PAGE_PASSWORD = "password";
|
||||||
|
|
||||||
|
private HttpDigestTestPage page;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init(){
|
||||||
|
page = new HttpDigestTestPage();
|
||||||
|
page.addUser(PAGE_USERNAME, PAGE_PASSWORD.toCharArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void cleanRequest() throws IOException {
|
public void cleanRequest() throws IOException {
|
||||||
HttpHeader rspHeader = makeRequest(new HttpHeader());
|
HttpHeader rspHeader = HttpTestUtil.makeRequest(page);
|
||||||
|
|
||||||
assertEquals(401, rspHeader.getHTTPCode());
|
assertEquals(401, rspHeader.getHTTPCode());
|
||||||
assertTrue(rspHeader.getHeader("WWW-Authenticate") != null);
|
assertTrue(rspHeader.getHeader("WWW-Authenticate") != null);
|
||||||
|
|
@ -29,47 +46,60 @@ public class HttpDigestAuthPageTest {
|
||||||
Map<String,String> authHeader = parseAuthHeader(rspHeader);
|
Map<String,String> authHeader = parseAuthHeader(rspHeader);
|
||||||
assertTrue(authHeader.containsKey("realm"));
|
assertTrue(authHeader.containsKey("realm"));
|
||||||
assertTrue(authHeader.containsKey("nonce"));
|
assertTrue(authHeader.containsKey("nonce"));
|
||||||
|
assertThat(IOUtil.readContentAsString(rspHeader.getInputStream()),
|
||||||
|
not(containsString(PAGE_CONTENT)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void authenticate() throws IOException {
|
public void authenticate() throws IOException {
|
||||||
|
HttpHeader rspHeader = authenticate(PAGE_USERNAME, PAGE_PASSWORD);
|
||||||
|
assertEquals(200, rspHeader.getHTTPCode());
|
||||||
|
assertThat(IOUtil.readContentAsString(rspHeader.getInputStream()),
|
||||||
|
containsString(PAGE_CONTENT));
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void wrongUsername() throws IOException {
|
||||||
|
HttpHeader rspHeader = authenticate(PAGE_USERNAME+"wrong", PAGE_PASSWORD);
|
||||||
|
assertEquals(403, rspHeader.getHTTPCode());
|
||||||
|
assertThat(IOUtil.readContentAsString(rspHeader.getInputStream()),
|
||||||
|
not(containsString(PAGE_CONTENT)));
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void wrongPassword() throws IOException {
|
||||||
|
HttpHeader rspHeader = authenticate(PAGE_USERNAME, PAGE_PASSWORD+"wrong");
|
||||||
|
assertEquals(403, rspHeader.getHTTPCode());
|
||||||
|
assertThat(IOUtil.readContentAsString(rspHeader.getInputStream()),
|
||||||
|
not(containsString(PAGE_CONTENT)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public HttpHeader authenticate(String username, String password) throws IOException {
|
||||||
HttpHeader reqHeader = new HttpHeader();
|
HttpHeader reqHeader = new HttpHeader();
|
||||||
HttpHeader rspHeader = makeRequest(reqHeader);
|
HttpHeader rspHeader = HttpTestUtil.makeRequest(page, reqHeader);
|
||||||
Map<String,String> authHeader = parseAuthHeader(rspHeader);
|
Map<String,String> authHeader = parseAuthHeader(rspHeader);
|
||||||
reqHeader = new HttpHeader();
|
reqHeader = new HttpHeader();
|
||||||
|
|
||||||
String realm = authHeader.get("realm");
|
String realm = authHeader.get("realm");
|
||||||
String nonce = authHeader.get("nonce");
|
String nonce = authHeader.get("nonce");
|
||||||
String uri = "/login";
|
String uri = "/login";
|
||||||
|
String ha1 = Hasher.MD5(username+":"+realm+":"+password);
|
||||||
String ha1 = Hasher.MD5("username:password");
|
String ha2 = Hasher.MD5("MD5:" +uri);
|
||||||
String ha2 = Hasher.MD5("MD5:/" +uri);
|
|
||||||
String response = Hasher.MD5(ha1 +":"+ nonce +":"+ ha2);
|
String response = Hasher.MD5(ha1 +":"+ nonce +":"+ ha2);
|
||||||
reqHeader.setHeader("Authorization", "Digest username=\"username\", " +
|
reqHeader.setRequestURL(uri);
|
||||||
|
reqHeader.setHeader("Authorization", "Digest " +
|
||||||
|
"username=\""+username+"\", " +
|
||||||
"realm=\""+realm+"\", " +
|
"realm=\""+realm+"\", " +
|
||||||
"nonce=\""+nonce+"\", " +
|
"nonce=\""+nonce+"\", " +
|
||||||
"uri=\""+uri+"\", " +
|
"uri=\""+uri+"\", " +
|
||||||
"response=\""+response+"\"");
|
"response=\""+response+"\"");
|
||||||
rspHeader = makeRequest(reqHeader);
|
|
||||||
assertEquals(200, rspHeader.getHTTPCode());
|
return HttpTestUtil.makeRequest(page, reqHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static HttpHeader makeRequest(HttpHeader headers) throws IOException {
|
|
||||||
StringOutputStream buff = new StringOutputStream();
|
|
||||||
HttpPrintStream out = new HttpPrintStream(buff);
|
|
||||||
new HttpDigestTestPage().respond(
|
|
||||||
out, headers, new HashMap(), new HashMap(), new HashMap());
|
|
||||||
out.flush();
|
|
||||||
HttpHeaderParser parser = new HttpHeaderParser(buff.toString());
|
|
||||||
return parser.read();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String parseAuthType(HttpHeader headers){
|
public static String parseAuthType(HttpHeader headers){
|
||||||
String tmp = headers.getHeader("WWW-Authenticate");
|
String tmp = headers.getHeader("WWW-Authenticate");
|
||||||
return tmp.substring(0, tmp.indexOf(' '));
|
return tmp.substring(0, tmp.indexOf(' '));
|
||||||
|
|
|
||||||
34
test/zutil/net/http/page/HttpJsonPageTest.java
Executable file
34
test/zutil/net/http/page/HttpJsonPageTest.java
Executable file
|
|
@ -0,0 +1,34 @@
|
||||||
|
package zutil.net.http.page;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import zutil.io.IOUtil;
|
||||||
|
import zutil.net.http.HttpHeader;
|
||||||
|
import zutil.net.http.HttpTestUtil;
|
||||||
|
import zutil.parser.DataNode;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Ziver on 2016-11-04.
|
||||||
|
*/
|
||||||
|
public class HttpJsonPageTest {
|
||||||
|
|
||||||
|
private HttpJsonPage page = new HttpJsonPage() {
|
||||||
|
@Override
|
||||||
|
protected DataNode jsonRespond(HttpHeader headers, Map<String, Object> session, Map<String, String> cookie, Map<String, String> request) {
|
||||||
|
return new DataNode(DataNode.DataType.Map);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void simpleResponse() throws IOException {
|
||||||
|
HttpHeader header = HttpTestUtil.makeRequest(page);
|
||||||
|
assertEquals("application/json", header.getHeader("Content-Type"));
|
||||||
|
assertEquals("{}", IOUtil.readContentAsString(header.getInputStream()));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue