]> granicus.if.org Git - ejabberd/commitdiff
Add auth to mod_http_fileserver
authorPaweł Chmielowski <pchmielowski@process-one.net>
Thu, 17 Nov 2016 11:59:27 +0000 (12:59 +0100)
committerPaweł Chmielowski <pchmielowski@process-one.net>
Thu, 17 Nov 2016 11:59:46 +0000 (12:59 +0100)
src/mod_http_fileserver.erl

index 728a2d137632e2b734e99cbdfc0f7d405939e668..a896cb8b42929a1212bb1c58ad6aaf93b9e04eae 100644 (file)
@@ -56,7 +56,7 @@
 -record(state,
        {host, docroot, accesslog, accesslogfd,
         directory_indices, custom_headers, default_content_type,
-        content_types = []}).
+        content_types = [], user_access = none}).
 
 -define(PROCNAME, ejabberd_mod_http_fileserver).
 
@@ -133,7 +133,8 @@ start_link(Host, Opts) ->
 init([Host, Opts]) ->
     try initialize(Host, Opts) of
        {DocRoot, AccessLog, AccessLogFD, DirectoryIndices,
-         CustomHeaders, DefaultContentType, ContentTypes} ->
+        CustomHeaders, DefaultContentType, ContentTypes,
+        UserAccess} ->
            {ok, #state{host = Host,
                        accesslog = AccessLog,
                        accesslogfd = AccessLogFD,
@@ -141,7 +142,8 @@ init([Host, Opts]) ->
                         directory_indices = DirectoryIndices,
                         custom_headers = CustomHeaders,
                         default_content_type = DefaultContentType,
-                        content_types = ContentTypes}}
+                       content_types = ContentTypes,
+                       user_access = UserAccess}}
     catch
        throw:Reason ->
            {stop, Reason}
@@ -165,7 +167,15 @@ initialize(Host, Opts) ->
                                     []),
     DefaultContentType = gen_mod:get_opt(default_content_type, Opts,
                                          fun iolist_to_binary/1,
-                                         ?DEFAULT_CONTENT_TYPE),
+                                        ?DEFAULT_CONTENT_TYPE),
+    UserAccess0 = gen_mod:get_opt(must_authenticate_with, Opts,
+                                 mod_opt_type(must_authenticate_with),
+                                 []),
+    UserAccess = case UserAccess0 of
+                    [] -> none;
+                    _ ->
+                        dict:from_list(UserAccess0)
+                end,
     ContentTypes = build_list_content_types(
                      gen_mod:get_opt(content_types, Opts,
                                      fun(L) when is_list(L) ->
@@ -180,7 +190,7 @@ initialize(Host, Opts) ->
              [str:join([[$*, K, " -> ", V] || {K, V} <- ContentTypes],
                        <<", ">>)]),
     {DocRoot, AccessLog, AccessLogFD, DirectoryIndices,
-     CustomHeaders, DefaultContentType, ContentTypes}.
+     CustomHeaders, DefaultContentType, ContentTypes, UserAccess}.
 
 
 %% @spec (AdminCTs::[CT], Default::[CT]) -> [CT]
@@ -246,10 +256,11 @@ try_open_log(FN, Host) ->
 %%                                      {stop, Reason, State}
 %% Description: Handling call messages
 %%--------------------------------------------------------------------
