Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Auth enhancements by Anders Dahlin,

- Auth - unauthorized enhancements
- Setup auth rewrite/cleanup
- (Embedded) Config enhancements
  • Loading branch information...
commit d5676b22c92b6f69bfe86ba0017d91663ab14f95 1 parent aa32fa0
Claes Wikstrom authored
24 src/yaws.erl
View
@@ -18,7 +18,8 @@
-export([start/0, stop/0, hup/1, restart/0, modules/0, load/0]).
-export([start_embedded/1, start_embedded/2, start_embedded/3,
start_embedded/4,
- add_server/2]).
+ add_server/2,
+ create_gconf/2, create_sconf/2]).
-export([new_ssl/0,
ssl_keyfile/1, ssl_keyfile/2,
ssl_certfile/1, ssl_certfile/2,
@@ -138,14 +139,27 @@ start_embedded(DocRoot, SL, GL, Id) when is_list(DocRoot),is_list(SL),is_list(GL
ok = application:set_env(yaws, embedded, true),
ok = application:set_env(yaws, id, Id),
application:start(yaws),
- GC = setup_gconf(GL, yaws_config:make_default_gconf(false, Id)),
- SC = setup_sconf(DocRoot, #sconf{}, SL),
+ GC = create_gconf(GL, Id),
+ SC = create_sconf(DocRoot, SL),
yaws_config:add_yaws_soap_srv(GC),
yaws_api:setconf(GC, [[SC]]).
add_server(DocRoot, SL) when is_list(DocRoot),is_list(SL) ->
- SC = setup_sconf(DocRoot, #sconf{}, SL),
- yaws_config:add_sconf(SC).
+ SC = create_sconf(DocRoot, SL),
+ %% Change #auth in authdirs to {Dir, #auth} if needed
+ Fun = fun
+ (A = #auth{dir = [Dir]}, Acc) -> [{Dir, A}| Acc];
+ (A, Acc) -> [A| Acc]
+ end,
+ Authdirs = lists:foldr(Fun, [], SC#sconf.authdirs),
+ yaws_config:add_sconf(SC#sconf{authdirs = Authdirs}).
+
+create_gconf(GL, Id) when is_list(GL) ->
+ setup_gconf(GL, yaws_config:make_default_gconf(false, Id)).
+
+create_sconf(DocRoot, SL) when is_list(DocRoot), is_list(SL) ->
+ setup_sconf(DocRoot, #sconf{}, SL).
+
%%% Access functions for the SSL record.
new_ssl() -> #ssl{}.
171 src/yaws_config.erl
View
@@ -94,118 +94,83 @@ add_yaws_soap_srv(_GC) ->
add_yaws_auth(SCs) ->
- lists:map(
- fun(SC) ->
- SC#sconf{authdirs = setup_auth(SC)}
- end, SCs).
+ [SC#sconf{authdirs = setup_auth(SC)} || SC <- SCs].
%% We search and setup www authenticate for each directory
%% specified as an auth directory or containing a .yaws_auth file.
%% These are merged with server conf.
-
-setup_auth(SC) ->
- Auth_dirs0 = get_yaws_auth_dirs(SC#sconf.docroot),
-
- %% create new auth records
- Auth_dirs1 = [#auth{dir = [X]} || X <- Auth_dirs0],
- Auth_dirs2 = Auth_dirs1 ++ SC#sconf.authdirs,
-
- %% load the .yaws_auth files and parse them
- Auth_dirs3 = load_yaws_auth_file(SC, Auth_dirs2, []),
-
- start_pam(Auth_dirs3),
- Auth_dirs3.
+setup_auth(#sconf{docroot = Docroot, authdirs = Authdirs}) ->
+ Authdirs1 = load_yaws_auth_from_docroot(Docroot),
+ Authdirs2 = load_yaws_auth_from_authdirs(Authdirs, Docroot, Authdirs1),
+ Authdirs3 = [{Dir, A} || A = #auth{dir = [Dir]} <- Authdirs2], % A->{Dir, A}
+ start_pam(Authdirs3),
+ Authdirs3.
-
-%% Call get_yaws_auth_dirs/3 with default values and then
-%% strip leading docroot from dir
-get_yaws_auth_dirs(undefined) ->
+load_yaws_auth_from_docroot(undefined) ->
[];
-get_yaws_auth_dirs(Docroot) ->
- case file:list_dir(Docroot) of
- {ok, FileList} ->
- Auth_dirs = get_yaws_auth_dirs(Docroot ++ "/", FileList, []),
- Len = string:len(Docroot),
- [string:sub_string(X, Len+1) || X <- Auth_dirs];
- _ ->
- []
- end.
+load_yaws_auth_from_docroot(Docroot) ->
+ Fun = fun (Path, Acc) ->
+ %% Strip Docroot and then filename
+ SP = string:sub_string(Path, length(Docroot)+1),
+ Dir = filename:dirname(SP),
+ case load_yaws_auth_file(Path, #auth{dir = [Dir]}) of
+ {ok, Auth} -> [Auth| Acc];
+ _ -> Acc
+ end
+ end,
+ filelib:fold_files(Docroot, "^.yaws_auth$", true, Fun, []).
+
+
+load_yaws_auth_from_authdirs([], _, Acc) ->
+ lists:reverse(Acc);
+load_yaws_auth_from_authdirs([Auth = #auth{dir = [D]}| Rest], Docroot, Acc) ->
+ Dir = case D of
+ "/" ++ Rest -> Rest; % strip leading slash
+ _ -> D
+ end,
+ Path = filename:join([Docroot, Dir, ".yaws_auth"]),
+ NewAuth = case catch load_yaws_auth_file(Path, Auth) of
+ {ok, A} -> A;
+ _ -> Auth
+ end,
+ load_yaws_auth_from_authdirs(Rest, Docroot, [NewAuth| Acc]);
+load_yaws_auth_from_authdirs([{_Dir, Auth}| Rest], Docroot, Acc) ->
+ %% handle {Dir, A} by stripping it
+ load_yaws_auth_from_authdirs([Auth| Rest], Docroot, Acc);
+load_yaws_auth_from_authdirs([_| Rest], Docroot, Acc) ->
+ load_yaws_auth_from_authdirs(Rest, Docroot, Acc).
-%% Bottom of recursion, we have searched all the entries in this directory
-get_yaws_auth_dirs(_Docroot, [], AuthDirs) ->
- AuthDirs;
-
-%% Recursivly search the docroot for any dir that contains .yaws_auth
-%% and return it
-get_yaws_auth_dirs(Docroot, [File|T], AuthDirs0) ->
- Path = string:concat(Docroot, File),
- case filelib:is_dir(Path) of
- true ->
- case file:list_dir(Path) of
- {ok, FileList} ->
- AuthDirs1 = get_yaws_auth_dirs(Path ++ "/", FileList,
- AuthDirs0),
- get_yaws_auth_dirs(Docroot, T, AuthDirs1);
- _ ->
- []
- end;
- false ->
- case File of
- ".yaws_auth" ->
- get_yaws_auth_dirs(Docroot, T, [Docroot|AuthDirs0]);
- _ ->
- get_yaws_auth_dirs(Docroot, T, AuthDirs0)
- end
+load_yaws_auth_file(Path, Auth) ->
+ case file:consult(Path) of
+ {ok, TermList} ->
+ error_logger:info_msg("Reading .yaws_auth ~s~n", [Path]),
+ parse_yaws_auth_file(TermList, Auth);
+ {error, enoent} ->
+ {error, enoent};
+ Error ->
+ error_logger:format("Bad .yaws_auth file ~s ~p~n", [Path, Error]),
+ Error
end.
+
start_pam([]) ->
ok;
-
+start_pam([{_Dir, #auth{pam = false}}|T]) ->
+ start_pam(T);
start_pam([{_Dir, A}|T]) ->
- PamStarted = whereis(yaws_pam) /= undefined,
- if
- A#auth.pam == false ->
- ok;
- PamStarted == false ->
+ case whereis(yaws_pam) of
+ undefined -> % pam not started
Spec = {yaws_pam, {yaws_pam, start_link,
[yaws:to_list(A#auth.pam),undefined,undefined]},
permanent, 5000, worker, [yaws_pam]},
- spawn(fun() ->
- supervisor:start_child(yaws_sup, Spec)
- end);
- true ->
- ok
- end,
- start_pam(T).
-
-
-
-load_yaws_auth_file(_SC, [], Acc) ->
- Acc;
-
-%% Load the .yaws_auth file if it exists and parse it,
-%% adding the result to the record
-load_yaws_auth_file(SC, [A|T], Acc) ->
- [Dir] = A#auth.dir,
- FN0=[SC#sconf.docroot, [$/|Dir], [$/|".yaws_auth"]],
- FN1 = remove_multiple_slash(lists:flatten(FN0)),
- A2 = case file:consult(FN1) of
- {ok, TermList} ->
- error_logger:info_msg("Reading .yaws_auth ~s~n",[FN1]),
- Auth = parse_yaws_auth_file(TermList, A),
- [Dir1] = Auth#auth.dir,
- {Dir1, Auth};
- {error, enoent} ->
- {Dir, A};
- _Err ->
- error_logger:format("Bad .yaws_auth file in dir ~p~n",
- [Dir]),
- {Dir, A}
- end,
- load_yaws_auth_file(SC, T, [A2|Acc]).
+ spawn(fun() -> supervisor:start_child(yaws_sup, Spec) end);
+ _ ->
+ start_pam(T)
+ end.
+
parse_yaws_auth_file([], Auth0) ->
Realm = Auth0#auth.realm,
@@ -242,17 +207,18 @@ parse_yaws_auth_file([{User, Password}|T], Auth0)
parse_yaws_auth_file(T, Auth0#auth{users = Users}).
+%% Not used anymore
%% Replace all "//" and "///" with "/"
%% Might be a better way to do this
-remove_multiple_slash(L) ->
- remove_multiple_slash(L, []).
+%% remove_multiple_slash(L) ->
+%% remove_multiple_slash(L, []).
-remove_multiple_slash([], Acc)->
- lists:reverse(Acc);
-remove_multiple_slash("//" ++ T, Acc) ->
- remove_multiple_slash([$/|T], Acc);
-remove_multiple_slash([H|T], Acc) ->
- remove_multiple_slash(T, [H|Acc]).
+%% remove_multiple_slash([], Acc)->
+%% lists:reverse(Acc);
+%% remove_multiple_slash("//" ++ T, Acc) ->
+%% remove_multiple_slash([$/|T], Acc);
+%% remove_multiple_slash([H|T], Acc) ->
+%% remove_multiple_slash(T, [H|Acc]).
%% This is the function that arranges sconfs into
%% different server groups
@@ -1794,4 +1760,3 @@ delete_sconf(SC) ->
update_gconf(GC) ->
ok = gen_server:call(yaws_server, {update_gconf, GC}, infinity).
-
19 src/yaws_outmod.erl
View
@@ -12,7 +12,8 @@
-include("../include/yaws.hrl").
-include("../include/yaws_api.hrl").
--export([out404/3,
+-export([out401/3,
+ out404/3,
out404/1,
out/1,
crashmsg/3]).
@@ -41,8 +42,20 @@ out404(Arg, GC, SC) ->
%% The default error 401 error delivery module
%% This function can be used to generate
%% a special page on 401's (it doesn't even have to be a 401)
-
-
+out401(_Arg, _Auth, _Realm) ->
+ {ehtml,
+ [{html,[],
+ [
+ {body, [],
+ [{h1,[], "401 authentication needed"}
+ ]
+ }
+ ]
+ }
+ ]
+ }.
+
+%% Deprecated, out401/3 will be used
out(_Arg) ->
{ehtml,
[{html,[],
41 src/yaws_server.erl
View
@@ -1590,11 +1590,6 @@ handle_request(CliSock, ARG, N) ->
QueryString, PP, N);
{false_403, _, _} ->
deliver_403(CliSock, Req);
- {false, _, _} ->
- UT = #urltype{type = unauthorized,
- path = DecPath
- },
- handle_ut(CliSock, ARG, UT, N);
{{false, AuthMethods, Realm}, _, _} ->
UT = #urltype{
type = {unauthorized, AuthMethods, Realm},
@@ -1644,7 +1639,9 @@ is_auth(ARG, Req_dir, H, [{Auth_dir, Auth_methods}|T], {Ret, Auth_headers}) ->
{true, User};
false ->
L = Auth_methods#auth.headers,
- is_auth(ARG, Req_dir, H, T, {false, L ++ Auth_headers});
+ Realm = Auth_methods#auth.realm,
+ is_auth(ARG, Req_dir, H, T, {{false, Auth_methods, Realm},
+ L ++ Auth_headers});
{false, Realm} ->
L = Auth_methods#auth.headers,
is_auth(ARG, Req_dir, H, T, {{false, Auth_methods, Realm},
@@ -1663,10 +1660,11 @@ handle_auth(ARG, _Auth_H, #auth{realm = Realm,
handle_auth(ARG, Auth_H, Auth_methods = #auth{mod = Mod}, Ret) when Mod /= [] ->
case catch Mod:auth(ARG, Auth_methods) of
- {'EXIT', _Reason} ->
+ {'EXIT', Reason} ->
L = ?F("authmod crashed ~n~p:auth(~p, ~n ~p) \n"
+ "Reason: ~p~n"
"Stack: ~p~n",
- [Mod, ARG, Auth_methods,
+ [Mod, ARG, Auth_methods, Reason,
erlang:get_stacktrace()]),
handle_crash(ARG, L),
deliver_accumulated(ARG#arg.clisock),
@@ -1703,9 +1701,6 @@ handle_auth(ARG, {User, Password, OrigString},
Auth_methods#auth{pam = false}, Ret)
end;
-
-
-
handle_auth(ARG, {User, Password, OrigString},
Auth_methods = #auth{users = Users}, Ret) when Users /= [] ->
case member({User, Password}, Users) of
@@ -1896,30 +1891,6 @@ handle_ut(CliSock, ARG, UT = #urltype{type = {unauthorized, Auth, Realm}}, N) ->
DeliverFun = fun (A) -> finish_up_dyn_file(A, CliSock) end,
deliver_dyn_part(CliSock, 0, "appmod", N, ARG, UT, OutFun, DeliverFun);
-
-handle_ut(CliSock, ARG, UT = #urltype{type = unauthorized}, N) ->
- Req = ARG#arg.req,
- H = ARG#arg.headers,
- SC = get(sc),
- yaws:outh_set_dyn_headers(Req, H, UT),
-
- %% outh_set_dyn headers sets status to 200 by default
- %% so we need to set it 401
- yaws:outh_set_status_code(401),
- Outmod = get_unauthorized_outmod(UT#urltype.path, SC#sconf.authdirs,
- SC#sconf.errormod_401),
-
- deliver_dyn_part(CliSock,
- 0,
- "appmod",
- N,
- ARG,
- UT,
- fun(A)->Outmod:out(A)
- end,
- fun(A)->finish_up_dyn_file(A, CliSock)
- end);
-
handle_ut(CliSock, ARG, UT = #urltype{type = error}, N) ->
Req = ARG#arg.req,
H = ARG#arg.headers,
Please sign in to comment.
Something went wrong with that request. Please try again.