parent
a61fda6e80
commit
6d9758c71d
6 changed files with 164 additions and 34 deletions
|
@ -208,9 +208,12 @@ body {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.save-btn {
|
.toolbox2 {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: 2em;
|
margin-right: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-btn {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,12 +72,27 @@
|
||||||
<input id="search" name="q" type="text" maxlength="128" autocomplete="off" tabindex="1">
|
<input id="search" name="q" type="text" maxlength="128" autocomplete="off" tabindex="1">
|
||||||
<input type="submit" hidden />
|
<input type="submit" hidden />
|
||||||
</form>
|
</form>
|
||||||
<div class="save-btn hidden" title="Save file">
|
<div class="toolbox2">
|
||||||
<svg viewBox="0 0 1024 1024" width="24" height="24">
|
<div class="login-btn hidden" title="Login">
|
||||||
<path
|
<svg width="16" height="16" viewBox="0 0 16 16">
|
||||||
d="M426.666667 682.666667v42.666666h170.666666v-42.666666h-170.666666z m-42.666667-85.333334h298.666667v128h42.666666V418.133333L605.866667 298.666667H298.666667v426.666666h42.666666v-128h42.666667z m260.266667-384L810.666667 379.733333V810.666667H213.333333V213.333333h430.933334zM341.333333 341.333333h85.333334v170.666667H341.333333V341.333333z"
|
<path fill-rule="evenodd"
|
||||||
fill="#444444" p-id="8311"></path>
|
d="M6 3.5a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-8a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 0-1 0v2A1.5 1.5 0 0 0 6.5 14h8a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 14.5 2h-8A1.5 1.5 0 0 0 5 3.5v2a.5.5 0 0 0 1 0v-2z" />
|
||||||
</svg>
|
<path fill-rule="evenodd"
|
||||||
|
d="M11.854 8.354a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5H1.5a.5.5 0 0 0 0 1h8.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="user-btn hidden">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6Zm2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4Zm-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664h10Z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="save-btn hidden" title="Save file">
|
||||||
|
<svg viewBox="0 0 1024 1024" width="24" height="24">
|
||||||
|
<path
|
||||||
|
d="M426.666667 682.666667v42.666666h170.666666v-42.666666h-170.666666z m-42.666667-85.333334h298.666667v128h42.666666V418.133333L605.866667 298.666667H298.666667v426.666666h42.666666v-128h42.666667z m260.266667-384L810.666667 379.733333V810.666667H213.333333V213.333333h430.933334zM341.333333 341.333333h85.333334v170.666667H341.333333V341.333333z"
|
||||||
|
fill="#444444" p-id="8311"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
* @property {boolean} allow_delete
|
* @property {boolean} allow_delete
|
||||||
* @property {boolean} allow_search
|
* @property {boolean} allow_search
|
||||||
* @property {boolean} allow_archive
|
* @property {boolean} allow_archive
|
||||||
|
* @property {boolean} auth
|
||||||
|
* @property {string} user
|
||||||
* @property {boolean} dir_exists
|
* @property {boolean} dir_exists
|
||||||
* @property {string} editable
|
* @property {string} editable
|
||||||
*/
|
*/
|
||||||
|
@ -71,6 +73,10 @@ let $emptyFolder;
|
||||||
* @type Element
|
* @type Element
|
||||||
*/
|
*/
|
||||||
let $editor;
|
let $editor;
|
||||||
|
/**
|
||||||
|
* @type Element
|
||||||
|
*/
|
||||||
|
let $userBtn;
|
||||||
|
|
||||||
function ready() {
|
function ready() {
|
||||||
$pathsTable = document.querySelector(".paths-table")
|
$pathsTable = document.querySelector(".paths-table")
|
||||||
|
@ -79,6 +85,7 @@ function ready() {
|
||||||
$uploadersTable = document.querySelector(".uploaders-table");
|
$uploadersTable = document.querySelector(".uploaders-table");
|
||||||
$emptyFolder = document.querySelector(".empty-folder");
|
$emptyFolder = document.querySelector(".empty-folder");
|
||||||
$editor = document.querySelector(".editor");
|
$editor = document.querySelector(".editor");
|
||||||
|
$userBtn = document.querySelector(".user-btn");
|
||||||
|
|
||||||
addBreadcrumb(DATA.href, DATA.uri_prefix);
|
addBreadcrumb(DATA.href, DATA.uri_prefix);
|
||||||
|
|
||||||
|
@ -86,6 +93,10 @@ function ready() {
|
||||||
document.title = `Index of ${DATA.href} - Dufs`;
|
document.title = `Index of ${DATA.href} - Dufs`;
|
||||||
document.querySelector(".index-page").classList.remove("hidden");
|
document.querySelector(".index-page").classList.remove("hidden");
|
||||||
|
|
||||||
|
if (DATA.auth) {
|
||||||
|
setupAuth();
|
||||||
|
}
|
||||||
|
|
||||||
if (DATA.allow_search) {
|
if (DATA.allow_search) {
|
||||||
setupSearch()
|
setupSearch()
|
||||||
}
|
}
|
||||||
|
@ -106,7 +117,6 @@ function ready() {
|
||||||
document.title = `Edit of ${DATA.href} - Dufs`;
|
document.title = `Edit of ${DATA.href} - Dufs`;
|
||||||
document.querySelector(".editor-page").classList.remove("hidden");;
|
document.querySelector(".editor-page").classList.remove("hidden");;
|
||||||
|
|
||||||
|
|
||||||
setupEditor();
|
setupEditor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -203,16 +213,22 @@ Uploader.globalIdx = 0;
|
||||||
|
|
||||||
Uploader.runnings = 0;
|
Uploader.runnings = 0;
|
||||||
|
|
||||||
|
Uploader.auth = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type Uploader[]
|
* @type Uploader[]
|
||||||
*/
|
*/
|
||||||
Uploader.queues = [];
|
Uploader.queues = [];
|
||||||
|
|
||||||
|
|
||||||
Uploader.runQueue = () => {
|
Uploader.runQueue = async () => {
|
||||||
if (Uploader.runnings > 2) return;
|
if (Uploader.runnings > 2) return;
|
||||||
let uploader = Uploader.queues.shift();
|
let uploader = Uploader.queues.shift();
|
||||||
if (!uploader) return;
|
if (!uploader) return;
|
||||||
|
if (!Uploader.auth) {
|
||||||
|
Uploader.auth = true;
|
||||||
|
await login();
|
||||||
|
}
|
||||||
uploader.ajax();
|
uploader.ajax();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,7 +381,7 @@ function addPath(file, index) {
|
||||||
${getPathSvg(file.path_type)}
|
${getPathSvg(file.path_type)}
|
||||||
</td>
|
</td>
|
||||||
<td class="path cell-name">
|
<td class="path cell-name">
|
||||||
<a href="${url}">${encodedName}</a>
|
<a href="${url}" target="_blank">${encodedName}</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="cell-mtime">${formatMtime(file.mtime)}</td>
|
<td class="cell-mtime">${formatMtime(file.mtime)}</td>
|
||||||
<td class="cell-size">${formatSize(file.size).join(" ")}</td>
|
<td class="cell-size">${formatSize(file.size).join(" ")}</td>
|
||||||
|
@ -385,10 +401,11 @@ async function deletePath(index) {
|
||||||
if (!confirm(`Delete \`${file.name}\`?`)) return;
|
if (!confirm(`Delete \`${file.name}\`?`)) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
await login();
|
||||||
const res = await fetch(newUrl(file.name), {
|
const res = await fetch(newUrl(file.name), {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
});
|
});
|
||||||
await assertFetch(res);
|
await assertResOK(res);
|
||||||
document.getElementById(`addPath${index}`).remove();
|
document.getElementById(`addPath${index}`).remove();
|
||||||
DATA.paths[index] = null;
|
DATA.paths[index] = null;
|
||||||
if (!DATA.paths.find(v => !!v)) {
|
if (!DATA.paths.find(v => !!v)) {
|
||||||
|
@ -425,14 +442,15 @@ async function movePath(index) {
|
||||||
const newFileUrl = fileUrlObj.origin + prefix + newPath.split("/").map(encodeURIComponent).join("/");
|
const newFileUrl = fileUrlObj.origin + prefix + newPath.split("/").map(encodeURIComponent).join("/");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
await login();
|
||||||
const res = await fetch(fileUrl, {
|
const res = await fetch(fileUrl, {
|
||||||
method: "MOVE",
|
method: "MOVE",
|
||||||
headers: {
|
headers: {
|
||||||
"Destination": newFileUrl,
|
"Destination": newFileUrl,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await assertFetch(res);
|
await assertResOK(res);
|
||||||
location.href = newFileUrl.split("/").slice(0, -1).join("/")
|
location.href = newFileUrl.split("/").slice(0, -1).join("/");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(`Cannot move \`${filePath}\` to \`${newPath}\`, ${err.message}`);
|
alert(`Cannot move \`${filePath}\` to \`${newPath}\`, ${err.message}`);
|
||||||
}
|
}
|
||||||
|
@ -445,7 +463,7 @@ function dropzone() {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
document.addEventListener("drop", e => {
|
document.addEventListener("drop", async e => {
|
||||||
if (!e.dataTransfer.items[0].webkitGetAsEntry) {
|
if (!e.dataTransfer.items[0].webkitGetAsEntry) {
|
||||||
const files = e.dataTransfer.files.filter(v => v.size > 0);
|
const files = e.dataTransfer.files.filter(v => v.size > 0);
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
|
@ -462,6 +480,18 @@ function dropzone() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setupAuth() {
|
||||||
|
if (DATA.user) {
|
||||||
|
$userBtn.classList.remove("hidden");
|
||||||
|
$userBtn.title = DATA.user;
|
||||||
|
} else {
|
||||||
|
const $loginBtn = document.querySelector(".login-btn");
|
||||||
|
$loginBtn.classList.remove("hidden");
|
||||||
|
$loginBtn.addEventListener("click", () => login(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup searchbar
|
* Setup searchbar
|
||||||
*/
|
*/
|
||||||
|
@ -491,7 +521,7 @@ function setupUpload() {
|
||||||
if (name) createFolder(name);
|
if (name) createFolder(name);
|
||||||
});
|
});
|
||||||
document.querySelector(".upload-file").classList.remove("hidden");
|
document.querySelector(".upload-file").classList.remove("hidden");
|
||||||
document.getElementById("file").addEventListener("change", e => {
|
document.getElementById("file").addEventListener("change", async e => {
|
||||||
const files = e.target.files;
|
const files = e.target.files;
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
new Uploader(file, []).upload();
|
new Uploader(file, []).upload();
|
||||||
|
@ -527,7 +557,7 @@ async function setupEditor() {
|
||||||
$editor.classList.remove("hidden");
|
$editor.classList.remove("hidden");
|
||||||
try {
|
try {
|
||||||
const res = await fetch(baseUrl());
|
const res = await fetch(baseUrl());
|
||||||
await assertFetch(res);
|
await assertResOK(res);
|
||||||
const text = await res.text();
|
const text = await res.text();
|
||||||
$editor.value = text;
|
$editor.value = text;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -549,6 +579,24 @@ async function saveChange() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function login(alert = false) {
|
||||||
|
if (!DATA.auth) return;
|
||||||
|
try {
|
||||||
|
const res = await fetch(baseUrl() + "?auth");
|
||||||
|
await assertResOK(res);
|
||||||
|
document.querySelector(".login-btn").classList.add("hidden");
|
||||||
|
$userBtn.classList.remove("hidden");
|
||||||
|
$userBtn.title = "";
|
||||||
|
} catch (err) {
|
||||||
|
let message = `Cannot login, ${err.message}`;
|
||||||
|
if (alert) {
|
||||||
|
alert(message);
|
||||||
|
} else {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a folder
|
* Create a folder
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
|
@ -556,10 +604,11 @@ async function saveChange() {
|
||||||
async function createFolder(name) {
|
async function createFolder(name) {
|
||||||
const url = newUrl(name);
|
const url = newUrl(name);
|
||||||
try {
|
try {
|
||||||
|
await login();
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
method: "MKCOL",
|
method: "MKCOL",
|
||||||
});
|
});
|
||||||
await assertFetch(res);
|
await assertResOK(res);
|
||||||
location.href = url;
|
location.href = url;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(`Cannot create folder \`${name}\`, ${err.message}`);
|
alert(`Cannot create folder \`${name}\`, ${err.message}`);
|
||||||
|
@ -569,11 +618,12 @@ async function createFolder(name) {
|
||||||
async function createFile(name) {
|
async function createFile(name) {
|
||||||
const url = newUrl(name);
|
const url = newUrl(name);
|
||||||
try {
|
try {
|
||||||
|
await login();
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
body: "",
|
body: "",
|
||||||
});
|
});
|
||||||
await assertFetch(res);
|
await assertResOK(res);
|
||||||
location.href = url + "?edit";
|
location.href = url + "?edit";
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(`Cannot create file \`${name}\`, ${err.message}`);
|
alert(`Cannot create file \`${name}\`, ${err.message}`);
|
||||||
|
@ -663,7 +713,7 @@ function encodedStr(rawStr) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function assertFetch(res) {
|
async function assertResOK(res) {
|
||||||
if (!(res.status >= 200 && res.status < 300)) {
|
if (!(res.status >= 200 && res.status < 300)) {
|
||||||
throw new Error(await res.text())
|
throw new Error(await res.text())
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,10 @@ impl AccessControl {
|
||||||
Ok(Self { rules })
|
Ok(Self { rules })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn valid(&self) -> bool {
|
||||||
|
!self.rules.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn guard(
|
pub fn guard(
|
||||||
&self,
|
&self,
|
||||||
path: &str,
|
path: &str,
|
||||||
|
@ -134,6 +138,9 @@ impl GuardType {
|
||||||
pub fn is_reject(&self) -> bool {
|
pub fn is_reject(&self) -> bool {
|
||||||
*self == GuardType::Reject
|
*self == GuardType::Reject
|
||||||
}
|
}
|
||||||
|
pub fn is_readwrite(&self) -> bool {
|
||||||
|
*self == GuardType::ReadWrite
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sanitize_path(path: &str, uri_prefix: &str) -> String {
|
fn sanitize_path(path: &str, uri_prefix: &str) -> String {
|
||||||
|
|
|
@ -144,6 +144,18 @@ impl Server {
|
||||||
return Ok(res);
|
return Ok(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let query = req.uri().query().unwrap_or_default();
|
||||||
|
let query_params: HashMap<String, String> = form_urlencoded::parse(query.as_bytes())
|
||||||
|
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if query_params.contains_key("auth") {
|
||||||
|
if !guard_type.is_readwrite() {
|
||||||
|
self.auth_reject(&mut res);
|
||||||
|
}
|
||||||
|
return Ok(res);
|
||||||
|
}
|
||||||
|
|
||||||
let head_only = method == Method::HEAD;
|
let head_only = method == Method::HEAD;
|
||||||
|
|
||||||
if self.args.path_is_file {
|
if self.args.path_is_file {
|
||||||
|
@ -170,11 +182,6 @@ impl Server {
|
||||||
|
|
||||||
let path = path.as_path();
|
let path = path.as_path();
|
||||||
|
|
||||||
let query = req.uri().query().unwrap_or_default();
|
|
||||||
let query_params: HashMap<String, String> = form_urlencoded::parse(query.as_bytes())
|
|
||||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let (is_miss, is_dir, is_file, size) = match fs::metadata(path).await.ok() {
|
let (is_miss, is_dir, is_file, size) = match fs::metadata(path).await.ok() {
|
||||||
Some(meta) => (false, meta.is_dir(), meta.is_file(), meta.len()),
|
Some(meta) => (false, meta.is_dir(), meta.is_file(), meta.len()),
|
||||||
None => (true, false, false, 0),
|
None => (true, false, false, 0),
|
||||||
|
@ -204,21 +211,32 @@ impl Server {
|
||||||
}
|
}
|
||||||
self.handle_zip_dir(path, head_only, &mut res).await?;
|
self.handle_zip_dir(path, head_only, &mut res).await?;
|
||||||
} else if allow_search && query_params.contains_key("q") {
|
} else if allow_search && query_params.contains_key("q") {
|
||||||
self.handle_search_dir(path, &query_params, head_only, &mut res)
|
let user = self.retrieve_user(authorization);
|
||||||
|
self.handle_search_dir(path, &query_params, head_only, user, &mut res)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
|
let user = self.retrieve_user(authorization);
|
||||||
self.handle_render_index(
|
self.handle_render_index(
|
||||||
path,
|
path,
|
||||||
&query_params,
|
&query_params,
|
||||||
headers,
|
headers,
|
||||||
head_only,
|
head_only,
|
||||||
|
user,
|
||||||
&mut res,
|
&mut res,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
} else if render_index || render_spa {
|
} else if render_index || render_spa {
|
||||||
self.handle_render_index(path, &query_params, headers, head_only, &mut res)
|
let user = self.retrieve_user(authorization);
|
||||||
.await?;
|
self.handle_render_index(
|
||||||
|
path,
|
||||||
|
&query_params,
|
||||||
|
headers,
|
||||||
|
head_only,
|
||||||
|
user,
|
||||||
|
&mut res,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
} else if query_params.contains_key("zip") {
|
} else if query_params.contains_key("zip") {
|
||||||
if !allow_archive {
|
if !allow_archive {
|
||||||
status_not_found(&mut res);
|
status_not_found(&mut res);
|
||||||
|
@ -226,15 +244,19 @@ impl Server {
|
||||||
}
|
}
|
||||||
self.handle_zip_dir(path, head_only, &mut res).await?;
|
self.handle_zip_dir(path, head_only, &mut res).await?;
|
||||||
} else if allow_search && query_params.contains_key("q") {
|
} else if allow_search && query_params.contains_key("q") {
|
||||||
self.handle_search_dir(path, &query_params, head_only, &mut res)
|
let user = self.retrieve_user(authorization);
|
||||||
|
self.handle_search_dir(path, &query_params, head_only, user, &mut res)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
self.handle_ls_dir(path, true, &query_params, head_only, &mut res)
|
let user = self.retrieve_user(authorization);
|
||||||
|
self.handle_ls_dir(path, true, &query_params, head_only, user, &mut res)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
} else if is_file {
|
} else if is_file {
|
||||||
if query_params.contains_key("edit") {
|
if query_params.contains_key("edit") {
|
||||||
self.handle_edit_file(path, head_only, &mut res).await?;
|
let user = self.retrieve_user(authorization);
|
||||||
|
self.handle_edit_file(path, head_only, user, &mut res)
|
||||||
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
self.handle_send_file(path, headers, head_only, &mut res)
|
self.handle_send_file(path, headers, head_only, &mut res)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -243,7 +265,8 @@ impl Server {
|
||||||
self.handle_render_spa(path, headers, head_only, &mut res)
|
self.handle_render_spa(path, headers, head_only, &mut res)
|
||||||
.await?;
|
.await?;
|
||||||
} else if allow_upload && req_path.ends_with('/') {
|
} else if allow_upload && req_path.ends_with('/') {
|
||||||
self.handle_ls_dir(path, false, &query_params, head_only, &mut res)
|
let user = self.retrieve_user(authorization);
|
||||||
|
self.handle_ls_dir(path, false, &query_params, head_only, user, &mut res)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
status_not_found(&mut res);
|
status_not_found(&mut res);
|
||||||
|
@ -382,6 +405,7 @@ impl Server {
|
||||||
exist: bool,
|
exist: bool,
|
||||||
query_params: &HashMap<String, String>,
|
query_params: &HashMap<String, String>,
|
||||||
head_only: bool,
|
head_only: bool,
|
||||||
|
user: Option<String>,
|
||||||
res: &mut Response,
|
res: &mut Response,
|
||||||
) -> BoxResult<()> {
|
) -> BoxResult<()> {
|
||||||
let mut paths = vec![];
|
let mut paths = vec![];
|
||||||
|
@ -394,7 +418,7 @@ impl Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.send_index(path, paths, exist, query_params, head_only, res)
|
self.send_index(path, paths, exist, query_params, head_only, user, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_search_dir(
|
async fn handle_search_dir(
|
||||||
|
@ -402,6 +426,7 @@ impl Server {
|
||||||
path: &Path,
|
path: &Path,
|
||||||
query_params: &HashMap<String, String>,
|
query_params: &HashMap<String, String>,
|
||||||
head_only: bool,
|
head_only: bool,
|
||||||
|
user: Option<String>,
|
||||||
res: &mut Response,
|
res: &mut Response,
|
||||||
) -> BoxResult<()> {
|
) -> BoxResult<()> {
|
||||||
let mut paths: Vec<PathItem> = vec![];
|
let mut paths: Vec<PathItem> = vec![];
|
||||||
|
@ -452,7 +477,7 @@ impl Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.send_index(path, paths, true, query_params, head_only, res)
|
self.send_index(path, paths, true, query_params, head_only, user, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_zip_dir(
|
async fn handle_zip_dir(
|
||||||
|
@ -495,6 +520,7 @@ impl Server {
|
||||||
query_params: &HashMap<String, String>,
|
query_params: &HashMap<String, String>,
|
||||||
headers: &HeaderMap<HeaderValue>,
|
headers: &HeaderMap<HeaderValue>,
|
||||||
head_only: bool,
|
head_only: bool,
|
||||||
|
user: Option<String>,
|
||||||
res: &mut Response,
|
res: &mut Response,
|
||||||
) -> BoxResult<()> {
|
) -> BoxResult<()> {
|
||||||
let index_path = path.join(INDEX_NAME);
|
let index_path = path.join(INDEX_NAME);
|
||||||
|
@ -507,7 +533,7 @@ impl Server {
|
||||||
self.handle_send_file(&index_path, headers, head_only, res)
|
self.handle_send_file(&index_path, headers, head_only, res)
|
||||||
.await?;
|
.await?;
|
||||||
} else if self.args.render_try_index {
|
} else if self.args.render_try_index {
|
||||||
self.handle_ls_dir(path, true, query_params, head_only, res)
|
self.handle_ls_dir(path, true, query_params, head_only, user, res)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
status_not_found(res)
|
status_not_found(res)
|
||||||
|
@ -682,6 +708,7 @@ impl Server {
|
||||||
&self,
|
&self,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
head_only: bool,
|
head_only: bool,
|
||||||
|
user: Option<String>,
|
||||||
res: &mut Response,
|
res: &mut Response,
|
||||||
) -> BoxResult<()> {
|
) -> BoxResult<()> {
|
||||||
let (file, meta) = tokio::join!(fs::File::open(path), fs::metadata(path),);
|
let (file, meta) = tokio::join!(fs::File::open(path), fs::metadata(path),);
|
||||||
|
@ -696,6 +723,8 @@ impl Server {
|
||||||
uri_prefix: self.args.uri_prefix.clone(),
|
uri_prefix: self.args.uri_prefix.clone(),
|
||||||
allow_upload: self.args.allow_upload,
|
allow_upload: self.args.allow_upload,
|
||||||
allow_delete: self.args.allow_delete,
|
allow_delete: self.args.allow_delete,
|
||||||
|
auth: self.args.auth.valid(),
|
||||||
|
user,
|
||||||
editable,
|
editable,
|
||||||
};
|
};
|
||||||
res.headers_mut()
|
res.headers_mut()
|
||||||
|
@ -842,6 +871,7 @@ impl Server {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn send_index(
|
fn send_index(
|
||||||
&self,
|
&self,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
|
@ -849,6 +879,7 @@ impl Server {
|
||||||
exist: bool,
|
exist: bool,
|
||||||
query_params: &HashMap<String, String>,
|
query_params: &HashMap<String, String>,
|
||||||
head_only: bool,
|
head_only: bool,
|
||||||
|
user: Option<String>,
|
||||||
res: &mut Response,
|
res: &mut Response,
|
||||||
) -> BoxResult<()> {
|
) -> BoxResult<()> {
|
||||||
if let Some(sort) = query_params.get("sort") {
|
if let Some(sort) = query_params.get("sort") {
|
||||||
|
@ -903,6 +934,8 @@ impl Server {
|
||||||
allow_search: self.args.allow_search,
|
allow_search: self.args.allow_search,
|
||||||
allow_archive: self.args.allow_archive,
|
allow_archive: self.args.allow_archive,
|
||||||
dir_exists: exist,
|
dir_exists: exist,
|
||||||
|
auth: self.args.auth.valid(),
|
||||||
|
user,
|
||||||
paths,
|
paths,
|
||||||
};
|
};
|
||||||
let output = if query_params.contains_key("json") {
|
let output = if query_params.contains_key("json") {
|
||||||
|
@ -1057,6 +1090,10 @@ impl Server {
|
||||||
size,
|
size,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn retrieve_user(&self, authorization: Option<&HeaderValue>) -> Option<String> {
|
||||||
|
self.args.auth_method.get_user(authorization?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
@ -1075,6 +1112,8 @@ struct IndexData {
|
||||||
allow_search: bool,
|
allow_search: bool,
|
||||||
allow_archive: bool,
|
allow_archive: bool,
|
||||||
dir_exists: bool,
|
dir_exists: bool,
|
||||||
|
auth: bool,
|
||||||
|
user: Option<String>,
|
||||||
paths: Vec<PathItem>,
|
paths: Vec<PathItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1085,6 +1124,8 @@ struct EditData {
|
||||||
uri_prefix: String,
|
uri_prefix: String,
|
||||||
allow_upload: bool,
|
allow_upload: bool,
|
||||||
allow_delete: bool,
|
allow_delete: bool,
|
||||||
|
auth: bool,
|
||||||
|
user: Option<String>,
|
||||||
editable: bool,
|
editable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,20 @@ fn auth_skip_on_options_method(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn auth_check(
|
||||||
|
#[with(&["--auth", "/@user:pass@user2:pass2", "-A"])] server: TestServer,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let url = format!("{}index.html?auth", server.url());
|
||||||
|
let resp = fetch!(b"GET", &url).send()?;
|
||||||
|
assert_eq!(resp.status(), 401);
|
||||||
|
let resp = fetch!(b"GET", &url).send_with_digest_auth("user2", "pass2")?;
|
||||||
|
assert_eq!(resp.status(), 401);
|
||||||
|
let resp = fetch!(b"GET", &url).send_with_digest_auth("user", "pass")?;
|
||||||
|
assert_eq!(resp.status(), 200);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn auth_readonly(
|
fn auth_readonly(
|
||||||
#[with(&["--auth", "/@user:pass@user2:pass2", "-A"])] server: TestServer,
|
#[with(&["--auth", "/@user:pass@user2:pass2", "-A"])] server: TestServer,
|
||||||
|
|
Loading…
Reference in a new issue