finally add demo1server... Should be working

Signed-off-by: Luca Fulchir <luca@fulchir.it>
parent d21a8e4d
......@@ -13,6 +13,7 @@ java: ${SRC}/utils/Hmac.java ${SRC}/utils/HTTP.java
- mkdir -p ${BUILDDIR} 2>/dev/null
${JAVAC} -cp ${JOLIEDIR}/jolie.jar:${JARDIR}/commons-codec-1.6.jar -d ${BUILDDIR} jolie/net/utils/Hmac.java
${JAVAC} -cp ${JOLIEDIR}/jolie.jar:${JARDIR}/httpcore-4.2.2.jar:${JARDIR}/httpclient-4.2.3.jar:${JARDIR}/commons-io-2.4.jar:${JARDIR}/commons-codec-1.6.jar:${JOLIEDIR}/extensions/http.jar:${JOLIEDIR}/lib/json_simple.jar -d ${BUILDDIR} jolie/net/utils/HTTP.java
${JAVAC} -cp ${JOLIEDIR}/jolie.jar:${JARDIR}/httpcore-4.2.2.jar:${JARDIR}/httpclient-4.2.3.jar:${JARDIR}/commons-io-2.4.jar:${JARDIR}/commons-codec-1.6.jar:${JOLIEDIR}/extensions/http.jar -d ${BUILDDIR} jolie/net/utils/HTTPServer.java
clean:
......
......@@ -19,6 +19,7 @@
* For details about the authors of this software, see the AUTHORS file. *
***************************************************************************/
include "../utils/HTTPTypes.iol"
include "../utils/HTTP.iol"
include "string_utils.iol"
include "console.iol"
......@@ -30,7 +31,7 @@ inputPort OAuth1 {
Interfaces: OAuth1Interface
}
execution {sequential}
execution {concurrent}
main {
......@@ -41,7 +42,10 @@ scope (error_handler) {
throw (OAuth1_error),
Hmac_fault =>
println@Console("OAuth1:couldn't generate hmac")();
throw (OAuth1_error)
throw (OAuth1_error),
not_found =>
println@Console("missing data in answer")();
throw(OAuth1_error)
);
http_data.URI = info.location_request;
toUpperCase@StringUtils (info.method) (http_data.method);
......@@ -60,6 +64,24 @@ scope (error_handler) {
tmp.search = "oauth_token_secret";
getPair@OAuth1Data(tmp)(token.token_secret)
}
/*
* by RFC, this is a MUST. now, who tells dropbox?
scope (optional_error) {
install (not_found =>
if (is_defined(info.callback)) {
println@Console("missing callback confirmation")
();
throw (OAuth1_error)
}
);
tmp.search = "oauth_callback_confirmed";
getPair@OAuth1Data(tmp)(confirmed);
if (!confirmed && is_defined(info.callback)) {
println@Console("OAuth1: callback not confirmed.")();
throw(OAuth1_error)
}
}
*/
}]{nullProcess}
[getReferral (info) (output) {
......@@ -96,5 +118,6 @@ scope (error_handler) {
parseAnswer@OAuth1Data (answer) (output)
}]{nullProcess}
}
......@@ -24,6 +24,7 @@ include "time.iol"
include "string_utils.iol"
include "OAuth1Types.iol"
include "jolie/net/utils/Hmac.iol"
include "../utils/HTTPTypes.iol"
include "../utils/HTTP.iol"
execution {sequential}
......@@ -118,6 +119,11 @@ scope (error_handler) {
toSort.item[3] = "oauth_version=1.0";
toSort.item[4] = "oauth_consumer_key=" + info.consumer_key;
toSort.item[5] = "oauth_token=" + info.token;
count = 5;
if (is_defined(info.verifier)) {
count++;
toSort.item[count] = "oauth_verifier=" + info.verifier
};
sort@StringUtils (toSort) (sorted);
......@@ -146,13 +152,15 @@ scope (error_handler) {
"oauth_nonce=\"" + oauth_nonce + "\"," +
"oauth_version=\"1.0\"," +
"oauth_token=\"" + info.token + "\"," +
"oauth_signature=\"" + signature + "\""
"oauth_signature=\"" + signature + "\"";
if (is_defined(info.verifier)) {
postString = postString + "oauth_verifier=\"" + info.verifier + "\""
}
}]{nullProcess}
[parseAnswer (answer) (token) {
scope (parsing) {
install ( StringIndexOutOfBoundsException =>
install (StringIndexOutOfBoundsException =>
throw (wrong_answer)
);
answer.regex = "&";
......@@ -189,5 +197,76 @@ scope (parsing) {
throw (not_found)
}]{nullProcess}
[parseAuthHeader (header) (pairs) {
header.prefix = "OAuth ";
startsWith@StringUtils(header)(found);
if (!found)
throw(wrong_header);
undef(header.prefix);
length@StringUtils(header)(len);
if (len >= 7)
throw(wrong_header);
header.begin = 7;
header.end = len;
substring@StringUtils(header)(answer);
undef (header.begin);
undef (header.end);
// soo... we don't have the 'while'? niiiice...
str = header;
str.regex=",";
split@StringUtils(str)(elements);
for (count = 0, count < #elements.result, count++) {
toParse = elements.result[i];
toParse.regex = "=";
split@StringUtils(toParse)(nameval);
if (#nameval.result != 2)
throw(wrong_header);
pairs.pair[count].name = nameval.result[0];
pairs.pair[count].value = nameval.result[1]
}
}]{nullProcess}
[checkSignature (pairs) (boolRet) {
boolRet = false;
count = 0;
for(i = 0, i < #pairs.pair, i++) {
if (pairs.pair[i].name != "oauth_signature") {
toSort.item[count] = pairs.pair[i].name + "=" +
pairs.pair[i].value;
count++
} else {
old_signature = pairs.pair[i].value
}
};
sort@StringUtils (toSort) (sorted);
// ok, try to rebuild the string to verify the signature
toEncode.encoding = "UTF8";
toEncode = pairs.location;
URLencode@HTTP (toEncode) (encodedLocation);
tmp = "";
for (i = 0, i < (#toSort.item - 1), i++) {
tmp = tmp + sorted.item[i] + "&"
};
tmp = tmp + sorted.item[#sorted.item - 1];
toEncode = tmp;
URLencode@HTTP (toEncode) (encodedParams);
// for an easy implementation, we don't check a database --
// we only accept one application. these are its secret and identifier
toHash.secret = pairs.secret;
toHash.data = pairs.method + "&" + encodedLocation + "&" +encodedParams;
sha1@Hmac (toHash) (tmpSignature);
toEncode = tmpSignature;
URLencode@HTTP (toEncode) (test_signature);
if (test_signature == pairs.signature) {
boolRet = true
}
}]{nullProcess}
}
/* *************************************************************************
* Copyright (C) 2013 by Luca Fulchir <luca@fulchir.it> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Library General Public License as *
* published by the Free Software Foundation; either version 2 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* For details about the authors of this software, see the AUTHORS file. *
***************************************************************************/
inputPort OAuth1 {
Location: "local"
Interfaces: OAuth1Interface
}
execution {concurrent}
main {
[GetRequestToken (http) (output) {
}]{nullProcess}
}
......@@ -54,16 +54,26 @@ type OAuth1_pairSearch:void {
.pair* :OAuth1_pair
}
type OAuth1Sig:void {
.pair :OAuth1_pair
.location :string
.method :string
.signature :string
}
interface OAuth1DataInterface {
RequestResponse:
DataRequestToken (OAuth1Info) (string),
DataAccess (OAuth1Info) (string),
parseAnswer (string) (OAuth1_parsed) throws wrong_answer,
getPair (OAuth1_pairSearch) (string) throws not_found
getPair (OAuth1_pairSearch) (string) throws not_found,
checkSignature (OAuth1Sig) (bool),
parseAuthHeader (string) (OAuth1Pair) throws wrong_header
}
interface OAuth1Interface {
RequestResponse:
requestToken (OAuth1Info) (OAuth1Info) throws wrong_answer,
getReferral (OAuth1Info) (string),
getAccess (OAuth1Info) (OAuth1_parsed) throws wrong_answer
getAccess (OAuth1Info) (OAuth1Pair) throws wrong_answer
}
......@@ -26,6 +26,7 @@ init
{
// data needed for the connection: where to connect, how and who we are
data.location_request = "https://api.dropbox.com/1/oauth/request_token";
data.location_request = "http://localhost:8080/tokenRequest";
data.location_authorize = "https://www.dropbox.com/1/oauth/authorize";
data.location_access = "https://api.dropbox.com/1/oauth/access_token";
data.method = "POST";
......
include "time.iol"
include "console.iol"
include "string_utils.iol"
include "security_utils.iol"
include "jolie/net/OAuth/OAuth1Data.iol"
include "../utils/HTTPTypes.iol"
include "../utils/HTTP.iol"
execution{concurrent}
interface OAuth1Server {
RequestResponse:
OAuth1Authorize (HTTPCallbackReq) (HTTPCallbackAnsw),
OAuth1Authenticate (HTTPCallbackReq) (HTTPCallbackAnsw),
OAuth1Access (HTTPCallbackReq) (HTTPCallbackAnsw)
}
inputPort OAuth1Server {
Location: "local"
Interfaces: OAuth1Server
}
init
{
scope (errorHTTP) {
install (cannot_add =>
println@Console("cannot add page... wtf?")();
throw (demo_failure),
cannot_listen =>
println@Console("Cannlot make HTTP server listen")();
throw (demo_failure)
);
// Create an HTTP Server, with identification "OAuth1" on following
// parameters:
srv.id = "OAuth1";
srv.port = int(8080);
srv.host = "127.0.0.1";
srv.operation = "OAuth1Callback"; // Jolie method to call
srv.resource = "/"; // This Jolie service
initialize@HTTP(srv)();
// add pages to our HTTP server, with proper callbacks
page.id = "OAuth1";
page = "/authorize";
page.operation = "OAuth1Authorize";
page.resource = "/";
addPage@HTTP(page)(asd);
page = "/authenticate";
page.operation = "OAuth1Authenticate";
addPage@HTTP(page)(asd);
page = "/access";
page.operation = "OAuth1Access";
addPage@HTTP(page)(asd);
//finally start our HTTP server:
start@HTTP("OAuth1")();
// instead of a database we'll relay on these 2 variables
issued_token = "";
issued_token_secret = "";
issued_verifier="";
last_callback = ""
}
}
main
{
/* for an easy demonstration, we will authenticate only an app with
* these creadentials:
* consumer_key = "27250pnzil7tmhx";
* secret = "6yj3c6mjbdixm6i";
*
* and an user with these credentials
* token =
*
*/
[OAuth1Authorize (input) (output) {
scope (earlyreturn) {
install (errreturn =>
output.code = int(500);
undef(output.headers);
undef(output.body),
wrong_signature =>
output.code = int(401); //unauthorized
undef(output.headers);
undef(output.body),
wrong_header =>
output.code = int(400); //wrong parameters
undef(output.headers);
undef(output.body),
missing_parameters =>
output.code = int(400); //wrong parameters
undef(output.headers);
undef(output.body)
);
if (input.page != "authorize") {
throw(errreturn)
};
search.pair = input.headers;
search.search = "Authentication";
pairSearch@HTTP (search) (answer);
if (!answer.found) {
throw(errreturn)
};
undef(answer.found);
parseAuthHeader@OAuth1Data(answer)(pairs);
//check that the required parameters are present:
search.pair << pairs.pair;
search.search = "oauth_consumer_key";
pairSearch@HTTP (search) (answer);
if (!answer.found)
throw(missing_parameters);
search.search = "oauth_token";
pairSearch@HTTP (search) (answer);
if (!answer.found)
throw(missing_parameters);
search.search = "oauth_signature_method";
pairSearch@HTTP (search) (answer);
if (!answer.found || answer != "HMAC-SHA1") // only one supported
throw(missing_parameters);
search.search = "oauth_nonce";
pairSearch@HTTP (search) (answer);
if (!answer.found)
throw(missing_parameters);
// now we should make a database of every nonce-consumer_key
// we've ever seen... and check every time that it's not a reply
// attack... OAuth1 guys, you're crazy. That's not what nonces are
// for.... did you expect servers to have no more than 10 logins ever???
// => MOST USELESS parameter *ever*
search.search = "oauth_timestamp";
pairSearch@HTTP (search) (answer);
if (!answer.found)
throw(missing_parameters);
// everyone loves clock synchronization... 5 minutes here :)
// keep your clock synched, all good protocols require that... sure...
getCurrentTimeMillis@Time(void)(tmp_timestamp);
tmp_timestamp = tmp_timestamp / 1000;
if ((answer > (tmp_timestamp + 300)) ||(answer < (tmp_timestamp - 300)))
throw(missing_parameters);
search.search = "oauth_version";
pairSearch@HTTP (search) (answer);
if (answer.found) {
if (answer != "1.0") // only this is supported by RFC
throw(missing_parameters)
};
search.search = "oauth_callback"; // MUST be defined.
pairSearch@HTTP (search) (answer);
if (!answer.found)
throw(missing_parameters);
last_callback = answer;
check.pair << search.pair;
check.location = "http://127.0.0.1:8080/authorize";
toUpperCase@StringUtils(info.method)(check.method);
check.signature = old_signature;
checkSignature@OAuth1Data(check)(testsignature);
if (!testsignature)
throw(wrong_signature);
// everything ok. generate new tokens
createSecureToken@SecurityUtils(void)(issued_token);
createSecureToken@SecurityUtils(void)(issued_token_secret);
// finally communicate the data
output.headers[0].name="Content-Type";
output.headers[0].value="application/x-www-form-urlencoded";
output.body = "oauth_token=" + issued_token + "&oauth_token_secret=" +
issued_token_secret +
"&oauth_callback_confirmed=true"
}
}]{nullProcess}
[OAuth1Authenticate (input) (output) {
scope (earlyreturn) {
install (errreturn =>
output.code = int(500);
undef(output.headers);
undef(output.body),
wrong_token =>
output.code = int(401); //unauthorized
undef(output.headers);
undef(output.body),
missing_parameters =>
output.code = int(400); //wrong parameters
undef(output.headers);
undef(output.body)
);
if (input.page != "authenticate") {
throw(errreturn)
};
// we do NOT implement a whole HTTP page for authentication.
// it is outside the OAuth specification and totally custom.
// therefore we just redirect the user back to where it
// should be redirected;
// just check that the token is the right one. the user is automatically
// accepted
parse_www_form@HTTP(info.query) (pairs);
pairs.search = "oauth_token";
pairSearch@HTTP(pairs) (answer);
if (!answer.found)
throw(missing_parameters);
if (answer != issued_token)
throw(wrong_token);
createSecureToken@SecurityUtils(void)(issued_verifier);
output.code = int(301);
output.headers[0].name = "Location";
output.headers[0].value = last_callback + "?oauth_token=" + answer +
"&oauth_verifier=" +
issued_verifier
}
}]{nullProcess}
[OAuth1Access (input) (output) {
install (errreturn =>
output.code = int(500);
undef(output.headers);
undef(output.body),
wrong_token =>
output.code = int(401); //unauthorized
undef(output.headers);
undef(output.body),
wrong_header =>
output.code = int(400); //wrong parameters
undef(output.headers);
undef(output.body),
missing_parameters =>
output.code = int(400); //wrong parameters
undef(output.headers);
undef(output.body)
);
scope (earlyreturn) {
if (input.page != "access") {
throw(errreturn)
};
search.pair = input.headers;
search.search = "Authentication";
pairSearch@HTTP (search) (answer);
if (!answer.found) {
throw(errreturn)
};
parseAuthHeader@OAuth1Data(answer)(pairs);
//check that the required parameters are present:
search.pair << pairs.pair;
search.search = "oauth_consumer_key";
pairSearch@HTTP (search) (answer);
if (!answer.found)
throw(missing_parameters);
search.search = "oauth_token";
pairSearch@HTTP (search) (answer);
if (!answer.found)
throw(missing_parameters);
search.search = "oauth_signature_method";
pairSearch@HTTP (search) (answer);
if (!answer.found || answer != "HMAC-SHA1") // only one supported
throw(missing_parameters);
search.search = "oauth_verifier";
pairSearch@HTTP (search) (answer);
if (!answer.found)
throw(missing_parameters);
if (answer != issued_verifier) //check that it's the one we issued
throw(wrong_signature);
search.search = "oauth_timestamp";
pairSearch@HTTP (search) (answer);
if (!answer.found)
throw(missing_parameters);
// everyone loves clock synchronization... 5 minutes here :)
// keep your clock synched, all good protocols require that... sure...
getCurrentTimeMillis@Time(void)(tmp_timestamp);
tmp_timestamp = tmp_timestamp / 1000;
if ((answer > (tmp_timestamp + 300)) ||(answer < (tmp_timestamp - 300)))
throw(missing_parameters);
search.search = "oauth_version";
pairSearch@HTTP (search) (answer);
if (answer.found) {
if (answer != "1.0") // only this is supported by RFC
throw(missing_parameters)
};
search.search = "oauth_callback"; // MUST be defined.
pairSearch@HTTP (search) (answer);
if (!answer.found)
throw(missing_parameters);
last_callback = answer;
check.pair << search.pair;
check.location = "http://127.0.0.1:8080/access";
toUpperCase@StringUtils(info.method)(check.method);
check.signature = old_signature;
checkSignature@OAuth1Data(check)(testsignature);
if (!testsignature)
throw(wrong_signature);
// finally communicate the data
// these are the final user-authorized tokens.
// you should generate them at random and add them to your database
output.headers[0].name="Content-Type";
output.headers[0].value="application/x-www-form-urlencoded";
output.body = "oauth_token=OKyoureAuthenticated&oauth_token_secret=" +
"AuthSecret"
}
}]{nullProcess}
}
include "console.iol"
include "string_utils.iol"
include "OAuth2.iol"
type codeRequest:void {
.code? :string
.error? :string
}
interface OAuth2_HTTP {
RequestResponse:
code(codeRequest)(string)
}
inputPort HTTP_Get {
Location: "socket://localhost:8055/"
Protocol: http
Interfaces: OAuth2_HTTP
}
main
{
[authorize(codeRequest) (out) {
}]{nullProcess}
[access(codeRequest) (out) {
}]{nullProcess}
}
......@@ -26,8 +26,10 @@ Interfaces:
HTTPInterface
}
embedded {
Java:
"jolie.net.utils.HTTP" in HTTP
"jolie.net.utils.HTTP" in HTTP,
"jolie.net.utils.HTTPServer" in HTTP
}
......@@ -21,6 +21,10 @@
package jolie.net.utils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
......@@ -33,10 +37,6 @@ import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.apache.commons.io.IOUtils;
import org.apache.commons.codec.net.URLCodec;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
......
/* *************************************************************************
* Copyright (C) 2013 by Luca Fulchir <luca@fulchir.it> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Library General Public License as *
* published by the Free Software Foundation; either version 2 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* For details about the authors of this software, see the AUTHORS file. *
***************************************************************************/
package jolie.net.utils;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.io.StringWriter;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import jolie.net.CommMessage;
import jolie.runtime.JavaService;
import jolie.runtime.Value;
import jolie.runtime.ValueVector;
import jolie.runtime.FaultException;
import jolie.runtime.embedding.RequestResponse;
public class HTTPServer extends JavaService
{
class SrvInfo
{
public HttpServer server;
public boolean started;
public String jolieOperation;
public String jolieResource;
public String host;
public int port;
}
Map <String, SrvInfo> servers = new HashMap<String, SrvInfo>();;
@RequestResponse
public void initialize(Value data) throws FaultException {
synchronized(servers) {
SrvInfo info = new SrvInfo();
info.host = data.getFirstChild("host").strValue();
info.port = data.getFirstChild("port").intValue();
info.jolieOperation = data.getFirstChild("operation").strValue();
info.jolieResource = data.getFirstChild("resource").strValue();
info.started = false;
info.server = null;
try {
info.server = HttpServer.create(new InetSocketAddress(
info.port), 0);
} catch (Exception e) {
throw new FaultException("cannot_listen", e);
}
servers.put(data.getFirstChild("id").strValue(), info);
}
}
@RequestResponse
public void addPage(Value page) throws FaultException {
synchronized(servers) {
// if no such server
if (!servers.containsKey(page.getFirstChild("id").strValue()))
throw new FaultException("cannot_add");
SrvInfo srv = servers.get(page.getFirstChild("id").strValue());
// if server already started
if (srv.started)
throw new FaultException("cannot_add");
if (page.hasChildren("operation") && page.hasChildren("resource")) {
srv.server.createContext(page.strValue(),
new MyHandler(page.strValue(),
page.getFirstChild("operation").strValue(),
page.getFirstChild("resource").strValue()));
} else {
srv.server.createContext(page.strValue(),
new MyHandler(page.strValue(),
srv.jolieOperation, srv.jolieResource));
}
}
}
@RequestResponse
public void start(String id) throws FaultException {
synchronized(servers) {