%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1998-2010. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% %% -module(mod_auth_plain). -include("httpd.hrl"). -include("mod_auth.hrl"). -include("httpd_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]). %%==================================================================== %% Internal application API %%==================================================================== %% Storage format of users in the ets table: %% {UserName, Password, UserData} add_user(DirData, #httpd_user{username = 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) -> 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) -> 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) -> 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} -> case load_group(GroupFile) of {ok, 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. %% 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). %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- %% 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. store_group(Addr,Port,GroupList) -> %% Not a named table so not importante to add Profile to name 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). store_passwd(Addr,Port,PasswdList) -> %% Not a named table so not importante to add Profile to name 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). parse_group(Stream, GroupList) -> Line = case io:get_line(Stream,'') of eof -> eof; String -> httpd_conf:white_space_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 re:split(Line, ":", [{return, list}]) of [Group,Users] -> UserList = re:split(Users," ", [{return, list}]), parse_group(Stream, [{Group,UserList}|GroupList]); _ -> {error, ?NICE(Line)} end. 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:white_space_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 re:split(Line,":", [{return, list}]) of [User,Password] -> parse_passwd(Stream, [{User,Password, []}|PasswdList]); _ -> {error, ?NICE(Line)} end. load_group(AuthGroupFile) -> case file:open(AuthGroupFile, [read]) of {ok, Stream} -> parse_group(Stream,[]); {error, _} -> {error, ?NICE("Can't open " ++ AuthGroupFile)} end.