1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1998-2016. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20%% 21-module(mod_auth_dets). 22 23%% dets authentication storage 24 25-export([get_user/2, 26 list_group_members/2, 27 add_user/2, 28 add_group_member/3, 29 list_users/1, 30 delete_user/2, 31 list_groups/1, 32 delete_group_member/3, 33 delete_group/2, 34 remove/1]). 35 36-export([store_directory_data/3]). 37 38-include("httpd.hrl"). 39-include("httpd_internal.hrl"). 40-include("mod_auth.hrl"). 41 42%%==================================================================== 43%% Internal application API 44%%==================================================================== 45 46store_directory_data(_Directory, DirData, Server_root) -> 47 {PWFile, Absolute_pwdfile} = absolute_file_name(auth_user_file, DirData, 48 Server_root), 49 {GroupFile, Absolute_groupfile} = absolute_file_name(auth_group_file, 50 DirData, Server_root), 51 Addr = proplists:get_value(bind_address, DirData), 52 Port = proplists:get_value(port, DirData), 53 Profile = proplists:get_value(profile, DirData, ?DEFAULT_PROFILE), 54 55 PWName = httpd_util:make_name("httpd_dets_pwdb", Addr, Port, Profile), 56 case dets:open_file(PWName,[{type,set},{file,Absolute_pwdfile},{repair,true}]) of 57 {ok, PWDB} -> 58 GDBName = httpd_util:make_name("httpd_dets_groupdb", Addr, Port, Profile), 59 case dets:open_file(GDBName,[{type,set},{file,Absolute_groupfile},{repair,true}]) of 60 {ok, GDB} -> 61 NDD1 = lists:keyreplace(auth_user_file, 1, DirData, 62 {auth_user_file, PWDB}), 63 NDD2 = lists:keyreplace(auth_group_file, 1, NDD1, 64 {auth_group_file, GDB}), 65 {ok, NDD2}; 66 {error, Err}-> 67 {error, {{file, GroupFile},Err}} 68 end; 69 {error, Err2} -> 70 {error, {{file, PWFile},Err2}} 71 end. 72 73%% Storage format of users in the dets table: 74%% {{UserName, Addr, Port, Dir}, Password, UserData} 75add_user(DirData, UStruct) -> 76 {Addr, Port, Dir} = lookup_common(DirData), 77 PWDB = proplists:get_value(auth_user_file, DirData), 78 Record = {{UStruct#httpd_user.username, Addr, Port, Dir}, 79 UStruct#httpd_user.password, UStruct#httpd_user.user_data}, 80 case dets:lookup(PWDB, UStruct#httpd_user.username) of 81 [Record] -> 82 {error, user_already_in_db}; 83 _ -> 84 dets:insert(PWDB, Record), 85 true 86 end. 87 88get_user(DirData, UserName) -> 89 {Addr, Port, Dir} = lookup_common(DirData), 90 PWDB = proplists:get_value(auth_user_file, DirData), 91 User = {UserName, Addr, Port, Dir}, 92 case dets:lookup(PWDB, User) of 93 [{User, Password, UserData}] -> 94 {ok, #httpd_user{username=UserName, password=Password, user_data=UserData}}; 95 _ -> 96 {error, no_such_user} 97 end. 98 99list_users(DirData) -> 100 {Addr, Port, Dir} = lookup_common(DirData), 101 PWDB = proplists:get_value(auth_user_file, DirData), 102 case dets:traverse(PWDB, fun(X) -> {continue, X} end) of 103 Records when is_list(Records) -> 104 {ok, [UserName || {{UserName, AnyAddr, AnyPort, AnyDir}, 105 _Password, _Data} <- Records, 106 AnyAddr == Addr, AnyPort == Port, 107 AnyDir == Dir]}; 108 _O -> 109 {ok, []} 110 end. 111 112delete_user(DirData, UserName) -> 113 {Addr, Port, Dir} = lookup_common(DirData), 114 PWDB = proplists:get_value(auth_user_file, DirData), 115 User = {UserName, Addr, Port, Dir}, 116 case dets:lookup(PWDB, User) of 117 [{User, _SomePassword, _UserData}] -> 118 dets:delete(PWDB, User), 119 {ok, Groups} = list_groups(DirData), 120 lists:foreach(fun(Group) -> 121 delete_group_member(DirData, 122 Group, UserName) end, 123 Groups), 124 true; 125 _ -> 126 {error, no_such_user} 127 end. 128 129%% Storage of groups in the dets table: 130%% {Group, UserList} where UserList is a list of strings. 131add_group_member(DirData, GroupName, UserName) -> 132 {Addr, Port, Dir} = lookup_common(DirData), 133 GDB = proplists:get_value(auth_group_file, DirData), 134 Group = {GroupName, Addr, Port, Dir}, 135 case dets:lookup(GDB, Group) of 136 [{Group, Users}] -> 137 case lists:member(UserName, Users) of 138 true -> 139 true; 140 false -> 141 dets:insert(GDB, {Group, [UserName|Users]}), 142 true 143 end; 144 [] -> 145 dets:insert(GDB, {Group, [UserName]}), 146 true; 147 Other -> 148 {error, Other} 149 end. 150 151list_group_members(DirData, GroupName) -> 152 {Addr, Port, Dir} = lookup_common(DirData), 153 GDB = proplists:get_value(auth_group_file, DirData), 154 Group = {GroupName, Addr, Port, Dir}, 155 case dets:lookup(GDB, Group) of 156 [{Group, Users}] -> 157 {ok, Users}; 158 _ -> 159 {error, no_such_group} 160 end. 161 162list_groups(DirData) -> 163 {Addr, Port, Dir} = lookup_common(DirData), 164 GDB = proplists:get_value(auth_group_file, DirData), 165 case dets:match(GDB, {'$1', '_'}) of 166 [] -> 167 {ok, []}; 168 List when is_list(List) -> 169 Groups = lists:flatten(List), 170 {ok, [GroupName || 171 {GroupName, AnyAddr, AnyPort, AnyDir} <- Groups, 172 AnyAddr == Addr, AnyPort == Port, AnyDir == Dir]}; 173 _ -> 174 {ok, []} 175 end. 176 177delete_group_member(DirData, GroupName, UserName) -> 178 {Addr, Port, Dir} = lookup_common(DirData), 179 GDB = proplists:get_value(auth_group_file, DirData), 180 Group = {GroupName, Addr, Port, Dir}, 181 case dets:lookup(GDB, GroupName) of 182 [{Group, Users}] -> 183 case lists:member(UserName, Users) of 184 true -> 185 dets:delete(GDB, Group), 186 dets:insert(GDB, {Group, 187 lists:delete(UserName, Users)}), 188 true; 189 false -> 190 {error, no_such_group_member} 191 end; 192 _ -> 193 {error, no_such_group} 194 end. 195 196delete_group(DirData, GroupName) -> 197 {Addr, Port, Dir} = lookup_common(DirData), 198 GDB = proplists:get_value(auth_group_file, DirData), 199 Group = {GroupName, Addr, Port, Dir}, 200 case dets:lookup(GDB, Group) of 201 [{Group, _Users}] -> 202 dets:delete(GDB, Group), 203 true; 204 _ -> 205 {error, no_such_group} 206 end. 207 208%% Closes dets tables used by this auth mod. 209remove(DirData) -> 210 PWDB = proplists:get_value(auth_user_file, DirData), 211 GDB = proplists:get_value(auth_group_file, DirData), 212 dets:close(GDB), 213 dets:close(PWDB), 214 ok. 215 216%%-------------------------------------------------------------------- 217%%% Internal functions 218%%-------------------------------------------------------------------- 219%% Return the absolute path name of File_type. 220absolute_file_name(File_type, DirData, Server_root) -> 221 Path = proplists:get_value(File_type, DirData), 222 Absolute_path = case filename:pathtype(Path) of 223 relative -> 224 case Server_root of 225 undefined -> 226 {error, 227 ?NICE(Path++ 228 " is an invalid file name because " 229 "ServerRoot is not defined")}; 230 _ -> 231 filename:join(Server_root,Path) 232 end; 233 _ -> 234 Path 235 end, 236 {Path, Absolute_path}. 237 238lookup_common(DirData) -> 239 Dir = proplists:get_value(path, DirData), 240 Port = proplists:get_value(port, DirData), 241 Addr = proplists:get_value(bind_address, DirData), 242 {Addr, Port, Dir}. 243