%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2001-2016. 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_htaccess). -export([do/1, load/2, store/2]). -include("httpd.hrl"). -include("httpd_internal.hrl"). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Public methods that interface the eswapi %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %---------------------------------------------------------------------- % Public method called by the webbserver to insert the data about % Names on accessfiles %---------------------------------------------------------------------- load("AccessFileName" ++ FileNames, _Context)-> CleanFileNames=string:strip(FileNames), {ok,[],{access_files,string:tokens(CleanFileNames," ")}}. store({access_files, Files} = Conf, _) when is_list(Files)-> {ok, Conf}; store({access_files, Value}, _) -> {error, {wrong_type, {access_files, Value}}}. %---------------------------------------------------------------------- % Public method that the webbserver calls to control the page %---------------------------------------------------------------------- do(Info)-> case proplists:get_value(status, Info#mod.data) of {_Status_code, _PhraseArgs, _Reason}-> {proceed,Info#mod.data}; undefined -> control_path(Info) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% The functions that start the control if there is a accessfile %% %% and if so controls if the dir is allowed or not %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %---------------------------------------------------------------------- %Info = record mod as specified in httpd.hrl %returns either {proceed,Info#mod.data} %{proceed,[{status,403....}|Info#mod.data]} %{proceed,[{status,401....}|Info#mod.data]} %{proceed,[{status,500....}|Info#mod.data]} %---------------------------------------------------------------------- control_path(Info) -> Path = mod_alias:path(Info#mod.data, Info#mod.config_db, Info#mod.request_uri), case isErlScriptOrNotAccessibleFile(Path,Info) of true-> {proceed,Info#mod.data}; false-> case getHtAccessData(Path,Info)of {ok,public}-> %%There was no restrictions on the page continue {proceed,Info#mod.data}; {error, _Reason} -> %%Something got wrong continue or quit??????????????????/ {proceed,Info#mod.data}; {accessData,AccessData}-> controlAllowedMethod(Info,AccessData) end end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% These methods controls that the method the client used in the %% %% request is one of the limited %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %---------------------------------------------------------------------- %Control that if the accessmethod used is in the list of modes to challenge % %Info is the mod record as specified in httpd.hrl %AccessData is an ets table whit the data in the .htaccessfiles %---------------------------------------------------------------------- controlAllowedMethod(Info,AccessData)-> case allowedRequestMethod(Info,AccessData) of allow-> %%The request didnt use one of the limited methods ets:delete(AccessData), {proceed,Info#mod.data}; challenge-> authenticateUser(Info,AccessData) end. %---------------------------------------------------------------------- %Check the specified access method in the .htaccessfile %---------------------------------------------------------------------- allowedRequestMethod(Info,AccessData)-> case ets:lookup(AccessData,limit) of [{limit,all}]-> challenge; [{limit,Methods}]-> isLimitedRequestMethod(Info,Methods) end. %---------------------------------------------------------------------- %Check the specified accessmethods in the .htaccesfile against the users %accessmethod % %Info is the record from the do call %Methods is a list of the methods specified in the .htaccessfile %---------------------------------------------------------------------- isLimitedRequestMethod(Info,Methods)-> case lists:member(Info#mod.method,Methods) of true-> challenge; false -> allow end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% These methods controls that the user comes from an allowwed net %% %% and if so wheather its a valid user or a challenge shall be %% %% generated %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %---------------------------------------------------------------------- %The first thing to control is that the user is from a network %that has access to the page %---------------------------------------------------------------------- authenticateUser(Info,AccessData)-> case controlNet(Info,AccessData) of allow-> %the network is ok control that it is an allowed user authenticateUser2(Info,AccessData); deny-> %The user isnt allowed to access the pages from that network ets:delete(AccessData), {proceed,[{status,{403,Info#mod.request_uri, "Restricted area not allowed from your network"}}|Info#mod.data]} end. %---------------------------------------------------------------------- %The network the user comes from is allowed to view the resources %control whether the user needsto supply a password or not %---------------------------------------------------------------------- authenticateUser2(Info,AccessData)-> case ets:lookup(AccessData,require) of [{require,AllowedUsers}]-> case ets:lookup(AccessData,auth_name) of [{auth_name,Realm}]-> authenticateUser2(Info,AccessData,Realm,AllowedUsers); _NoAuthName-> ets:delete(AccessData), {break,[{status,{500,none, ?NICE("mod_htaccess:AuthName directive " "not specified")}}]} end; [] -> %%No special user is required the network is ok so let %%the user in ets:delete(AccessData), {proceed,Info#mod.data} end. %---------------------------------------------------------------------- %The user must send a userId and a password to get the resource %Control if its already in the http-request %if the file with users is bad send an 500 response %---------------------------------------------------------------------- authenticateUser2(Info,AccessData,Realm,AllowedUsers)-> case authenticateUser(Info,AccessData,AllowedUsers) of allow -> ets:delete(AccessData), {user,Name, _Pwd} = getAuthenticatingDataFromHeader(Info), {proceed, [{remote_user_name,Name}|Info#mod.data]}; challenge-> ets:delete(AccessData), ReasonPhrase = httpd_util:reason_phrase(401), Message = httpd_util:message(401,none,Info#mod.config_db), {proceed, [{response, {401, ["WWW-Authenticate: Basic realm=\"",Realm, "\"\r\n\r\n","\n\n", ReasonPhrase,"\n", "\n\n

",ReasonPhrase, "

\n",Message,"\n\n\n"]}}| Info#mod.data]}; deny-> ets:delete(AccessData), {break,[{status,{500,none, ?NICE("mod_htaccess:Bad path to user " "or group file")}}]} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% Methods that validate the netwqork the user comes from %% %% according to the allowed networks %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %--------------------------------------------------------------------- %Controls the users networkaddress agains the specifed networks to %allow or deny % %returns either allow or deny %---------------------------------------------------------------------- controlNet(Info,AccessData)-> UserNetwork=getUserNetworkAddress(Info), case getAllowDenyOrder(AccessData) of {_deny,[],_allow,[]}-> allow; {deny,[],allow,AllowedNetworks}-> controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny); {allow,AllowedNetworks,deny,[]}-> controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny); {deny,DeniedNetworks,allow,[]}-> controlIfAllowed(DeniedNetworks,UserNetwork,allow,deny); {allow,[],deny,DeniedNetworks}-> controlIfAllowed(DeniedNetworks,UserNetwork,allow,deny); {deny,DeniedNetworks,allow,AllowedNetworks}-> controlDenyAllow(DeniedNetworks,AllowedNetworks,UserNetwork); {allow,AllowedNetworks,deny,DeniedNetworks}-> controlAllowDeny(AllowedNetworks,DeniedNetworks,UserNetwork) end. %---------------------------------------------------------------------- %Returns the users IP-Number %---------------------------------------------------------------------- getUserNetworkAddress(Info)-> {_Socket,Address}=(Info#mod.init_data)#init_data.peername, Address. %---------------------------------------------------------------------- %Control the users Ip-number against the ip-numbers in the .htaccessfile %---------------------------------------------------------------------- controlIfAllowed(AllowedNetworks,UserNetwork,IfAllowed,IfDenied)-> case AllowedNetworks of [{allow,all}]-> IfAllowed; [{deny,all}]-> IfDenied; [{deny,Networks}]-> memberNetwork(Networks,UserNetwork,IfDenied,IfAllowed); [{allow,Networks}]-> memberNetwork(Networks,UserNetwork,IfAllowed,IfDenied); _Error-> IfDenied end. %---------------------------------------------------------------------% %The Denycontrol isn't neccessary to preform since the allow control % %override the deny control % %---------------------------------------------------------------------% controlDenyAllow(_DeniedNetworks, AllowedNetworks, UserNetwork)-> case AllowedNetworks of [{allow, all}]-> allow; [{allow, Networks}]-> case memberNetwork(Networks, UserNetwork) of true-> allow; false-> deny end end. %----------------------------------------------------------------------% %Control that the user is in the allowed list if so control that the % %network is in the denied list %----------------------------------------------------------------------% controlAllowDeny(AllowedNetworks,DeniedNetworks,UserNetwork)-> case controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny) of allow-> controlIfAllowed(DeniedNetworks,UserNetwork,deny,allow); deny -> deny end. %---------------------------------------------------------------------- %Controls if the users Ipnumber is in the list of either denied or %allowed networks %---------------------------------------------------------------------- memberNetwork(Networks,UserNetwork,IfTrue,IfFalse)-> case memberNetwork(Networks,UserNetwork) of true-> IfTrue; false-> IfFalse end. %---------------------------------------------------------------------- %regexp match the users ip-address against the networks in the list of %ipadresses or subnet addresses. memberNetwork(Networks,UserNetwork)-> case lists:filter(fun(Net)-> case re:run(UserNetwork, formatRegexp(Net), [{capture, first}]) of {match,[{0,_}]}-> true; _NotSubNet -> false end end,Networks) of []-> false; _MemberNetWork -> true end. %---------------------------------------------------------------------- %Creates a regexp from an ip-number i.e "127.0.0-> "^127[.]0[.]0.*" %"127.0.0.-> "^127[.]0[.]0[.].*" %---------------------------------------------------------------------- formatRegexp(Net)-> [SubNet1|SubNets]=string:tokens(Net,"."), NetRegexp=lists:foldl(fun(SubNet,Newnet)-> Newnet ++ "[.]" ++SubNet end,"^"++SubNet1,SubNets), case string:len(Net)-string:rchr(Net,$.) of 0-> NetRegexp++"[.].*"; _-> NetRegexp++".*" end. %---------------------------------------------------------------------- %If the user has specified if the allow or deny check shall be preformed %first get that order if no order is specified take %allow - deny since its harder that deny - allow %---------------------------------------------------------------------- getAllowDenyOrder(AccessData)-> case ets:lookup(AccessData,order) of [{order,{deny,allow}}]-> {deny,ets:lookup(AccessData,deny), allow,ets:lookup(AccessData,allow)}; _DefaultOrder-> {allow,ets:lookup(AccessData,allow), deny,ets:lookup(AccessData,deny)} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% The methods that validates the user %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %---------------------------------------------------------------------- %Control if there is anyu autheticating data in threquest header %if so it controls it against the users in the list Allowed Users %---------------------------------------------------------------------- authenticateUser(Info,AccessData,AllowedUsers)-> case getAuthenticatingDataFromHeader(Info) of {user,User,PassWord}-> authenticateUser(Info,AccessData,AllowedUsers, {user,User,PassWord}); {error,nouser}-> challenge; {error, _BadData}-> challenge end. %---------------------------------------------------------------------- %Returns the Autheticating data in the http-request %---------------------------------------------------------------------- getAuthenticatingDataFromHeader(Info)-> PrsedHeader=Info#mod.parsed_header, case proplists:get_value("authorization", PrsedHeader) of undefined-> {error,nouser}; [$B,$a,$s,$i,$c,$\ |EncodedString] = Credentials -> case (catch base64:decode_to_string(EncodedString)) of {'EXIT',{function_clause, _}} -> {error, Credentials}; UnCodedString -> case httpd_util:split(UnCodedString,":",2) of {ok,[User,PassWord]}-> {user,User,PassWord}; Other -> {error, Other} end end; BadCredentials -> {error,BadCredentials} end. %---------------------------------------------------------------------- %Returns a list of all members of the allowed groups %---------------------------------------------------------------------- getGroupMembers(Groups,AllowedGroups)-> Allowed=lists:foldl(fun({group,Name,Members},AllowedMembers)-> case lists:member(Name,AllowedGroups) of true-> AllowedMembers++Members; false -> AllowedMembers end end,[],Groups), {ok,Allowed}. authenticateUser(Info,AccessData,{{users,[]},{groups,Groups}},User)-> authenticateUser(Info,AccessData,{groups,Groups},User); authenticateUser(Info,AccessData,{{users,Users},{groups,[]}},User)-> authenticateUser(Info,AccessData,{users,Users},User); authenticateUser(Info,AccessData,{{users,Users},{groups,Groups}},User)-> AllowUser=authenticateUser(Info,AccessData,{users,Users},User), AllowGroup=authenticateUser(Info,AccessData,{groups,Groups},User), case {AllowGroup,AllowUser} of {_,allow}-> allow; {allow,_}-> allow; {challenge,_}-> challenge; {_,challenge}-> challenge; {_deny,_deny}-> deny end; %---------------------------------------------------------------------- %Controls that the user is a member in one of the allowed group %---------------------------------------------------------------------- authenticateUser(Info,AccessData,{groups,AllowedGroups},{user,User,PassWord})-> case getUsers(AccessData,group_file) of {group_data,Groups}-> {ok, Members } = getGroupMembers(Groups,AllowedGroups), authenticateUser(Info,AccessData,{users,Members}, {user,User,PassWord}); {error, _BadData}-> deny end; %---------------------------------------------------------------------- %Control that the user is one of the allowed users and that the passwd is ok %---------------------------------------------------------------------- authenticateUser(_Info,AccessData,{users,AllowedUsers},{user,User,PassWord})-> case lists:member(User,AllowedUsers) of true-> %Get the usernames and passwords from the file case getUsers(AccessData,user_file) of {error, _BadData}-> deny; {user_data,Users}-> %Users is a list of the users in %the userfile [{user,User,Passwd}] checkPassWord(Users,{user,User,PassWord}) end; false -> challenge end. %---------------------------------------------------------------------- %Control that the user User={user,"UserName","PassWd"} is %member of the list of Users %---------------------------------------------------------------------- checkPassWord(Users,User)-> case lists:member(User,Users) of true-> allow; false-> challenge end. %---------------------------------------------------------------------- %Get the users in the specified file %UserOrGroup is an atom that specify if its a group file or a user file %i.e. group_file or user_file %---------------------------------------------------------------------- getUsers({file,FileName},UserOrGroup)-> case file:open(FileName,[read]) of {ok,AccessFileHandle} -> getUsers({stream,AccessFileHandle},[],UserOrGroup); {error,Reason} -> {error,{Reason,FileName}} end; %---------------------------------------------------------------------- %The method that starts the lokkong for user files %---------------------------------------------------------------------- getUsers(AccessData,UserOrGroup)-> case ets:lookup(AccessData,UserOrGroup) of [{UserOrGroup,File}]-> getUsers({file,File},UserOrGroup); _ -> {error,noUsers} end. %---------------------------------------------------------------------- %Reads data from the filehandle File to the list FileData and when its %reach the end it returns the list in a tuple {user_file|group_file,FileData} %---------------------------------------------------------------------- getUsers({stream,File},FileData,UserOrGroup)-> case io:get_line(File,[]) of eof when UserOrGroup =:= user_file -> {user_data,FileData}; eof when UserOrGroup =:= group_file -> {group_data,FileData}; Line -> getUsers({stream,File}, formatUser(Line,FileData,UserOrGroup),UserOrGroup) end. %---------------------------------------------------------------------- %If the line is a comment remove it %---------------------------------------------------------------------- formatUser([$#|_UserDataComment],FileData,_UserOrgroup)-> FileData; %---------------------------------------------------------------------- %The user name in the file is Username:Passwd\n %Remove the newline sign and split the user name in %UserName and Password %---------------------------------------------------------------------- formatUser(UserData,FileData,UserOrGroup)-> case string:tokens(UserData," \r\n")of [User| _Whitespace] when UserOrGroup =:= user_file -> case string:tokens(User,":") of [Name,PassWord]-> [{user,Name,PassWord}|FileData]; _Error-> FileData end; GroupData when UserOrGroup =:= group_file -> parseGroupData(GroupData,FileData); _Error -> FileData end. %---------------------------------------------------------------------- %if everything is right GroupData is on the form % ["groupName:", "Member1", "Member2", "Member2" %---------------------------------------------------------------------- parseGroupData([GroupName|GroupData],FileData)-> [{group,formatGroupName(GroupName),GroupData}|FileData]. %---------------------------------------------------------------------- %the line in the file is GroupName: Member1 Member2 .....MemberN %Remove the : from the group name %---------------------------------------------------------------------- formatGroupName(GroupName)-> string:strip(GroupName,right,$:). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% Functions that parses the accessfiles %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %---------------------------------------------------------------------- %Control that the asset is a real file and not a request for an virtual %asset %---------------------------------------------------------------------- isErlScriptOrNotAccessibleFile(Path, _Info)-> case file:read_file_info(Path) of {ok,_fileInfo}-> false; {error,_Reason} -> true end. %---------------------------------------------------------------------- %Path=PathToTheRequestedFile=String %Innfo=record#mod %---------------------------------------------------------------------- getHtAccessData(Path,Info)-> HtAccessFileNames=getHtAccessFileNames(Info), case getData(Path,Info,HtAccessFileNames) of {ok,public}-> {ok,public}; {accessData,AccessData}-> {accessData,AccessData}; {error,Reason} -> {error,Reason} end. %---------------------------------------------------------------------- %returns the names of the accessfiles %---------------------------------------------------------------------- getHtAccessFileNames(Info)-> case httpd_util:lookup(Info#mod.config_db,access_files) of undefined-> [".htaccess"]; Files-> Files end. %---------------------------------------------------------------------- %HtAccessFileNames=["accessfileName1",..."AccessFileName2"] %---------------------------------------------------------------------- getData(Path,Info,HtAccessFileNames)-> SplittedPath = re:split(Path, "/", [{return, list}]), getData2(HtAccessFileNames,SplittedPath,Info). %---------------------------------------------------------------------- %Add to together the data in the Splittedpath up to the path %that is the alias or the document root %Since we do not need to control after any accessfiles before here %---------------------------------------------------------------------- getData2(HtAccessFileNames,SplittedPath,Info)-> case getRootPath(SplittedPath,Info) of {error,Path}-> {error,Path}; {ok,StartPath,RestOfSplittedPath} -> getData2(HtAccessFileNames,StartPath,RestOfSplittedPath,Info) end. %---------------------------------------------------------------------- %HtAccessFilenames is a list the names the accesssfiles can have %Path is the shortest match agains all alias and documentroot %rest of splitted path is a list of the parts of the path %Info is the mod recod from the server %---------------------------------------------------------------------- getData2(HtAccessFileNames, StartPath, RestOfSplittedPath, _Info)-> case getHtAccessFiles(HtAccessFileNames,StartPath,RestOfSplittedPath) of []-> %No accessfile qiut its a public directory {ok,public}; Files -> loadAccessFilesData(Files) end. %---------------------------------------------------------------------- %Loads the data in the accessFiles specifiied by % AccessFiles=["/hoem/public/html/accefile", % "/home/public/html/priv/accessfile"] %---------------------------------------------------------------------- loadAccessFilesData(AccessFiles)-> loadAccessFilesData(AccessFiles,ets:new(accessData,[])). %---------------------------------------------------------------------- %Returns the found data %---------------------------------------------------------------------- contextToValues(AccessData)-> case ets:lookup(AccessData,context) of [{context,Values}]-> ets:delete(AccessData,context), insertContext(AccessData,Values), {accessData,AccessData}; _Error-> {error,errorInAccessFile} end. insertContext(_AccessData, [])-> ok; insertContext(AccessData,[{allow,From}|Values])-> insertDenyAllowContext(AccessData,{allow,From}), insertContext(AccessData,Values); insertContext(AccessData,[{deny,From}|Values])-> insertDenyAllowContext(AccessData,{deny,From}), insertContext(AccessData,Values); insertContext(AccessData,[{require,{GrpOrUsr,Members}}|Values])-> case ets:lookup(AccessData,require) of [] when GrpOrUsr =:= users -> ets:insert(AccessData,{require,{{users,Members},{groups,[]}}}); [{require,{{users,Users},{groups,Groups}}}] when GrpOrUsr =:= users -> ets:insert(AccessData,{require,{{users,Users++Members}, {groups,Groups}}}); [] when GrpOrUsr =:= groups -> ets:insert(AccessData,{require,{{users,[]},{groups,Members}}}); [{require,{{users,Users},{groups,Groups}}}] when GrpOrUsr =:= groups -> ets:insert(AccessData,{require,{{users,Users}, {groups,Groups++Members}}}) end, insertContext(AccessData,Values); %%limit and order directive need no transforming they areis just to insert insertContext(AccessData,[Elem|Values])-> ets:insert(AccessData,Elem), insertContext(AccessData,Values). insertDenyAllowContext(AccessData,{AllowDeny,From})-> case From of all -> ets:insert(AccessData,{AllowDeny,all}); _AllowedSubnets -> case ets:lookup(AccessData,AllowDeny) of []-> ets:insert(AccessData,{AllowDeny,From}); [{AllowDeny,all}]-> ok; [{AllowDeny,Networks}]-> ets:insert(AccessData,{allow,Networks++From}) end end. loadAccessFilesData([],AccessData)-> %preform context to limits contextToValues(AccessData), {accessData,AccessData}; %---------------------------------------------------------------------- %Takes each file in the list and load the data to the ets table %AccessData %---------------------------------------------------------------------- loadAccessFilesData([FileName|FileNames],AccessData)-> case loadAccessFileData({file,FileName},AccessData) of overRide-> loadAccessFilesData(FileNames,AccessData); noOverRide -> {accessData,AccessData}; error-> ets:delete(AccessData), {error,errorInAccessFile} end. %---------------------------------------------------------------------- %opens the filehandle to the specified file %---------------------------------------------------------------------- loadAccessFileData({file,FileName},AccessData)-> case file:open(FileName,[read]) of {ok,AccessFileHandle}-> loadAccessFileData({stream,AccessFileHandle},AccessData,[]); {error, _Reason} -> overRide end. %---------------------------------------------------------------------- %%look att each line in the file and add them to the database %%When end of file is reached control i overrride is allowed %% if so return %---------------------------------------------------------------------- loadAccessFileData({stream,File},AccessData,FileData)-> case io:get_line(File,[]) of eof-> insertData(AccessData,FileData), case ets:match_object(AccessData,{'_',error}) of []-> %Case we got no error control that we can override a %at least some of the values case ets:match_object(AccessData, {allow_over_ride,none}) of []-> overRide; _NoOverride-> noOverRide end; _ -> error end; Line -> loadAccessFileData({stream,File},AccessData, insertLine(string:strip(Line,left),FileData)) end. %---------------------------------------------------------------------- %AccessData is a ets table where the previous found data is inserted %FileData is a list of the directives in the last parsed file %before insertion a control is done that the directive is allowed to %override %---------------------------------------------------------------------- insertData(AccessData,{{context,Values},FileData})-> insertData(AccessData,[{context,Values}|FileData]); insertData(AccessData,FileData)-> case ets:lookup(AccessData,allow_over_ride) of [{allow_over_ride,all}]-> lists:foreach(fun(Elem)-> ets:insert(AccessData,Elem) end,FileData); []-> lists:foreach(fun(Elem)-> ets:insert(AccessData,Elem) end,FileData); [{allow_over_ride,Directives}] when is_list(Directives)-> lists:foreach(fun({Key,Value}) -> case lists:member(Key,Directives) of true-> ok; false -> ets:insert(AccessData,{Key,Value}) end end,FileData); [{allow_over_ride,_}]-> %Will never appear if the user %aint doing very strang econfig files ok end. %---------------------------------------------------------------------- %Take a line in the accessfile and transform it into a tuple that %later can be inserted in to the ets:table %---------------------------------------------------------------------- %%%Here is the alternatives that resides inside the limit context insertLine("order"++ Order, {{context, Values}, FileData})-> {{context,[{order,getOrder(Order)}|Values]},FileData}; %%Let the user place a tab in the beginning insertLine([$\t,$o,$r,$d,$e,$r|Order],{{context,Values},FileData})-> {{context,[{order,getOrder(Order)}|Values]},FileData}; insertLine("allow" ++ Allow, {{context, Values}, FileData})-> {{context,[{allow,getAllowDenyData(Allow)}|Values]},FileData}; insertLine([$\t,$a,$l,$l,$o,$w|Allow],{{context,Values},FileData})-> {{context,[{allow,getAllowDenyData(Allow)}|Values]},FileData}; insertLine("deny" ++ Deny, {{context,Values}, FileData})-> {{context,[{deny,getAllowDenyData(Deny)}|Values]},FileData}; insertLine([$\t, $d,$e,$n,$y|Deny],{{context,Values},FileData})-> {{context,[{deny,getAllowDenyData(Deny)}|Values]},FileData}; insertLine("require" ++ Require, {{context, Values}, FileData})-> {{context,[{require,getRequireData(Require)}|Values]},FileData}; insertLine([$\t,$r,$e,$q,$u,$i,$r,$e|Require],{{context,Values},FileData})-> {{context,[{require,getRequireData(Require)}|Values]},FileData}; insertLine(" [Context | FileData]; insertLine(" {{context,[{limit,getLimits(Limit)}]}, FileData}; insertLine([$A,$u,$t,$h,$U,$s,$e,$r,$F,$i,$l,$e,$\ |AuthUserFile],FileData)-> [{user_file,string:strip(AuthUserFile,right,$\n)}|FileData]; insertLine([$A,$u,$t,$h,$G,$r,$o,$u,$p,$F,$i,$l,$e,$\ |AuthGroupFile], FileData)-> [{group_file,string:strip(AuthGroupFile,right,$\n)}|FileData]; insertLine("AllowOverRide" ++ AllowOverRide, FileData)-> [{allow_over_ride,getAllowOverRideData(AllowOverRide)} | FileData]; insertLine([$A,$u,$t,$h,$N,$a,$m,$e,$\ |AuthName],FileData)-> [{auth_name,string:strip(AuthName,right,$\n)}|FileData]; insertLine("AuthType" ++ AuthType,FileData)-> [{auth_type,getAuthorizationType(AuthType)}|FileData]; insertLine(_BadDirectiveOrComment,FileData)-> FileData. %---------------------------------------------------------------------- %transform the Data specified about override to a form that is ieasier %handled later %Override data="all"|"md5"|"Directive1 .... DirectioveN" %---------------------------------------------------------------------- getAllowOverRideData(OverRideData)-> case string:tokens(OverRideData," \r\n") of ["all" ++ _] -> all; ["none" ++ _]-> none; Directives -> getOverRideDirectives(Directives) end. getOverRideDirectives(Directives)-> lists:map(fun(Directive)-> transformDirective(Directive) end,Directives). transformDirective("AuthUserFile" ++ _)-> user_file; transformDirective("AuthGroupFile" ++ _) -> group_file; transformDirective("AuthName" ++ _)-> auth_name; transformDirective("AuthType" ++ _)-> auth_type; transformDirective(_UnAllowedOverRideDirective) -> unallowed. %---------------------------------------------------------------------- %Replace the string that specify which method to use for authentication %and replace it with the atom for easier mathing %---------------------------------------------------------------------- getAuthorizationType(AuthType)-> [Arg | _Crap] = string:tokens(AuthType,"\n\r\ "), case Arg of "Basic"-> basic; "MD5" -> md5; _What -> error end. %---------------------------------------------------------------------- %Returns a list of the specified methods to limit or the atom all %---------------------------------------------------------------------- getLimits(Limits)-> case re:split(Limits,">", [{return, list}])of [_NoEndOnLimit]-> error; [Methods | _Crap]-> case re:split(Methods," ", [{return, list}]) of [[]]-> all; SplittedMethods -> SplittedMethods end end. %---------------------------------------------------------------------- % Transform the order to prefrom deny allow control to a tuple of atoms %---------------------------------------------------------------------- getOrder(Order)-> [First | _Rest]=lists:map(fun(Part)-> list_to_atom(Part) end,string:tokens(Order," \n\r")), case First of deny-> {deny,allow}; allow-> {allow,deny}; _Error-> error end. %---------------------------------------------------------------------- % The string AllowDeny is "from all" or "from Subnet1 Subnet2...SubnetN" %---------------------------------------------------------------------- getAllowDenyData(AllowDeny)-> case string:tokens(AllowDeny," \n\r") of [_From|AllowDenyData] when length(AllowDenyData)>=1 -> case lists:nth(1,AllowDenyData) of "all" -> all; _Hosts-> AllowDenyData end; _ -> error end. %---------------------------------------------------------------------- % Fix the string that describes who is allowed to se the page %---------------------------------------------------------------------- getRequireData(Require)-> [UserOrGroup|UserData]=string:tokens(Require," \n\r"), case UserOrGroup of "user"-> {users,UserData}; "group" -> {groups,UserData}; _Whatever -> error end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% Methods that collects the searchways to the accessfiles %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %---------------------------------------------------------------------- % Get the whole path to the different accessfiles %---------------------------------------------------------------------- getHtAccessFiles(HtAccessFileNames,Path,RestOfSplittedPath)-> getHtAccessFiles(HtAccessFileNames,Path,RestOfSplittedPath,[]). getHtAccessFiles(HtAccessFileNames,Path,[[]],HtAccessFiles)-> HtAccessFiles ++ accessFilesOfPath(HtAccessFileNames,Path++"/"); getHtAccessFiles(_HtAccessFileNames, _Path, [], HtAccessFiles)-> HtAccessFiles; getHtAccessFiles(HtAccessFileNames,Path,[NextDir|RestOfSplittedPath], AccessFiles)-> getHtAccessFiles(HtAccessFileNames,Path++"/"++NextDir,RestOfSplittedPath, AccessFiles ++ accessFilesOfPath(HtAccessFileNames,Path++"/")). %---------------------------------------------------------------------- %Control if therer are any accessfies in the path %---------------------------------------------------------------------- accessFilesOfPath(HtAccessFileNames,Path)-> lists:foldl(fun(HtAccessFileName,Files)-> case file:read_file_info(Path++HtAccessFileName) of {ok, _}-> [Path++HtAccessFileName|Files]; {error,_Error} -> Files end end,[],HtAccessFileNames). %---------------------------------------------------------------------- %Sake the splitted path and joins it up to the documentroot or the alias %that match first %---------------------------------------------------------------------- getRootPath(SplittedPath, Info)-> DocRoot=httpd_util:lookup(Info#mod.config_db,document_root,"/"), PresumtiveRootPath= [DocRoot|lists:map(fun({_Alias,RealPath})-> RealPath end, httpd_util:multi_lookup(Info#mod.config_db,alias))], getRootPath(PresumtiveRootPath,SplittedPath,Info). getRootPath(PresumtiveRootPath,[[],Splittedpath],Info)-> getRootPath(PresumtiveRootPath,["/",Splittedpath],Info); getRootPath(PresumtiveRootPath,[Part,NextPart|SplittedPath],Info)-> case lists:member(Part,PresumtiveRootPath)of true-> {ok,Part,[NextPart|SplittedPath]}; false -> getRootPath(PresumtiveRootPath, [Part++"/"++NextPart|SplittedPath],Info) end; getRootPath(PresumtiveRootPath, [Part], _Info)-> case lists:member(Part,PresumtiveRootPath)of true-> {ok,Part,[]}; false -> {error,Part} end.