-handle_call({serve, LocalPath}, _From, State) ->
-    Reply = serve(LocalPath, State#state.docroot, State#state.directory_indices,
+handle_call({serve, LocalPath, Auth}, _From, State) ->
+    Reply = serve(LocalPath, Auth, State#state.docroot, State#state.directory_indices,
                  State#state.custom_headers,
-                  State#state.default_content_type, State#state.content_types),
+                 State#state.default_content_type, State#state.content_types,
+                 State#state.user_access),
     {reply, Reply, State};
 handle_call(_Request, _From, State) ->
     {reply, ok, State}.
@@ -305,9 +316,9 @@ code_change(_OldVsn, State, _Extra) ->
 %% @doc Handle an HTTP request.
 %% LocalPath is the part of the requested URL path that is "local to the module".
 %% Returns the page to be sent back to the client and/or HTTP status code.
-process(LocalPath, Request) ->
+process(LocalPath, #request{host = Host, auth = Auth} = Request) ->
     ?DEBUG("Requested ~p", [LocalPath]),
-    try gen_server:call(get_proc_name(Request#request.host), {serve, LocalPath}) of
+    try gen_server:call(get_proc_name(Host), {serve, LocalPath, Auth}) of
        {FileSize, Code, Headers, Contents} ->
            add_to_log(FileSize, Code, Request),
            {Code, Headers, Contents}
@@ -318,21 +329,38 @@ process(LocalPath, Request) ->
            ejabberd_web:error(not_found)
     end.
 
-serve(LocalPath, DocRoot, DirectoryIndices, CustomHeaders, DefaultContentType, ContentTypes) ->
-    FileName = filename:join(filename:split(DocRoot) ++ LocalPath),
-    case file:read_file_info(FileName) of
-        {error, enoent}                    -> ?HTTP_ERR_FILE_NOT_FOUND;
-        {error, enotdir}                   -> ?HTTP_ERR_FILE_NOT_FOUND;
-        {error, eacces}                    -> ?HTTP_ERR_FORBIDDEN;
-        {ok, #file_info{type = directory}} -> serve_index(FileName,
-                                                          DirectoryIndices,
-                                                          CustomHeaders,
-                                                          DefaultContentType,
-                                                          ContentTypes);
-        {ok, FileInfo}                     -> serve_file(FileInfo, FileName,
-                                                         CustomHeaders,
-                                                         DefaultContentType,
-                                                         ContentTypes)
+
+serve(LocalPath, Auth, DocRoot, DirectoryIndices, CustomHeaders, DefaultContentType,
+    ContentTypes, UserAccess) ->
+    CanProceed = case {UserAccess, Auth} of
+                    {none, _} -> true;
+                    {_, {User, Pass}} ->
+                        case dict:find(User, UserAccess) of
+                            {ok, Pass} -> true;
+                            _ -> false
+                        end;
+                    _ ->
+                        false
+                end,
+    case CanProceed of
+       true ->
+           FileName = filename:join(filename:split(DocRoot) ++ LocalPath),
+           case file:read_file_info(FileName) of
+               {error, enoent}                    -> ?HTTP_ERR_FILE_NOT_FOUND;
+               {error, enotdir}                   -> ?HTTP_ERR_FILE_NOT_FOUND;
+               {error, eacces}                    -> ?HTTP_ERR_FORBIDDEN;
+               {ok, #file_info{type = directory}} -> serve_index(FileName,
+                                                                 DirectoryIndices,
+                                                                 CustomHeaders,
+                                                                 DefaultContentType,
+                                                                 ContentTypes);
+               {ok, FileInfo}                     -> serve_file(FileInfo, FileName,
+                                                                CustomHeaders,
+                                                                DefaultContentType,
+                                                                ContentTypes)
+           end;
+       _ ->
+           ?HTTP_ERR_FORBIDDEN
     end.
 
 %% Troll through the directory indices attempting to find one which
@@ -466,6 +494,14 @@ mod_opt_type(default_content_type) ->
 mod_opt_type(directory_indices) ->
     fun (L) when is_list(L) -> L end;
 mod_opt_type(docroot) -> fun (A) -> A end;
+mod_opt_type(must_authenticate_with) ->
+    fun (L) when is_list(L) ->
+           lists:map(fun(UP) when is_binary(UP) ->
+                             [K, V] = binary:split(UP, <<":">>),
+                             {K, V}
+                     end, L)
+    end;
 mod_opt_type(_) ->
     [accesslog, content_types, custom_headers,
-     default_content_type, directory_indices, docroot].
+     default_content_type, directory_indices, docroot,
+     must_authenticate_with].