Fred T-H avatar Fred T-H committed 29dcdc9

Adding the basic authentication calls for chat users

Two calls are added: one for authentication and one to know
if a user X can send a message to user Y.
There were two mock calls added to chut_webserver.erl in
order to allow testing from the shell.

The auth mechanism, once completed, should be documented
in detail.

Comments (0)

Files changed (2)

src/chut_auth.erl

+%%%===================================================================
+%%% chut_auth is a module to be used by the server to contact the
+%%% main site's frontend to know whether a user X can or can't
+%%% contact a user Y.
+%%% This module is also in charge of handling user authentication
+%%% with the main site.
+%%%===================================================================
+-module(chut_auth).
+-export([authentify/3, can_contact/3]).
+-include_lib("eunit/include/eunit.hrl").
+
+-define(AUTH_PATH, "auth").
+-define(CONTACT_PATH, "can-contact").
+-define(CT_POST, {"Content-Type","application/x-www-form-urlencoded"}).
+
+%%--------------------------------------------------------------------
+%% Function: authentify(BaseUrl, User, Token) -> bool().
+%% Description: Send a call to the authentication service located
+%% at BaseUrl to confirm that User can really be authentified with
+%% Token.
+%%
+%% The base site is supposed to have all the real user authentication
+%% and pass a given token to the client side. The client side can then
+%% pas the said token to Chut. This function allows Chut to contact
+%% the base site to close the loop and make sure the token is valid.
+%%--------------------------------------------------------------------
+authentify(BaseUrl, User, Token) ->
+    Body = postify([{"user",User},{"token",Token}]),
+    Url = BaseUrl++?AUTH_PATH,
+    {ok, "200", _Header, Reply} = ibrowse:send_req(Url, [?CT_POST], post, Body),
+    if Reply =:= "true" -> true;
+       Reply =:= "false"  -> false
+    end.
+
+%%--------------------------------------------------------------------
+%% Function: can_contact(BaseUrl, UserA, UserB) -> bool().
+%% Description: Send a call to the authentication service located
+%% at BaseUrl to confirm that UserA can really send messages to UserB.
+%%
+%% Some websites will have some relationships or user permissions
+%% that restrict who can contact who. UserB might have blocked UserA,
+%% or maybe UserA isn't allowed to contact UserB without the right
+%% type of account. All this business logic likely already exists on
+%% the base site, and so we prefer to send the calls there rather
+%% than forcing them to be reimplemented here (with data duplication
+%% and synchronisation being necessary.)
+%%--------------------------------------------------------------------
+can_contact(BaseUrl, UserA, UserB) ->
+    Body = postify([{"from",UserA}, {"to",UserB}]),
+    Url = BaseUrl ++ ?CONTACT_PATH,
+    {ok, "200", _Header, Reply} = ibrowse:send_req(Url, [?CT_POST], post, Body),
+    if Reply =:= "true"  -> true;
+       Reply =:= "false" -> false
+    end.
+
+%%--------------------------------------------------------------------
+%% Function: postify([{Key::string(),Value::string()}]) -> iolist().
+%% Description: Takes a key/value list and returns it concatenated
+%% as a valid POST body.
+%%--------------------------------------------------------------------
+postify(List) ->
+    Body = [[$&, ibrowse_lib:url_encode(Key),
+             $=, ibrowse_lib:url_encode(Val)] || {Key, Val} <- List],
+    %% strip the first '&' and return the rest
+    [[$&|FirstVal]|Rest] = Body,
+    [FirstVal|Rest].
+    

src/chut_webserver.erl

     chut_client:disconnect(Nick, Handler),
     reply(Req, JSON, Params);
 
+%% Test functions for auth and permissions
+handle("/auth", Req) ->
+    io:format("auth call~n"),
+    Params = Req:parse_post(),
+    io:format("Params: ~p~n",[Params]),
+    Req:respond({200, [{"Content-Type", "text/plain"}], "true"});
+handle("/can-contact", Req) ->
+    io:format("auth call~n"),
+    Params = Req:parse_post(),
+    io:format("Params: ~p~n",[Params]),
+    Req:respond({200, [{"Content-Type", "text/plain"}], "true"});
+
 handle(Path, Req) ->
     io:format("Unexpected ~p call~n", [Path]),
     Params = Req:parse_qs(),
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.