Current section
Files
Jump to
Current section
Files
src/webpage_auth.erl
-module(webpage_auth).
-author({ "David J Goehrig", "dave@dloh.org" }).
-copyright(<<"© 2016 David J Goehrig"/utf8>>).
-export([ add/3, remove/2, install/1, grant/2, revoke/2, users/0, token/1]).
-include("include/http.hrl").
-record(user_auth, { token, user, email, active, paths = [] }).
-export([ auth/1 ]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Public API
%
auth(Request = #request{ path = Path, headers = Headers }) ->
io:format("auth headers are ~p~n",[ Headers ]),
case proplists:get_value(<<"Authorization">>, Headers ) of
undefined ->
io:format("challenging~n"),
#response{ status = 401, headers = [{ <<"WWW-Authenticate">>,<<"Basic realm=\"webpage\"">> }, {<<"Content-Length">>, <<"0">> }]};
Authorization ->
F = fun() ->
{ ok, Salt } = application:get_env(webpage,salt),
[ <<"Basic">>,Auth ] = binary:split(Authorization,<<" ">>),
Token = crypto:hmac(sha256,Salt,Auth),
case mnesia:read(user_auth,Token) of
[ #user_auth{ user = User, email = Email, active = true, paths = Paths } ] ->
case lists:foldl(fun(Pattern,Match) ->
webpage_path:match(Path,Pattern) or Match end, false, Paths) of
true ->
Request#request{ headers = [ { <<"User">>, User},{<<"Email">>,Email } | Headers ] };
false ->
#response{ status = 401, headers = [{ <<"WWW-Authenticate">>,<<"Basic realm=\"webpage\"">> }, {<<"Content-Length">>, <<"0">> }]}
end;
_ ->
#response{ status = 401, headers = [{ <<"WWW-Authenticate">>,<<"Basic realm=\"webpage\"">> }, {<<"Content-Length">>, <<"0">> }]}
end
end,
mnesia:activity(transaction,F)
end.
add(User,Email,Password) when is_list(User) ->
add(list_to_binary(User),Email,Password);
add(User,Email,Password) when is_list(Email) ->
add(User,list_to_binary(Email),Password);
add(User,Email,Password) when is_list(Password) ->
add(User,Email,list_to_binary(Password));
add(User,Email,Password) ->
{ ok, Salt } = application:get_env(webpage,salt),
Auth = base64:encode(<< User/binary,":",Password/binary>>),
Token = crypto:hmac(sha256,Salt,Auth),
F = fun() ->
remove(User,Email),
mnesia:write(#user_auth{ token = Token, user = User, email = Email, active = true, paths = [] })
end,
mnesia:activity(transaction,F).
remove(User,Email) when is_list(User) ->
remove(list_to_binary(User),Email);
remove(User,Email) when is_list(Email) ->
remove(User,list_to_binary(Email));
remove(User,Email) ->
F = fun() ->
case mnesia:match_object(#user_auth{ user = User, email = Email, token = '_', active = '_', paths = '_' }) of
[] -> ok;
Records ->
[ mnesia:delete_object(Record) || Record <- Records ]
end
end,
mnesia:activity(transaction,F).
grant(User,Pattern) when is_list(User) ->
grant(list_to_binary(User),Pattern);
grant(User,Pattern) ->
F = fun() ->
case mnesia:match_object(#user_auth{ user = User, email = '_', token = '_', active = true, paths = '_' }) of
[] -> ok;
[ Auth = #user_auth{ paths = Paths } ] ->
mnesia:write(Auth#user_auth{ paths = [ Pattern | Paths ]})
end
end,
mnesia:activity(transaction,F).
revoke(User,Pattern) when is_list(User) ->
revoke(list_to_binary(User), Pattern);
revoke(User,Pattern) ->
F = fun() ->
case mnesia:match_object(#user_auth{ user = User, email = '_', token = '_', active = true, paths = '_' }) of
[] -> ok;
[ Auth = #user_auth{ paths = Paths }] ->
mnesia:write(Auth#user_auth{ paths = lists:delete(Pattern,Paths) })
end
end,
mnesia:activity(transaction,F).
users() ->
F = fun() ->
case mnesia:match_object(#user_auth{ user = '_', email = '_', token = '_', active = true, paths = '_' }) of
Users when is_list(Users) ->
[ User || #user_auth{ user = User } <- Users ];
_ -> []
end
end,
mnesia:activity(transaction,F).
token(User) when is_list(User) ->
token(list_to_binary(User));
token(User) ->
F = fun() ->
case mnesia:match_object(#user_auth{ user = User, email = '_', token = '_', active = true, paths = '_' }) of
[ #user_auth{ token = Token }] ->
Token;
_ ->
undefined
end
end,
mnesia:activity(transaction,F).
install(Nodes) ->
Database = code:priv_dir(webpage),
application:set_env(mnesia,dir, Database),
rpc:multicall(Nodes,application,start,[ mnesia ]),
{ atomic, ok } = mnesia:create_table(user_auth, [
{ attributes, record_info(fields,user_auth) },
{ disc_copies, Nodes }]),
rpc:multicall(Nodes,application,stop, [ mnesia ]).