%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(mod_auth_plain).
-include("httpd.hrl").
-include("mod_auth.hrl").
-include("httpd_internal.hrl").
-include("inets_internal.hrl").
-define(VMODULE,"AUTH_PLAIN").
%% Internal API
-export([store_directory_data/3]).
-export([get_user/2,
list_group_members/2,
add_user/2,
add_group_member/3,
list_users/1,
delete_user/2,
list_groups/1,
delete_group_member/3,
delete_group/2,
remove/1]).
%%
%% API
%%
%%
%% Storage format of users in the ets table:
%% {UserName, Password, UserData}
%%
add_user(DirData, #httpd_user{username = User} = UStruct) ->
?hdrt("add user", [{user, UStruct}]),
PWDB = proplists:get_value(auth_user_file, DirData),
Record = {User,
UStruct#httpd_user.password,
UStruct#httpd_user.user_data},
case ets:lookup(PWDB, User) of
[{User, _SomePassword, _SomeData}] ->
{error, user_already_in_db};
_ ->
ets:insert(PWDB, Record),
true
end.
get_user(DirData, User) ->
?hdrt("get user", [{dir_data, DirData}, {user, User}]),
PWDB = proplists:get_value(auth_user_file, DirData),
case ets:lookup(PWDB, User) of
[{User, PassWd, Data}] ->
{ok, #httpd_user{username = User,
password = PassWd,
user_data = Data}};
_Other ->
{error, no_such_user}
end.
list_users(DirData) ->
PWDB = proplists:get_value(auth_user_file, DirData),
Records = ets:match(PWDB, '$1'),
{ok, lists:foldr(fun({User, _PassWd, _Data}, A) -> [User | A] end,
[], lists:flatten(Records))}.
delete_user(DirData, UserName) ->
?hdrt("delete user", [{dir_data, DirData}, {user, UserName}]),
PWDB = proplists:get_value(auth_user_file, DirData),
case ets:lookup(PWDB, UserName) of
[{UserName, _SomePassword, _SomeData}] ->
ets:delete(PWDB, UserName),
{ok, Groups} = list_groups(DirData),
lists:foreach(fun(Group) ->
delete_group_member(DirData,
Group, UserName)
end, Groups);
_ ->
{error, no_such_user}
end.
%%
%% Storage of groups in the ets table:
%% {Group, UserList} where UserList is a list of strings.
%%
add_group_member(DirData, Group, UserName) ->
GDB = proplists:get_value(auth_group_file, DirData),
case ets:lookup(GDB, Group) of
[{Group, Users}] ->
case lists:member(UserName, Users) of
true ->
true;
false ->
ets:insert(GDB, {Group, [UserName|Users]}),
true
end;
[] ->
ets:insert(GDB, {Group, [UserName]}),
true;
Other ->
{error, Other}
end.
list_group_members(DirData, Group) ->
GDB = proplists:get_value(auth_group_file, DirData),
case ets:lookup(GDB, Group) of
[{Group, Users}] ->
{ok, Users};
_ ->
{error, no_such_group}
end.
list_groups(DirData) ->
GDB = proplists:get_value(auth_group_file, DirData),
Groups = ets:match(GDB, '$1'),
{ok, httpd_util:uniq(lists:foldr(fun({G, _}, A) -> [G|A] end,
[], lists:flatten(Groups)))}.
delete_group_member(DirData, Group, User) ->
GDB = proplists:get_value(auth_group_file, DirData),
case ets:lookup(GDB, Group) of
[{Group, Users}] when is_list(Users) ->
case lists:member(User, Users) of
true ->
ets:delete(GDB, Group),
ets:insert(GDB, {Group, lists:delete(User, Users)}),
true;
false ->
{error, no_such_group_member}
end;
_ ->
{error, no_such_group}
end.
delete_group(DirData, Group) ->
GDB = proplists:get_value(auth_group_file, DirData),
case ets:lookup(GDB, Group) of
[{Group, _Users}] ->
ets:delete(GDB, Group),
true;
_ ->
{error, no_such_group}
end.
store_directory_data(_Directory, DirData, Server_root) ->
?hdrt("store directory data",
[{dir_data, DirData}, {server_root, Server_root}]),
PWFile = absolute_file_name(auth_user_file, DirData, Server_root),
GroupFile = absolute_file_name(auth_group_file, DirData, Server_root),
case load_passwd(PWFile) of
{ok, PWDB} ->
?hdrt("password file loaded", [{file, PWFile}, {pwdb, PWDB}]),
case load_group(GroupFile) of
{ok, GRDB} ->
?hdrt("group file loaded",
[{file, GroupFile}, {grdb, GRDB}]),
%% Address and port is included in the file names...
Addr = proplists:get_value(bind_address, DirData),
Port = proplists:get_value(port, DirData),
{ok, PasswdDB} = store_passwd(Addr,Port,PWDB),
{ok, GroupDB} = store_group(Addr,Port,GRDB),
NDD1 = lists:keyreplace(auth_user_file, 1, DirData,
{auth_user_file, PasswdDB}),
NDD2 = lists:keyreplace(auth_group_file, 1, NDD1,
{auth_group_file, GroupDB}),
{ok, NDD2};
Err ->
{error, Err}
end;
Err2 ->
{error, Err2}
end.
%% load_passwd
load_passwd(AuthUserFile) ->
case file:open(AuthUserFile, [read]) of
{ok,Stream} ->
parse_passwd(Stream, []);
{error, _} ->
{error, ?NICE("Can't open " ++ AuthUserFile)}
end.
parse_passwd(Stream, PasswdList) ->
Line =
case io:get_line(Stream, '') of
eof ->
eof;
String ->
httpd_conf:clean(String)
end,
parse_passwd(Stream, PasswdList, Line).
parse_passwd(Stream, PasswdList, eof) ->
file:close(Stream),
{ok, PasswdList};
parse_passwd(Stream, PasswdList, "") ->
parse_passwd(Stream, PasswdList);
parse_passwd(Stream, PasswdList, [$#|_]) ->
parse_passwd(Stream, PasswdList);
parse_passwd(Stream, PasswdList, Line) ->
case inets_regexp:split(Line,":") of
{ok, [User,Password]} ->
parse_passwd(Stream, [{User,Password, []}|PasswdList]);
{ok,_} ->
{error, ?NICE(Line)}
end.
%% load_group
load_group(AuthGroupFile) ->
case file:open(AuthGroupFile, [read]) of
{ok, Stream} ->
parse_group(Stream,[]);
{error, _} ->
{error, ?NICE("Can't open " ++ AuthGroupFile)}
end.
parse_group(Stream, GroupList) ->
Line =
case io:get_line(Stream,'') of
eof ->
eof;
String ->
httpd_conf:clean(String)
end,
parse_group(Stream, GroupList, Line).
parse_group(Stream, GroupList, eof) ->
file:close(Stream),
{ok, GroupList};
parse_group(Stream, GroupList, "") ->
parse_group(Stream, GroupList);
parse_group(Stream, GroupList, [$#|_]) ->
parse_group(Stream, GroupList);
parse_group(Stream, GroupList, Line) ->
case inets_regexp:split(Line, ":") of
{ok, [Group,Users]} ->
{ok, UserList} = inets_regexp:split(Users," "),
parse_group(Stream, [{Group,UserList}|GroupList]);
{ok, _} ->
{error, ?NICE(Line)}
end.
%% store_passwd
store_passwd(Addr,Port,PasswdList) ->
Name = httpd_util:make_name("httpd_passwd",Addr,Port),
PasswdDB = ets:new(Name, [set, public]),
store_passwd(PasswdDB, PasswdList).
store_passwd(PasswdDB, []) ->
{ok, PasswdDB};
store_passwd(PasswdDB, [User|Rest]) ->
ets:insert(PasswdDB, User),
store_passwd(PasswdDB, Rest).
%% store_group
store_group(Addr,Port,GroupList) ->
Name = httpd_util:make_name("httpd_group",Addr,Port),
GroupDB = ets:new(Name, [set, public]),
store_group(GroupDB, GroupList).
store_group(GroupDB,[]) ->
{ok, GroupDB};
store_group(GroupDB, [User|Rest]) ->
ets:insert(GroupDB, User),
store_group(GroupDB, Rest).
%% remove/1
%%
%% Deletes ets tables used by this auth mod.
%%
remove(DirData) ->
PWDB = proplists:get_value(auth_user_file, DirData),
GDB = proplists:get_value(auth_group_file, DirData),
ets:delete(PWDB),
ets:delete(GDB).
%% absolute_file_name/2
%%
%% Return the absolute path name of File_type.
absolute_file_name(File_type, DirData, Server_root) ->
Path = proplists:get_value(File_type, DirData),
case filename:pathtype(Path) of
relative ->
case Server_root of
undefined ->
{error,
?NICE(Path++
" is an invalid file name because "
"ServerRoot is not defined")};
_ ->
filename:join(Server_root,Path)
end;
_ ->
Path
end.