chut / 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(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(Url, User, Token) ->
    Body = postify([{"user",User},{"token",Token}]),
    {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(Url, UserA, UserB) ->
    Body = postify([{"from",UserA}, {"to",UserB}]),
    {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].
    
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.