Skip to content
Merged

Devel #1873

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion core/database/foxx/api/authz_router.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,14 @@ router
if (!client) {
throw [error.ERR_PERM_DENIED, "Unknown client: " + req.queryParams.client];
}
let repo = new Repo(req.queryParams.repo);
let repo = Repo.resolveFromPath(req.queryParams.file);

if (repo.id() !== req.queryParams.repo) {
throw [
error.ERR_PERM_DENIED,
"File path does not match repository: " + req.queryParams.file,
];
}
let path_type = repo.pathType(req.queryParams.file);

// If the provided path is not within the repo throw an error
Expand Down
8 changes: 8 additions & 0 deletions core/database/foxx/api/posix_path.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use strict";

const error = require("./lib/error_codes");
const path = require("path");

module.exports = (function () {
Expand Down Expand Up @@ -45,5 +46,12 @@ module.exports = (function () {
return components.filter((component) => component !== "");
};

obj.normalizePOSIXPath = function (a_posix_path) {
if (!a_posix_path || typeof a_posix_path !== "string") {
throw [error.ERR_INVALID_PARAM, "Invalid POSIX path"];
}
return path.posix.normalize(a_posix_path);
};

return obj;
})();
108 changes: 51 additions & 57 deletions core/database/foxx/api/record.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,23 +66,23 @@ class Record {
/**
* Generates the full path to the record as it should appear in the repository.
*
* @param {object} loc - The location object which specifies the owner of the record.
* @param {string} basePath - The base path where the record is stored.
*
* @returns {string} - the path to the record or null if error
* @param {string} uid - The owner uid (e.g. "u/bob" or "p/myproject")
* @param {string} basePath - The base path of the repository.
* @returns {string|null} - the path to the record or null if error
*/
_pathToRecord(loc, basePath) {
_pathToRecord(uid, basePath) {
const path = basePath.endsWith("/") ? basePath : basePath + "/";
if (loc.uid.charAt(0) == "u") {
return path + "user/" + loc.uid.substr(2) + "/" + this.#key;
} else if (loc.uid.charAt(0) == "p") {
return path + "project/" + loc.uid.substr(2) + "/" + this.#key;
if (uid.charAt(0) === "u") {
return path + "user/" + uid.substr(2) + "/" + this.#key;
} else if (uid.charAt(0) === "p") {
return path + "project/" + uid.substr(2) + "/" + this.#key;
Comment on lines +75 to +78
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Avoid deprecated substr and prefer slice for uid extraction.

This still calls uid.substr(2) for both user and project prefixes. Since substr is deprecated, please switch to uid.slice(2) to use the modern equivalent without changing behavior.

Suggested change
if (uid.charAt(0) === "u") {
return path + "user/" + uid.substr(2) + "/" + this.#key;
} else if (uid.charAt(0) === "p") {
return path + "project/" + uid.substr(2) + "/" + this.#key;
if (uid.charAt(0) === "u") {
return path + "user/" + uid.slice(2) + "/" + this.#key;
} else if (uid.charAt(0) === "p") {
return path + "project/" + uid.slice(2) + "/" + this.#key;

} else {
this.#error = error.ERR_INTERNAL_FAULT;
this.#err_msg = "Provided path does not fit within supported directory ";
this.#err_msg += "structure for repository, no user or project folder has";
this.#err_msg += " been determined for the record.";
console.log(e);
this.#err_msg =
"Provided uid does not fit within supported directory " +
"structure for repository, no user or project folder has " +
"been determined for the record. uid: " +
uid;
return null;
}
}
Expand Down Expand Up @@ -174,77 +174,71 @@ class Record {
return !!this.#alloc;
}

/**
* Validates if the provided record path is consistent with the database.
*
* @param {string} a_path - The path to validate.
* @returns {boolean} True if consistent, otherwise false.
*/
isPathConsistent(a_path) {
// This function will populate the this.#loc member and the this.#alloc
// member
if (!this.isManaged()) {
return false;
}

// If there is a new repo we need to check the path there and use that
if (!a_path.startsWith("/")) {
a_path = "/" + a_path;
}

// If record is in flight, only check new location
if (this.#loc.hasOwnProperty("new_repo") && this.#loc.new_repo) {
// Below we get the allocation associated with data item by
// 1. Checking if the data item is in flight, is in the process
// of being moved to a new location or new owner and using that
// oweners id.
// 2. Using the loc.uid parameter if not inflight to get the owner
// id.
const new_uid = this.#loc.new_owner ? this.#loc.new_owner : this.#loc.uid;
const new_alloc = g_db.alloc.firstExample({
_from: this.#loc.new_owner ? this.#loc.new_owner : this.#loc.uid,
_from: new_uid,
_to: this.#loc.new_repo,
});

// If no allocation is found for the item throw an error
// if the paths do not align also throw an error.
if (!new_alloc) {
this.#error = error.ERR_PERM_DENIED;
this.#err_msg =
"Permission denied, '" + this.#key + "' is not part of an allocation '";
"Permission denied, '" + this.#key + "' is not part of an allocation'";
return false;
}

this.#repo = g_db._document(this.#loc.new_repo);

if (!this.#repo) {
const new_repo = g_db._document(this.#loc.new_repo);
if (!new_repo) {
this.#error = error.ERR_INTERNAL_FAULT;
this.#err_msg =
"Unable to find repo that record is meant to be allocated too, '" +
"Unable to find repo '" +
this.#loc.new_repo +
"' record '" +
this.#data_id;
"' for record '" +
this.#data_id +
"'";
return false;
}

// If path is missing the starting "/" add it back in
if (!a_path.startsWith("/") && this.#repo.path.startsWith("/")) {
a_path = "/" + a_path;
let new_path = this._pathToRecord(new_uid, new_repo.path);
if (new_path === a_path) {
return true;
}

let stored_path = this._pathToRecord(this.#loc, this.#repo.path);

if (!this._comparePaths(stored_path, a_path)) {
return false;
}
} else {
this.#repo = g_db._document(this.#loc._to);
this.#error = error.ERR_PERM_DENIED;
this.#err_msg =
"Record path is not consistent with repo. Expected: " +
new_path +
" but got: " +
a_path;
return false;
}

if (!a_path.startsWith("/") && this.#repo.path.startsWith("/")) {
a_path = "/" + a_path;
}
let stored_path = this._pathToRecord(this.#loc, this.#repo.path);
// No in-flight move — check current location
this.#repo = g_db._document(this.#loc._to);
let current_path = this._pathToRecord(this.#loc.uid, this.#repo.path);

// If there is no new repo check that the paths align
if (!this._comparePaths(stored_path, a_path)) {
return false;
}
if (current_path === a_path) {
return true;
}
return true;

this.#error = error.ERR_PERM_DENIED;
this.#err_msg =
"Record path is not consistent with repo. Expected: " +
current_path +
" but got: " +
a_path;
return false;
}
}

Expand Down
34 changes: 34 additions & 0 deletions core/database/foxx/api/repo.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,40 @@ class Repo {

return PathType.UNKNOWN;
}

static resolveFromPath(file_path) {
var canonical = pathModule.normalizePOSIXPath(file_path);

if (canonical !== file_path) {
throw [error.ERR_PERM_DENIED, "Path contains invalid sequences: " + file_path];
}

var repos = g_db.repo.all().toArray();
var best_match = null;
var best_length = 0;

for (var i = 0; i < repos.length; i++) {
var repo_path = repos[i].path;
if (repo_path.charAt(repo_path.length - 1) !== "/") {
repo_path += "/";
}
if (canonical.indexOf(repo_path) === 0 || canonical === repo_path.slice(0, -1)) {
if (repo_path.length > best_length) {
best_match = repos[i];
best_length = repo_path.length;
}
}
}

if (!best_match) {
throw [
error.ERR_PERM_DENIED,
"File path does not match any known repository: " + file_path,
];
}

return new Repo(best_match._id);
}
}

module.exports = { Repo, PathType };
17 changes: 14 additions & 3 deletions core/database/foxx/api/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -892,14 +892,20 @@ var tasks_func = (function () {
//console.log("taskRunRecAllocChg - do xfr");
// Transfer data step

var tokens = g_lib.getAccessToken(a_task.client);
const token_doc = new UserToken({
user_id: a_task.client,
}).get_token();
var tokens = UserToken.formatUserTokenForTransferTask(token_doc);
const extra_token_format = UserToken.formatUserToken(false, token_doc, false);
params = {
uid: a_task.client,
type: a_task.type,
encrypt: state.encrypt,
acc_tok: tokens.acc_tok,
ref_tok: tokens.ref_tok,
acc_tok_exp_in: tokens.acc_tok_exp_in,
token_type: extra_token_format.token_type,
scopes: extra_token_format.scopes,
};
params = Object.assign(params, xfr);
reply = {
Expand Down Expand Up @@ -1276,15 +1282,20 @@ var tasks_func = (function () {
case 1:
//console.log("taskRunRecOwnerChg - do xfr");
// Transfer data step

var tokens = g_lib.getAccessToken(a_task.client);
const token_doc = new UserToken({
user_id: a_task.client,
}).get_token();
var tokens = UserToken.formatUserTokenForTransferTask(token_doc);
const extra_token_format = UserToken.formatUserToken(false, token_doc, false);
params = {
uid: a_task.client,
type: a_task.type,
encrypt: state.encrypt,
acc_tok: tokens.acc_tok,
ref_tok: tokens.ref_tok,
acc_tok_exp_in: tokens.acc_tok_exp_in,
token_type: extra_token_format.token_type,
scopes: extra_token_format.scopes,
};
params = Object.assign(params, xfr);
reply = {
Expand Down
2 changes: 1 addition & 1 deletion core/database/foxx/tests/record.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ describe("Record Class", () => {
const valid_key = "1127";
const key_id = "d/" + valid_key;
const owner_id = "u/john";
const repo_id = "repo/orange-at-com";
const repo_id = "repo/orange-at-org";
const new_repo_id = "repo/watermelon-at-org";

// Create nodes
Expand Down
Loading