Skip to content

Commit

Permalink
Enable basic authorization
Browse files Browse the repository at this point in the history
  • Loading branch information
tgrk committed Mar 17, 2014
1 parent c4bc178 commit 20a6d73
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 74 deletions.
26 changes: 13 additions & 13 deletions src/web/web_admin.erl
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@
%%% @end
%%%-----------------------------------------------------------------------------
-module(web_admin).

-behaviour(gen_server).

-export([start_link/0]).

%% gen_server callbacks
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).

-record(state, {}).

%% ===================================================================
%% API functions
%% ===================================================================
Expand All @@ -30,21 +30,23 @@ start_link() ->
%% ===================================================================
%% web admin process callbacks
%% ===================================================================

init([]) ->
% start server
ok = gen_server:cast(self(), start_serve),
% init internal state
{ok, #state{}}.

handle_call(_Request, _From, State) ->
{reply, ignored, State}.

handle_cast(start_serve, State) ->
% Get web admin config
{ok, WebAdmin} = application:get_env(ybot, web_admin),

% Get Host
{webadmin_host, Host} = lists:keyfind(webadmin_host, 1, WebAdmin),

% Get Port
{webadmin_port, Port} = lists:keyfind(webadmin_port, 1, WebAdmin),

Expand All @@ -56,11 +58,9 @@ handle_cast(start_serve, State) ->
{dir, docroot("css"), [{mimetypes, cow_mimetypes, all}]}},
{"/js/[...]", cowboy_static,
{dir, docroot("js"), [{mimetypes, cow_mimetypes, all}]}},
{"/", cowboy_static,
{file, docroot("index.html"), [{mimetypes, cow_mimetypes, all}]}},
{"/views/[...]", cowboy_static,
{dir, docroot("views"), [{mimetypes, cow_mimetypes, all}]}},
{"/admin", web_admin_req_handler, []}
{"/", web_admin_req_handler, []}
]}
]),
% start serving
Expand All @@ -70,13 +70,13 @@ handle_cast(start_serve, State) ->

handle_cast(_Msg, State) ->
{noreply, State}.

handle_info(_Info, State) ->
{noreply, State}.

terminate(_Reason, _State) ->
ok.

code_change(_OldVsn, State, _Extra) ->
{ok, State}.

Expand Down
138 changes: 77 additions & 61 deletions src/web/web_admin_req_handler.erl
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,23 @@ init(_Transport, Req, []) ->
{ok, Req, undefined}.

handle(Req, State) ->

Username = "",
Password = "",

% check login credentials
{Username1, Password1, Req1} = credentials(Req),
{ok, Req2} = case {Username, Password} of
{?USERNAME, ?PASSWORD} ->
authorized(Req1, State);
_ ->
unauthorized(Req)
end,
{ok, Req2, State}.
{ok, WebAdmin} = application:get_env(ybot, web_admin),

%% check access credentials if auth is enabled
case config_option(webadmin_auth, WebAdmin) of
true ->
%% get configured credentials
AuthUser = list_to_binary(
config_option(webadmin_auth_user, WebAdmin)),
AuthPasswd = list_to_binary(
config_option(webadmin_auth_passwd, WebAdmin)),
case is_authorized(Req, AuthUser, AuthPasswd) of
{true, Req1} -> authorized(Req1, State);
{false, Req1} -> unauthorized(Req1, State)
end;
_ ->
authorized(Req, State)
end.

terminate(_Reason, _Req, _State) ->
ok.
Expand All @@ -39,18 +43,29 @@ terminate(_Reason, _Req, _State) ->
%% Internal functions
%%=============================================================================
authorized(Req, State) ->
% check body
case cowboy_req:has_body(Req) of
true ->
% get body
case cowboy_req:body(Req) of
{ok, Body, Req2} ->
handle_request(Body, Req2, State);
_ ->
{ok, Req, State}
end;
false ->
{ok, Req, State}
{Path, Req1} = cowboy_req:path(Req),
case Path of
<<"/">> ->
{ok, Bin} = file:read_file(docroot("index.html")),
{ok, Req2} = cowboy_req:reply(200,
[
{<<"content-type">>, <<"text/html">>}
], Bin, Req1),
{ok, Req2, State};
<<"/admin">> ->
%% check body
case cowboy_req:has_body(Req1) of
true ->
%% get body
case cowboy_req:body(Req1) of
{ok, Body, Req2} ->
handle_request(Body, Req2, State);
_ ->
{ok, Req1, State}
end;
false ->
{ok, Req1, State}
end
end.

handle_request(Body, Req, State) ->
Expand Down Expand Up @@ -205,47 +220,48 @@ format_plugins_helper(Plugins) ->
list_to_binary([Lang ++ " " ++ Name ++ " " ++ Path ++ "\n" || {plugin, Lang, Name, Path} <- Plugins]).

%% Authorization helpers
credentials(Req) ->
{AuthorizationHeader, Req} = cowboy_http_req:header('Authorization', Req),
case AuthorizationHeader of
undefined ->
{undefined, undefined, Req};
is_authorized(Req, User, Passwd) ->
{ok, Auth, Req1} = cowboy_req:parse_header(<<"authorization">>, Req),
case Auth of
{<<"basic">>, {User, Passwd}} ->
{true, Req1};
_ ->
{Username, Password} = credentials_from_header(AuthorizationHeader),
{Username, Password, Req}
{false, Req1}
end.

credentials_from_header(AuthorizationHeader) ->
case binary:split(AuthorizationHeader, <<$ >>) of
[<<"Basic">>, EncodedCredentials] ->
decoded_credentials(EncodedCredentials);
_ ->
{undefined, undefined}
end.

decoded_credentials(EncodedCredentials) ->
case binary:split(base64:decode(EncodedCredentials), <<$:>>) of
[Username, Password] ->
{Username, Password};
_ ->
{undefined, undefined}
end.

unauthorized(Req) ->
{ok, Req} =
cowboy_http_req:set_resp_header(<<"Www-Authenticate">>,
<<"Basic realm=\"Secure Area\"">>, Req),
{ok, Req} = cowboy_http_req:set_resp_body(unauthorized_body(), Req),
cowboy_http_req:reply(401, Req).
unauthorized(Req, State) ->
Req1 = cowboy_req:set_resp_header(<<"Www-Authenticate">>,
<<"Basic realm=\"Secure Area\"">>, Req),
Req2 = cowboy_req:set_resp_body(unauthorized_body(), Req1),
{ok, Req3} = cowboy_req:reply(401, Req2),
{ok, Req3, State}.

unauthorized_body() ->
<<"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"
\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dt\">
<HTML>
<HEAD>
<TITLE>Error</TITLE>
<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">
</HEAD>
<BODY><H1>401 Unauthorized.</H1></BODY>
</HTML>
<html>
<head>
<title>Error</title>
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">
</head>
<body><h1>401 Unauthorized.</h1></body>
</html>
">>.

docroot(Append) ->
priv_dir() ++ "webadmin/" ++ Append.

priv_dir() ->
case code:priv_dir(ybot) of
{error, bad_name} ->
{ok, Cwd} = file:get_cwd(),
Cwd ++ "/" ++ "priv/";
Priv ->
Priv ++ "/"
end.

config_option(Key, Options) ->
case lists:keyfind(Key, 1, Options) of
{Key, Value} -> Value;
false -> false
end.

0 comments on commit 20a6d73

Please sign in to comment.