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