Skip to content

Commit

Permalink
Merge branch 'bugfix/fix-new-upload-sharing-regressions' into 'devel'
Browse files Browse the repository at this point in the history
Fix new upload sharing regressions

See merge request sds-dev/sd-connect/swift-browser-ui!192
  • Loading branch information
Sampsa Penna committed Oct 26, 2023
2 parents ef18621 + 246c951 commit 1ce78c5
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 80 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- (GL #1137) Fix upload repeatability and worker lifecycle issues leading to upload unavailability
- (GL #1127) Fix `ServiceWorker` issues affecting Firefox downloads
- (GL #1091) Fix subfolder download button downloading only one file
- (GL #1129) Fix shared folder uploads and downloads in the new upload implementation (follow-up)

### Removed

Expand Down
35 changes: 35 additions & 0 deletions swift_browser_ui_frontend/src/common/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -542,3 +542,38 @@ export async function getUploadCryptedEndpoint(

return ret.json();
}


// Convenience function for performing a signed fetch
export async function signedFetch(
method,
base,
path,
body,
params,
lifetime = 60,
) {
let signatureUrl = new URL(`/sign/${lifetime}`, document.location.origin);
signatureUrl.searchParams.append("path", path);
let signed = await GET(signatureUrl);
signed = await signed.json();

let fetchUrl = new URL(base.concat(path));
fetchUrl.searchParams.append("valid", signed.valid);
fetchUrl.searchParams.append("signature", signed.signature);
if (params !== undefined) {
for (const param in params) {
fetchUrl.searchParams.append(param, params[param]);
}
}

let resp = await fetch(
fetchUrl,
{
method,
body,
},
);

return resp;
}
77 changes: 49 additions & 28 deletions swift_browser_ui_frontend/src/common/socket.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Functions for handling interfacing between workers and upload API socket

import { DELETE, GET, PUT, getUploadEndpoint, getUploadSocket } from "./api";
import { getUploadEndpoint, getUploadSocket, signedFetch } from "./api";
import { DEV } from "./conv";
import { getDB } from "./db";

Expand Down Expand Up @@ -89,6 +89,8 @@ export default class UploadSocket {
e.data.container,
e.data.files,
e.data.pubkey,
e.data.owner,
e.data.ownerName,
).then(() => {
if (DEV) {
console.log(
Expand Down Expand Up @@ -146,7 +148,7 @@ export default class UploadSocket {
}

// Get headers for download
async getHeaders(container, fileList, pubkey) {
async getHeaders(container, fileList, pubkey, owner, ownerName) {
let headers = {};

// If no files are specified, get all files in the container
Expand All @@ -173,38 +175,34 @@ export default class UploadSocket {
container,
);

let signatureUrl = new URL(`/sign/${60}`, document.location.origin);
signatureUrl.searchParams.append("path", whitelistPath);
let signed = await GET(signatureUrl);
signed = await signed.json();
let whitelistUrl = new URL(
this.$store.state.uploadEndpoint.concat(whitelistPath),
await signedFetch(
"PUT",
this.$store.state.uploadEndpoint,
whitelistPath,
pubkey,
{
flavor: "crypt4gh",
session: upInfo.id,
},
);
whitelistUrl.searchParams.append("valid", signed.valid);
whitelistUrl.searchParams.append("signature", signed.signature);
whitelistUrl.searchParams.append("flavor", "crypt4gh");
whitelistUrl.searchParams.append("session", upInfo.id);
await PUT(whitelistUrl, pubkey);

for (const file of files) {
// Get the file header
let headerPath = `/header/${this.active.name}/${container}/${file}`;
signatureUrl = new URL(`/sign/${60}`, document.location.origin);
signatureUrl.searchParams.append("path", headerPath);
signed = await GET(signatureUrl);
signed = await signed.json();
let headerUrl = new URL(
this.$store.state.uploadEndpoint.concat(headerPath),
let header = await signedFetch(
"GET",
this.$store.state.uploadEndpoint,
`/header/${this.active.name}/${container}/${file}`,
undefined,
{
session: upInfo.id,
owner: ownerName,
},
);
headerUrl.searchParams.append("valid", signed.valid);
headerUrl.searchParams.append("signature", signed.signature);
headerUrl.searchParams.append("session", upInfo.id);
let resp = await GET(headerUrl);
let header = await resp.text();
header = await header.text();

// Prepare and sign the file URL
// Prepare the file URL
let fileUrl = new URL(
`/download/${this.active.id}/${container}/${file}`,
`/download/${owner ? owner : this.active.id}/${container}/${file}`,
document.location.origin,
);
fileUrl.searchParams.append("project", this.active.id);
Expand All @@ -215,7 +213,15 @@ export default class UploadSocket {
};
}

await DELETE(whitelistUrl);
await signedFetch(
"DELETE",
this.$store.state.uploadEndpoint,
whitelistPath,
undefined,
{
session: upInfo.id,
},
);

if (!this.useServiceWorker) {
this.downWorker.postMessage({
Expand Down Expand Up @@ -290,7 +296,14 @@ export default class UploadSocket {
async addDownload(
container,
objects,
owner = "",
) {
let ownerName = "";
if (owner) {
let ids = await this.$store.state.client.projectCheckIDs(owner);
ownerName = ids.name;
}

let fileHandle = undefined;
if (objects.length == 1) {
// Download directly into the file if available.
Expand All @@ -315,6 +328,8 @@ export default class UploadSocket {
container: container,
file: objects[0],
handle: fileHandle,
owner: owner,
ownerName: ownerName,
});
} else {
if (DEV) {
Expand All @@ -325,6 +340,8 @@ export default class UploadSocket {
command: "downloadFile",
container: container,
file: objects[0],
owner: owner,
ownerName: ownerName,
});
});
}
Expand All @@ -348,13 +365,17 @@ export default class UploadSocket {
container: container,
files: objects.length < 1 ? [] : objects,
handle: fileHandle,
owner: owner,
ownerName: ownerName,
});
} else {
navigator.serviceWorker.ready.then(reg => {
reg.active.postMessage({
command: "downloadFiles",
container: container,
files: objects.length < 1 ? [] : objects,
owner: owner,
ownerName: ownerName,
});
});
}
Expand Down
2 changes: 2 additions & 0 deletions swift_browser_ui_frontend/src/components/CObjectTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -417,13 +417,15 @@ export default {
this.$store.state.socket.addDownload(
this.$route.params.container,
subfolderFiles,
this.$route.params.owner ? this.$route.params.owner : "",
).then(() => {
if (DEV) console.log(`Started downloading subfolder ${object.name}`);
});
} else {
this.$store.state.socket.addDownload(
this.$route.params.container,
[object.name],
this.$route.params.owner ? this.$route.params.owner : "",
).then(() => {
if (DEV) console.log(`Started downloading object ${object.name}`);
});
Expand Down
8 changes: 6 additions & 2 deletions swift_browser_ui_frontend/src/components/ContainerTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,10 @@ export default {
size: "small",
title: this.$t("message.download"),
onClick: () => {
this.beginDownload(item.name);
this.beginDownload(
item.name,
item.owner ? item.owner : "",
);
},
target: "_blank",
path: mdiTrayArrowDown,
Expand Down Expand Up @@ -485,10 +488,11 @@ export default {
this.paginationOptions.itemCount - 1,
});
},
beginDownload(container) {
beginDownload(container, owner) {
this.$store.state.socket.addDownload(
container,
[],
owner,
).then(() => {
if (DEV) console.log(`Started downloading all objects from container ${container}`);
});
Expand Down
99 changes: 62 additions & 37 deletions swift_browser_ui_frontend/src/components/UploadModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ import {
keyboardNavigationInsideModal,
} from "@/common/keyboardNavigation";
import CUploadButton from "@/components/CUploadButton.vue";
import { swiftDeleteObjects, getObjects } from "@/common/api";
import { swiftDeleteObjects, getObjects, signedFetch } from "@/common/api";
import { debounce, delay } from "lodash";
import { mdiDelete } from "@mdi/js";
Expand Down Expand Up @@ -817,10 +817,32 @@ export default {
this.beginEncryptedUpload();
}
},
beginEncryptedUpload() {
if (this.pubkey.length > 0) {
async aBeginEncryptedUpload() {
// We need the proper IDs for the other project for Vault access
let owner = "";
let ownerName = "";
if (this.pubkey.length > 0 && !(this.$route.params.owner)) {
this.recvkeys = this.recvkeys.concat(this.pubkey);
} else if (this.$route.params.owner) {
let ids = await this.$store.state.client.projectCheckIDs(
this.$route.params.owner,
);
owner = ids.id;
ownerName = ids.name;
}
// Also need to get the other project's key from Vault
if (this.$route.params.owner) {
let sharedKey = await signedFetch(
"GET",
this.$store.state.uploadEndpoint,
`/cryptic/${ownerName}/keys`,
);
sharedKey = await sharedKey.text();
sharedKey = `-----BEGIN CRYPT4GH PUBLIC KEY-----\n${sharedKey}\n-----END CRYPT4GH PUBLIC KEY-----\n`;
this.recvkeys = this.recvkeys.concat([sharedKey]);
}
// Clean up old stale upload if exists
this.$store.commit("abortCurrentUpload");
this.$store.commit("eraseCurrentUpload");
Expand All @@ -835,44 +857,47 @@ export default {
this.currentFolder ? this.currentFolder : this.inputFolder,
this.$store.state.dropFiles.map(item => item),
this.recvkeys.map(item => item),
this.$route.params.owner ? this.$route.params.owner : "",
this.$route.params.owner ? this.$route.params.owner : "",
owner,
ownerName,
);
delay(() => {
if (this.abortReason !== undefined) {
if (this.abortReason
.match("Could not create or access the container.")) {
this.uploadError = this.currentFolder ?
this.$t("message.upload.accessFail")
: this.$t("message.error.createFail")
.concat(" ", this.$t("message.error.inUseOtherPrj"));
},
beginEncryptedUpload() {
this.aBeginEncryptedUpload().then(() => {
delay(() => {
if (this.abortReason !== undefined) {
if (this.abortReason
.match("Could not create or access the container.")) {
this.uploadError = this.currentFolder ?
this.$t("message.upload.accessFail")
: this.$t("message.error.createFail")
.concat(" ", this.$t("message.error.inUseOtherPrj"));
}
else if (this.abortReason.match("cancel")) {
this.uploadError = this.$t("message.upload.cancelled");
}
this.$store.commit("setUploadAbortReason", undefined);
}
else if (this.abortReason.match("cancel")) {
this.uploadError = this.$t("message.upload.cancelled");
else if (this.$store.state.encryptedFile == ""
&& this.dropFiles.length) {
//upload didn't start
this.uploadError = this.$t("message.upload.error");
this.$store.commit("stopUploading", true);
this.$store.commit("toggleUploadNotification", false);
}
this.$store.commit("setUploadAbortReason", undefined);
}
else if (this.$store.state.encryptedFile == ""
&& this.dropFiles.length) {
//upload didn't start
this.uploadError = this.$t("message.upload.error");
this.$store.commit("stopUploading", true);
this.$store.commit("toggleUploadNotification", false);
}
if (this.uploadError) {
document.querySelector("#container-error-toasts").addToast(
{
type: "error",
duration: 6000,
progress: false,
message: this.uploadError,
},
);
}
}, 1000);
this.toggleUploadModal();
if (this.uploadError) {
document.querySelector("#container-error-toasts").addToast(
{
type: "error",
duration: 6000,
progress: false,
message: this.uploadError,
},
);
}
}, 1000);
this.toggleUploadModal();
});
},
handleKeyDown: function (e) {
const focusableList = this.$refs.uploadContainer.querySelectorAll(
Expand Down
17 changes: 6 additions & 11 deletions swift_browser_ui_frontend/src/entries/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { vControl } from "@/common/csc-ui-vue-directive";

// Project JS functions
import { i18n } from "@/common/i18n";
import { GET, getUser } from "@/common/api";
import { getUser, signedFetch } from "@/common/api";
import { getProjects } from "@/common/api";

// Import SharingView and Request API
Expand Down Expand Up @@ -312,17 +312,12 @@ const app = createApp({
"setUploadEndpoint",
discovery.upload_endpoint,
);
let keyPath = `/cryptic/${this.active.name}/keys`;
let signatureUrl = new URL(`/sign/${60}`, document.location.origin);
signatureUrl.searchParams.append("path", keyPath);
let signed = await GET(signatureUrl);
signed = await signed.json();
let keyURL = new URL(
discovery.upload_endpoint.concat(keyPath),

let key = await signedFetch(
"GET",
discovery.upload_endpoint,
`/cryptic/${this.active.name}/keys`,
);
keyURL.searchParams.append("valid", signed.valid);
keyURL.searchParams.append("signature", signed.signature);
let key = await GET(keyURL);
key = await key.text();
key = `-----BEGIN CRYPT4GH PUBLIC KEY-----\n${key}\n-----END CRYPT4GH PUBLIC KEY-----\n`;
this.$store.commit("appendPubKey", key);
Expand Down
Loading

0 comments on commit 1ce78c5

Please sign in to comment.