Skip to content

Commit

Permalink
[TECH-1154] OAuth support
Browse files Browse the repository at this point in the history
  • Loading branch information
cstar committed Nov 8, 2010
1 parent 5d7fa29 commit 9dc328f
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 16 deletions.
68 changes: 68 additions & 0 deletions examples/http-oauth.xml.in
@@ -0,0 +1,68 @@
<?xml version="1.0"?>
<!DOCTYPE tsung SYSTEM "@prefix@/share/@PACKAGE_NAME@/@DTD@">
<tsung loglevel="debug" dumptraffic="true" version="1.0">

<clients>
<client host="localhost" weight="1" maxusers="1000" use_controller_vm="true"/>
</clients>

<servers>
<server host="127.0.0.1" port="8000" type="tcp"></server>
</servers>

<load>
<arrivalphase phase="1" duration="2" unit="second">
<users interarrival="1" unit="second"></users>
</arrivalphase>
</load>

<sessions>
<session name="http_test_1" probability="100" type="ts_http">
<!--1. fetching token from SSO
using tsung XPath dynvars, the access_token and secret are stored for future use.
Please note the <oauth/> element with the consumer information.
By default requests are signed with "HMAC-SHA1" so it's not necessary here.
-->
<request>
<dyn_variable name="access_token" xpath="/tokenCreationResponse/tokenInfo/token/text()"/>
<dyn_variable name="access_token_secret" xpath="/tokenCreationResponse/tokenInfo/tokenSecret/text()" />
<http url="/token" method="POST" version="1.1" contents="rien">
<oauth consumer_key="key" consumer_secret="secret" method="HMAC-SHA1"/>
</http>
</request>

<!--2. access to protected resources
Using the variables set from the SSO, we are now able to access the protected resource
-->
<request subst="true">
<http url="/echo" method="GET" version="1.1">
<oauth consumer_key="key" consumer_secret="secret" access_token="%%_access_token%%" access_token_secret="%%_access_token_secret%%"/>
</http>
</request>

<!--3. refreshing token
refreshing the token work mostly as in step 1.
Values are overridden-->

<request subst="true">
<dyn_variable name="access_token" xpath="/tokenCreationResponse/tokenInfo/token/text()"/>
<dyn_variable name="access_token_secret" xpath="/tokenCreationResponse/tokenInfo/tokenSecret/text()" />
<http url="/token/%%_access_token%%" method="PUT" version="1.1">
<oauth consumer_key="key" consumer_secret="secret"/>
</http>
</request>

<!--4. access to protected resources
See step 2.-->

<request subst="true">
<http url="/echo" method="GET" version="1.1">
<oauth consumer_key="key" consumer_secret="secret" access_token="%%access_token_secret%%" access_token_secret="%%access_token_secret%%"/>
</http>
</request>
</session>
</sessions>

</tsung>


