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_dets.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
18-module(mod_auth_dets).
19
20%% dets authentication storage
21
22-export([get_user/2,
23	 list_group_members/2,
24	 add_user/2,
25	 add_group_member/3,
26	 list_users/1,
27	 delete_user/2,
28	 list_groups/1,
29	 delete_group_member/3,
30	 delete_group/2,
31	 remove/1]).
32
33-export([store_directory_data/2]).
34
35-include("httpd.hrl").
36-include("mod_auth.hrl").
37
38store_directory_data(Directory, DirData) ->
39    ?CDEBUG("store_directory_data -> ~n"
40	    "     Directory: ~p~n"
41	    "     DirData:   ~p",
42	    [Directory, DirData]),
43
44    PWFile = httpd_util:key1search(DirData, auth_user_file),
45    GroupFile = httpd_util:key1search(DirData, auth_group_file),
46    Addr = httpd_util:key1search(DirData, bind_address),
47    Port = httpd_util:key1search(DirData, port),
48
49    PWName  = httpd_util:make_name("httpd_dets_pwdb",Addr,Port),
50    case dets:open_file(PWName,[{type,set},{file,PWFile},{repair,true}]) of
51	{ok, PWDB} ->
52	    GDBName = httpd_util:make_name("httpd_dets_groupdb",Addr,Port),
53	    case dets:open_file(GDBName,[{type,set},{file,GroupFile},{repair,true}]) of
54		{ok, GDB} ->
55		    NDD1 = lists:keyreplace(auth_user_file, 1, DirData,
56					    {auth_user_file, PWDB}),
57		    NDD2 = lists:keyreplace(auth_group_file, 1, NDD1,
58					    {auth_group_file, GDB}),
59		    {ok, NDD2};
60		{error, Err}->
61		    {error, {{file, GroupFile},Err}}
62	    end;
63	{error, Err2} ->
64	    {error, {{file, PWFile},Err2}}
65    end.
66
67%%
68%% Storage format of users in the dets table:
69%% {{UserName, Addr, Port, Dir}, Password, UserData}
70%%
71
72add_user(DirData, UStruct) ->
73    {Addr, Port, Dir} = lookup_common(DirData),
74    PWDB = httpd_util:key1search(DirData, auth_user_file),
75    Record = {{UStruct#httpd_user.username, Addr, Port, Dir},
76	      UStruct#httpd_user.password, UStruct#httpd_user.user_data},
77    case dets:lookup(PWDB, UStruct#httpd_user.username) of
78	[Record] ->
79	    {error, user_already_in_db};
80	_ ->
81	    dets:insert(PWDB, Record),
82	    true
83    end.
84
85get_user(DirData, UserName) ->
86    {Addr, Port, Dir} = lookup_common(DirData),
87    PWDB = httpd_util:key1search(DirData, auth_user_file),
88    User = {UserName, Addr, Port, Dir},
89    case dets:lookup(PWDB, User) of
90	[{User, Password, UserData}] ->
91	    {ok, #httpd_user{username=UserName, password=Password, user_data=UserData}};
92	Other ->
93	    {error, no_such_user}
94    end.
95
96list_users(DirData) ->
97    ?DEBUG("list_users -> ~n"
98	   "     DirData: ~p", [DirData]),
99    {Addr, Port, Dir} = lookup_common(DirData),
100    PWDB = httpd_util:key1search(DirData, auth_user_file),
101    case dets:traverse(PWDB, fun(X) -> {continue, X} end) of    %% SOOOO Ugly !
102	Records when list(Records) ->
103	    ?DEBUG("list_users -> ~n"
104		   "     Records: ~p", [Records]),
105	    {ok, [UserName || {{UserName, AnyAddr, AnyPort, AnyDir}, Password, _Data} <- Records,
106			      AnyAddr == Addr, AnyPort == Port, AnyDir == Dir]};
107	O ->
108	    ?DEBUG("list_users -> ~n"
109		   "     O: ~p", [O]),
110	    {ok, []}
111    end.
112
113delete_user(DirData, UserName) ->
114    {Addr, Port, Dir} = lookup_common(DirData),
115    PWDB = httpd_util:key1search(DirData, auth_user_file),
116    User = {UserName, Addr, Port, Dir},
117    case dets:lookup(PWDB, User) of
118	[{User, SomePassword, UserData}] ->
119	    dets:delete(PWDB, User),
120	    lists:foreach(fun(Group) -> delete_group_member(DirData, Group, UserName) end,
121			  list_groups(DirData)),
122	    true;
123	_ ->
124	    {error, no_such_user}
125    end.
126
127%%
128%% Storage of groups in the dets table:
129%% {Group, UserList} where UserList is a list of strings.
130%%
131add_group_member(DirData, GroupName, UserName) ->
132    {Addr, Port, Dir} = lookup_common(DirData),
133    GDB = httpd_util:key1search(DirData, auth_group_file),
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 = httpd_util:key1search(DirData, auth_group_file),
154    Group = {GroupName, Addr, Port, Dir},
155    case dets:lookup(GDB, Group) of
156	[{Group, Users}] ->
157	    {ok, Users};
158	Other ->
159	    {error, no_such_group}
160    end.
161
162list_groups(DirData) ->
163    {Addr, Port, Dir} = lookup_common(DirData),
164    GDB  = httpd_util:key1search(DirData, auth_group_file),
165    case dets:match(GDB, {'$1', '_'}) of
166	[] ->
167	    {ok, []};
168	List when list(List) ->
169	    Groups = lists:flatten(List),
170	    {ok, [GroupName || {GroupName, AnyAddr, AnyPort, AnyDir} <- Groups,
171			   AnyAddr == Addr, AnyPort == Port, AnyDir == Dir]};
172	_ ->
173	    {ok, []}
174    end.
175
176delete_group_member(DirData, GroupName, UserName) ->
177    {Addr, Port, Dir} = lookup_common(DirData),
178    GDB = httpd_util:key1search(DirData, auth_group_file),
179    Group = {GroupName, Addr, Port, Dir},
180    case dets:lookup(GDB, GroupName) of
181	[{Group, Users}] ->
182	    case lists:member(UserName, Users) of
183		true ->
184		    dets:delete(GDB, Group),
185		    dets:insert(GDB, {Group,
186				      lists:delete(UserName, Users)}),
187		    true;
188		false ->
189		    {error, no_such_group_member}
190	    end;
191	_ ->
192	    {error, no_such_group}
193    end.
194
195delete_group(DirData, GroupName) ->
196    {Addr, Port, Dir} = lookup_common(DirData),
197    GDB = httpd_util:key1search(DirData, auth_group_file),
198    Group = {GroupName, Addr, Port, Dir},
199    case dets:lookup(GDB, Group) of
200	[{Group, Users}] ->
201	    dets:delete(GDB, Group),
202	    true;
203	_ ->
204	    {error, no_such_group}
205    end.
206
207lookup_common(DirData) ->
208    Dir  = httpd_util:key1search(DirData, path),
209    Port = httpd_util:key1search(DirData, port),
210    Addr = httpd_util:key1search(DirData, bind_address),
211    {Addr, Port, Dir}.
212
213%% remove/1
214%%
215%% Closes dets tables used by this auth mod.
216%%
217remove(DirData) ->
218    PWDB = httpd_util:key1search(DirData, auth_user_file),
219    GDB = httpd_util:key1search(DirData, auth_group_file),
220    dets:close(GDB),
221    dets:close(PWDB),
222    ok.
223