1%% ``Licensed under the Apache License, Version 2.0 (the "License"); 2%% you may not use this file except in compliance with the License. 3%% You may obtain a copy of the License at 4%% 5%% http://www.apache.org/licenses/LICENSE-2.0 6%% 7%% Unless required by applicable law or agreed to in writing, software 8%% distributed under the License is distributed on an "AS IS" BASIS, 9%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10%% See the License for the specific language governing permissions and 11%% limitations under the License. 12%% 13%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. 14%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings 15%% AB. All Rights Reserved.'' 16%% 17%% $Id: mod_auth_server.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $ 18%% 19 20-module(mod_auth_server). 21 22-include("httpd.hrl"). 23%% -include("mod_auth.hrl"). 24-include("httpd_verbosity.hrl"). 25 26-behaviour(gen_server). 27 28 29%% mod_auth exports 30-export([start/2, stop/2, 31 add_password/4, update_password/5, 32 add_user/5, delete_user/5, get_user/5, list_users/4, 33 add_group_member/6, delete_group_member/6, list_group_members/5, 34 delete_group/5, list_groups/4]). 35 36%% Management exports 37-export([verbosity/3]). 38 39%% gen_server exports 40-export([start_link/3, 41 init/1, 42 handle_call/3, handle_cast/2, handle_info/2, 43 terminate/2, code_change/3]). 44 45 46-record(state,{tab}). 47 48 49%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 50%% %% 51%% External API %% 52%% %% 53%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 54 55%% start_link/3 56%% 57%% NOTE: This is called by httpd_misc_sup when the process is started 58%% 59start_link(Addr, Port, Verbosity)-> 60 ?vlog("start_link -> entry with" 61 "~n Addr: ~p" 62 "~n Port: ~p", [Addr, Port]), 63 Name = make_name(Addr, Port), 64 gen_server:start_link({local, Name}, ?MODULE, [Verbosity], 65 [{timeout, infinity}]). 66 67 68%% start/2 69 70start(Addr, Port)-> 71 ?vtrace("start -> entry with" 72 "~n Addr: ~p" 73 "~n Port: ~p", [Addr, Port]), 74 Name = make_name(Addr, Port), 75 case whereis(Name) of 76 undefined -> 77 Verbosity = get(auth_verbosity), 78 case (catch httpd_misc_sup:start_auth_server(Addr, Port, 79 Verbosity)) of 80 {ok, Pid} -> 81 put(auth_server, Pid), 82 ok; 83 {error, Reason} -> 84 exit({failed_start_auth_server, Reason}); 85 Error -> 86 exit({failed_start_auth_server, Error}) 87 end; 88 _ -> %% Already started... 89 ok 90 end. 91 92 93%% stop/2 94 95stop(Addr, Port)-> 96 ?vtrace("stop -> entry with" 97 "~n Addr: ~p" 98 "~n Port: ~p", [Addr, Port]), 99 Name = make_name(Addr, Port), 100 case whereis(Name) of 101 undefined -> %% Already stopped 102 ok; 103 _ -> 104 (catch httpd_misc_sup:stop_auth_server(Addr, Port)) 105 end. 106 107 108%% verbosity/3 109 110verbosity(Addr, Port, Verbosity) -> 111 Name = make_name(Addr, Port), 112 Req = {verbosity, Verbosity}, 113 call(Name, Req). 114 115 116%% add_password/4 117 118add_password(Addr, Port, Dir, Password)-> 119 Name = make_name(Addr, Port), 120 Req = {add_password, Dir, Password}, 121 call(Name, Req). 122 123 124%% update_password/6 125 126update_password(Addr, Port, Dir, Old, New) when list(New) -> 127 Name = make_name(Addr, Port), 128 Req = {update_password, Dir, Old, New}, 129 call(Name, Req). 130 131 132%% add_user/5 133 134add_user(Addr, Port, Dir, User, Password) -> 135 Name = make_name(Addr, Port), 136 Req = {add_user, Addr, Port, Dir, User, Password}, 137 call(Name, Req). 138 139 140%% delete_user/5 141 142delete_user(Addr, Port, Dir, UserName, Password) -> 143 Name = make_name(Addr, Port), 144 Req = {delete_user, Addr, Port, Dir, UserName, Password}, 145 call(Name, Req). 146 147 148%% get_user/5 149 150get_user(Addr, Port, Dir, UserName, Password) -> 151 Name = make_name(Addr, Port), 152 Req = {get_user, Addr, Port, Dir, UserName, Password}, 153 call(Name, Req). 154 155 156%% list_users/4 157 158list_users(Addr, Port, Dir, Password) -> 159 Name = make_name(Addr,Port), 160 Req = {list_users, Addr, Port, Dir, Password}, 161 call(Name, Req). 162 163 164%% add_group_member/6 165 166add_group_member(Addr, Port, Dir, GroupName, UserName, Password) -> 167 Name = make_name(Addr,Port), 168 Req = {add_group_member, Addr, Port, Dir, GroupName, UserName, Password}, 169 call(Name, Req). 170 171 172%% delete_group_member/6 173 174delete_group_member(Addr, Port, Dir, GroupName, UserName, Password) -> 175 Name = make_name(Addr,Port), 176 Req = {delete_group_member, Addr, Port, Dir, GroupName, UserName, Password}, 177 call(Name, Req). 178 179 180%% list_group_members/4 181 182list_group_members(Addr, Port, Dir, Group, Password) -> 183 Name = make_name(Addr, Port), 184 Req = {list_group_members, Addr, Port, Dir, Group, Password}, 185 call(Name, Req). 186 187 188%% delete_group/5 189 190delete_group(Addr, Port, Dir, GroupName, Password) -> 191 Name = make_name(Addr, Port), 192 Req = {delete_group, Addr, Port, Dir, GroupName, Password}, 193 call(Name, Req). 194 195 196%% list_groups/4 197 198list_groups(Addr, Port, Dir, Password) -> 199 Name = make_name(Addr, Port), 200 Req = {list_groups, Addr, Port, Dir, Password}, 201 call(Name, Req). 202 203 204%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 205%% %% 206%% Server call-back functions %% 207%% %% 208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 209 210%% init 211 212init([undefined]) -> 213 init([?default_verbosity]); 214 215init([Verbosity]) -> 216 put(sname,auth), 217 put(verbosity,Verbosity), 218 ?vlog("starting",[]), 219 {ok,#state{tab = ets:new(auth_pwd,[set,protected])}}. 220 221 222%% handle_call 223 224%% Add a user 225handle_call({add_user, Addr, Port, Dir, User, AuthPwd}, _From, State) -> 226 Reply = api_call(Addr, Port, Dir, add_user, User, AuthPwd, State), 227 {reply, Reply, State}; 228 229%% Get data about a user 230handle_call({get_user, Addr, Port, Dir, User, AuthPwd}, _From, State) -> 231 Reply = api_call(Addr, Port, Dir, get_user, [User], AuthPwd, State), 232 {reply, Reply, State}; 233 234%% Add a group member 235handle_call({add_group_member, Addr, Port, Dir, Group, User, AuthPwd}, 236 _From, State) -> 237 Reply = api_call(Addr, Port, Dir, add_group_member, [Group, User], 238 AuthPwd, State), 239 {reply, Reply, State}; 240 241%% delete a group 242handle_call({delete_group_member, Addr, Port, Dir, Group, User, AuthPwd}, 243 _From, State)-> 244 Reply = api_call(Addr, Port, Dir, delete_group_member, [Group, User], 245 AuthPwd, State), 246 {reply, Reply, State}; 247 248%% List all users thats standalone users 249handle_call({list_users, Addr, Port, Dir, AuthPwd}, _From, State)-> 250 Reply = api_call(Addr, Port, Dir, list_users, [], AuthPwd, State), 251 {reply, Reply, State}; 252 253%% Delete a user 254handle_call({delete_user, Addr, Port, Dir, User, AuthPwd}, _From, State)-> 255 Reply = api_call(Addr, Port, Dir, delete_user, [User], AuthPwd, State), 256 {reply, Reply, State}; 257 258%% Delete a group 259handle_call({delete_group, Addr, Port, Dir, Group, AuthPwd}, _From, State)-> 260 Reply = api_call(Addr, Port, Dir, delete_group, [Group], AuthPwd, State), 261 {reply, Reply, State}; 262 263%% List the current groups 264handle_call({list_groups, Addr, Port, Dir, AuthPwd}, _From, State)-> 265 Reply = api_call(Addr, Port, Dir, list_groups, [], AuthPwd, State), 266 {reply, Reply, State}; 267 268%% List the members of the given group 269handle_call({list_group_members, Addr, Port, Dir, Group, AuthPwd}, 270 _From, State)-> 271 Reply = api_call(Addr, Port, Dir, list_group_members, [Group], 272 AuthPwd, State), 273 {reply, Reply, State}; 274 275 276%% Add password for a directory 277handle_call({add_password, Dir, Password}, _From, State)-> 278 Reply = do_add_password(Dir, Password, State), 279 {reply, Reply, State}; 280 281 282%% Update the password for a directory 283 284handle_call({update_password, Dir, Old, New},_From,State)-> 285 Reply = 286 case getPassword(State, Dir) of 287 OldPwd when binary(OldPwd)-> 288 case erlang:md5(Old) of 289 OldPwd -> 290 %% The old password is right => 291 %% update the password to the new 292 do_update_password(Dir,New,State), 293 ok; 294 _-> 295 {error, error_new} 296 end; 297 _-> 298 {error, error_old} 299 end, 300 {reply, Reply, State}; 301 302handle_call(stop, _From, State)-> 303 {stop, normal, State}; 304 305handle_call({verbosity,Verbosity},_From,State)-> 306 OldVerbosity = put(verbosity,Verbosity), 307 ?vlog("set verbosity: ~p -> ~p",[Verbosity,OldVerbosity]), 308 {reply,OldVerbosity,State}. 309 310handle_info(Info,State)-> 311 {noreply,State}. 312 313handle_cast(Request,State)-> 314 {noreply,State}. 315 316 317terminate(Reason,State) -> 318 ets:delete(State#state.tab), 319 ok. 320 321 322%% code_change({down, ToVsn}, State, Extra) 323%% 324code_change({down, _}, #state{tab = Tab}, downgrade_to_2_6_0) -> 325 ?vlog("downgrade to 2.6.0", []), 326 {ok, {state, Tab, undefined}}; 327 328 329%% code_change(FromVsn, State, Extra) 330%% 331code_change(_, {state, Tab, _}, upgrade_from_2_6_0) -> 332 ?vlog("upgrade from 2.6.0", []), 333 {ok, #state{tab = Tab}}. 334 335 336%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 337%% %% 338%% The functions that really changes the data in the database %% 339%% of users to different directories %% 340%% %% 341%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 342 343%% API gateway 344 345api_call(Addr, Port, Dir, Func, Args,Password,State) -> 346 case controlPassword(Password,State,Dir) of 347 ok-> 348 ConfigName = httpd_util:make_name("httpd_conf",Addr,Port), 349 case ets:match_object(ConfigName, {directory, Dir, '$1'}) of 350 [{directory, Dir, DirData}] -> 351 AuthMod = auth_mod_name(DirData), 352 ?DEBUG("api_call -> call ~p:~p",[AuthMod,Func]), 353 Ret = (catch apply(AuthMod, Func, [DirData|Args])), 354 ?DEBUG("api_call -> Ret: ~p",[ret]), 355 Ret; 356 O -> 357 ?DEBUG("api_call -> O: ~p",[O]), 358 {error, no_such_directory} 359 end; 360 bad_password -> 361 {error,bad_password} 362 end. 363 364controlPassword(Password,State,Dir)when Password=:="DummyPassword"-> 365 bad_password; 366 367controlPassword(Password,State,Dir)-> 368 case getPassword(State,Dir) of 369 Pwd when binary(Pwd)-> 370 case erlang:md5(Password) of 371 Pwd -> 372 ok; 373 _-> 374 bad_password 375 end; 376 _ -> 377 bad_password 378 end. 379 380 381getPassword(State,Dir)-> 382 case lookup(State#state.tab, Dir) of 383 [{_,Pwd}]-> 384 Pwd; 385 _ -> 386 {error,bad_password} 387 end. 388 389do_update_password(Dir, New, State) -> 390 ets:insert(State#state.tab, {Dir, erlang:md5(New)}). 391 392do_add_password(Dir, Password, State) -> 393 case getPassword(State,Dir) of 394 PwdExists when binary(PwdExists) -> 395 {error, dir_protected}; 396 {error, _} -> 397 do_update_password(Dir, Password, State) 398 end. 399 400 401auth_mod_name(DirData) -> 402 case httpd_util:key1search(DirData, auth_type, plain) of 403 plain -> mod_auth_plain; 404 mnesia -> mod_auth_mnesia; 405 dets -> mod_auth_dets 406 end. 407 408 409lookup(Db, Key) -> 410 ets:lookup(Db, Key). 411 412 413make_name(Addr,Port) -> 414 httpd_util:make_name("httpd_auth",Addr,Port). 415 416 417call(Name, Req) -> 418 case (catch gen_server:call(Name, Req)) of 419 {'EXIT', Reason} -> 420 {error, Reason}; 421 Reply -> 422 Reply 423 end. 424