4 changes: 4 additions & 0 deletions include/ts_http.hrl
Expand Up @@ -38,6 +38,10 @@
body = [],
id = 0,
user_agent,
oauth_consumer,
oauth_access_token,
oauth_access_secret,
oauth_url,
userid, % for www_authentication
passwd, % for www_authentication
soap_action % for SOAP support
Expand Down
17 changes: 10 additions & 7 deletions src/tsung/ts_http.erl
Expand Up @@ -106,13 +106,13 @@ parse_config(Element, Conf) ->
add_dynparams(false, DynData, Param, HostData) ->
add_dynparams(DynData#dyndata.proto, Param, HostData);
add_dynparams(true, DynData, OldReq=#http_request{url=OldUrl}, HostData) ->
Req = subst(OldReq, DynData#dyndata.dynvars),
Req = subst(OldReq, DynData#dyndata.dynvars),
case Req#http_request.url of
OldUrl ->
OldUrl ->
add_dynparams(DynData#dyndata.proto,Req, HostData);
"http" ++ _Rest -> % URL has changed and is absolute
URL=ts_config_http:parse_URL(Req#http_request.url),
?DebugF("URL dynamic subst: ~p~n",[URL]),
?LOGF("URL dynamic subst: ~p~n",[URL],?WARN),
NewPort = ts_config_http:set_port(URL),
NewUrl = case OldUrl of
"http"++_ -> % old url absolute: useproxy must be true
Expand All @@ -121,17 +121,17 @@ add_dynparams(true, DynData, OldReq=#http_request{url=OldUrl}, HostData) ->
ts_config_http:set_query(URL)
end,
NewReq=add_dynparams(DynData#dyndata.proto,
Req#http_request{url=NewUrl,host_header=undefined},
Req#http_request{url=NewUrl, host_header=undefined},
{URL#url.host, NewPort}),
{NewReq, {URL#url.host, NewPort,ts_config_http:set_scheme(URL#url.scheme)}};
_ -> % Same host:port
add_dynparams(DynData#dyndata.proto, Req, HostData)
end.

%% Function: add_dynparams/3
%% Function: add_dynparams/3
add_dynparams(DynData,Param=#http_request{host_header=undefined}, {Host,Port})->
Header=Host++":"++ integer_to_list(Port),
?DebugF("set host header dynamically: ~s~n",[Header]),
?LOGF("set host header dynamically: ~s~n",[Header],?WARN),
add_dynparams(DynData, Param#http_request{host_header=Header},{Host,Port});
%% no cookies
add_dynparams(#http_dyndata{cookies=[],user_agent=UA},Param, _) ->
Expand All @@ -157,11 +157,14 @@ init_dynparams() ->
%% request parameters.
%% @end
%%----------------------------------------------------------------------
subst(Req=#http_request{url=URL, body=Body, headers = Headers, userid=UserId, passwd=Passwd}, DynData) ->
subst(Req=#http_request{url=URL, body=Body, headers = Headers,oauth_url=OUrl, oauth_access_token=AToken, oauth_access_secret=ASecret, userid=UserId, passwd=Passwd}, DynData) ->
Req#http_request{url = ts_search:subst(URL, DynData),
body = ts_search:subst(Body, DynData),
headers = lists:foldl(fun ({Name, Value}, Result) ->
[{Name, ts_search:subst(Value, DynData)} | Result]
end, [], Headers),
oauth_access_token = ts_search:subst(AToken, DynData),
oauth_access_secret = ts_search:subst(ASecret, DynData),
oauth_url = ts_search:subst(OUrl, DynData),
userid = ts_search:subst(UserId, DynData),
passwd = ts_search:subst(Passwd, DynData)}.
20 changes: 17 additions & 3 deletions src/tsung/ts_http_common.erl
Expand Up @@ -59,12 +59,13 @@ http_get(Args) ->
http_no_body(Method,#http_request{url=URL, version=Version, cookie=Cookie,
headers=Headers, user_agent=UA,
get_ims_date=undefined, soap_action=SOAPAction,
host_header=Host, userid=UserId, passwd=Passwd})->
host_header=Host, userid=UserId, passwd=Passwd}=Req)->
?DebugF("~p ~p~n",[Method,URL]),
R = list_to_binary([Method, " ", URL," ", "HTTP/", Version, ?CRLF,
set_header("Host",Host,Headers, ""),
set_header("User-Agent",UA,Headers, ?USER_AGENT),
authenticate(UserId,Passwd),
oauth_sign(Method,Req),
soap_action(SOAPAction),
set_cookie_header({Cookie, Host, URL}),
headers(Headers),
Expand All @@ -75,14 +76,15 @@ http_no_body(Method,#http_request{url=URL, version=Version, cookie=Cookie,
http_no_body(Method,#http_request{url=URL, version=Version, cookie=Cookie,
headers=Headers, user_agent=UA,
get_ims_date=Date, soap_action=SOAPAction,
host_header=Host, userid=UserId, passwd=Passwd}) ->
host_header=Host, userid=UserId, passwd=Passwd}=Req) ->
?DebugF("~p ~p~n",[Method, URL]),
list_to_binary([Method, " ", URL," ", "HTTP/", Version, ?CRLF,
["If-Modified-Since: ", Date, ?CRLF],
set_header("Host",Host,Headers, ""),
set_header("User-Agent",UA,Headers, ?USER_AGENT),
soap_action(SOAPAction),
authenticate(UserId,Passwd),
oauth_sign(Method,Req),
set_cookie_header({Cookie, Host, URL}),
headers(Headers),
?CRLF]).
Expand All @@ -102,14 +104,15 @@ http_body(Method,#http_request{url=URL, version=Version,
user_agent=UA, soap_action=SOAPAction,
content_type=ContentType,
body=Content, host_header=Host,
userid=UserId, passwd=Passwd}) ->
userid=UserId, passwd=Passwd}=Req) ->
ContentLength=integer_to_list(size(Content)),
?DebugF("Content Length of POST: ~p~n.", [ContentLength]),
H = [Method, " ", URL," ", "HTTP/", Version, ?CRLF,
set_header("Host",Host,Headers, ""),
set_header("User-Agent",UA,Headers, ?USER_AGENT),
authenticate(UserId,Passwd),
soap_action(SOAPAction),
oauth_sign(Method, Req),
set_cookie_header({Cookie, Host, URL}),
headers(Headers),
"Content-Type: ", ContentType, ?CRLF,
Expand All @@ -128,6 +131,17 @@ authenticate(UserId,Passwd)->
AuthStr = ts_utils:encode_base64(lists:append([UserId,":",Passwd])),
["Authorization: Basic ",AuthStr,?CRLF].

oauth_sign(_, #http_request{oauth_consumer = undefined})->[];
oauth_sign(Method, #http_request{url=URL,
oauth_consumer=Consumer,
oauth_access_token=AccessToken,
oauth_access_secret=AccessSecret,
oauth_url=ServerURL})->
UrlParams = oauth_uri:params_from_string(URL),
?LOGF("Signing query with ~p~n",[ServerURL],?WARN),
Params = oauth:signed_params(Method, ServerURL, UrlParams, Consumer, AccessToken, AccessSecret),
["Authorization: OAuth ", oauth_uri:params_to_header_string(Params),?CRLF].

%%----------------------------------------------------------------------
%% @spec set_header(Name::string, Val::string | undefined, Headers::List,
%% Default::string) -> list()
Expand Down
48 changes: 43 additions & 5 deletions src/tsung_controller/ts_config_http.erl
Expand Up @@ -36,7 +36,7 @@
-include("ts_profile.hrl").
-include("ts_http.hrl").
-include("ts_config.hrl").

-include("xmerl.hrl").

%%----------------------------------------------------------------------
Expand All @@ -49,7 +49,7 @@
parse_config(Element = #xmlElement{name=dyn_variable}, Conf = #config{}) ->
ts_config:parse(Element,Conf);
parse_config(Element = #xmlElement{name=http},
Config=#config{curid = Id, session_tab = Tab, servers = Servers,
Config=#config{curid = Id, session_tab = Tab, servers = [Server|_] = Servers,
sessions = [CurS | _], dynvar=DynVar,
subst = SubstFlag, match=MatchRegExp}) ->
Version = ts_config:getAttr(string,Element#xmlElement.attributes, version, "1.1"),
Expand Down Expand Up @@ -96,17 +96,55 @@ parse_config(Element = #xmlElement{name=http},
Request2#http_request.headers),
Request3 = Request2#http_request{headers=Headers,cookie=Cookies},
%% HTTP Authentication
Msg = case lists:keysearch(www_authenticate, #xmlElement.name,
Request4 = case lists:keysearch(www_authenticate, #xmlElement.name,
Element#xmlElement.content) of
{value, AuthEl=#xmlElement{} } ->
UserId= ts_config:getAttr(string,AuthEl#xmlElement.attributes,
userid, undefined),
Passwd= ts_config:getAttr(string,AuthEl#xmlElement.attributes,
passwd, undefined),
NewReq=Request3#http_request{userid=UserId, passwd=Passwd},
Request3#http_request{userid=UserId, passwd=Passwd};
_ ->
Request3
end,
%% OAuth
Msg = case lists:keysearch(oauth, #xmlElement.name,
Element#xmlElement.content) of
{value, AuthEl2=#xmlElement{} } ->
ConsumerKey = ts_config:getAttr(string,AuthEl2#xmlElement.attributes,
consumer_key, undefined),
ConsumerSecret = ts_config:getAttr(string,AuthEl2#xmlElement.attributes,
consumer_secret, undefined),
AccessToken = ts_config:getAttr(string,AuthEl2#xmlElement.attributes,
access_token, []),
AccessTokenSecret = ts_config:getAttr(string,AuthEl2#xmlElement.attributes,
access_token_secret, []),
Encoding = ts_config:getAttr(string,AuthEl2#xmlElement.attributes,
encoding, "HMAC-SHA1"),
AbsoluteURL = case URL of
"http" ++ _Rest ->
hd(string:tokens(URL, "?"));
_Relative ->
server_to_url(Server) ++ hd(string:tokens(URL, "?"))
end,
SigEncoding = case Encoding of
"PLAINTEXT" ->
plaintext;
"HMAC-SHA1" ->
hmac_sha1;
"RSA-SHA1" ->
rsa_sha1
end,
?LOGF("TOKENS : ~p ~p ~p", [AccessToken, URL, AbsoluteURL], ?WARN),
NewReq=Request4#http_request{oauth_access_token=AccessToken,
oauth_url=AbsoluteURL,
oauth_access_secret=AccessTokenSecret,
oauth_consumer={ConsumerKey,
ConsumerSecret,
SigEncoding}},
set_msg(NewReq, {SubstFlag, MatchRegExp, UseProxy, Servers, PreviousHTTPServer, Tab, CurS#session.id} );
_ ->
set_msg(Request3, {SubstFlag, MatchRegExp, UseProxy, Servers, PreviousHTTPServer, Tab, CurS#session.id} )
set_msg(Request4, {SubstFlag, MatchRegExp, UseProxy, Servers, PreviousHTTPServer, Tab, CurS#session.id} )
end,
ts_config:mark_prev_req(Id-1, Tab, CurS),
ets:insert(Tab,{{CurS#session.id, Id},Msg#ts_request{endpage=true,
Expand Down
11 changes: 10 additions & 1 deletion tsung-1.0.dtd
Expand Up @@ -145,7 +145,7 @@ repeat | if | change_type )*>
<!ELEMENT transaction (request | setdynvars | thinktime | for | repeat)+>
<!ATTLIST transaction name NMTOKEN #REQUIRED>

<!ELEMENT http ( www_authenticate?, soap?, http_header*, add_cookie*)>
<!ELEMENT http (oauth?, www_authenticate?, soap?, http_header*, add_cookie*)>
<!ATTLIST http
contents CDATA #IMPLIED
contents_from_file CDATA #IMPLIED
Expand Down Expand Up @@ -184,6 +184,15 @@ repeat | if | change_type )*>
passwd CDATA #REQUIRED
userid CDATA #REQUIRED >

<!ELEMENT oauth EMPTY >
<!ATTLIST oauth
consumer_key CDATA #REQUIRED
consumer_secret CDATA #REQUIRED
access_token CDATA #IMPLIED
access_token_secret CDATA #IMPLIED
method (HMAC-SHA1 | PLAINTEXT | RSA-SHA1) "HMAC-SHA1"
>

<!ELEMENT jabber EMPTY >
<!ATTLIST jabber
ack (global | local | no_ack) #REQUIRED
Expand Down

0 comments on commit 9dc328f

Please sign in to comment.