diff --git a/src/context.js b/src/context.js index 1e9b3014..96e7837b 100644 --- a/src/context.js +++ b/src/context.js @@ -15,6 +15,7 @@ module.exports = { ssoPaginate: require("./models/ssoPaginate.js"), ssoRedirect: require("./models/ssoRedirect.js"), ssoHTML: require("./models/ssoHTML.js"), + callStack: require("./models/callStack.js"), models: { constructPackageObjectFull: require("./models/constructPackageObjectFull.js"), constructPackageObjectShort: require("./models/constructPackageObjectShort.js"), diff --git a/src/controllers/postPackages.js b/src/controllers/postPackages.js index d5de79cf..c086dcc3 100644 --- a/src/controllers/postPackages.js +++ b/src/controllers/postPackages.js @@ -78,12 +78,18 @@ module.exports = { }, async logic(params, context) { + + const callStack = new context.callStack(); + const user = await context.auth.verifyAuth(params.auth, context.database); + + callStack.addCall("auth.verifyAuth", user); + // Check authentication if (!user.ok) { const sso = new context.sso(); - return sso.notOk().addContent(user).addCalls("auth.verifyAuth", user); + return sso.notOk().addContent(user).assignCalls(callStack); } // Check repository format validity. @@ -131,14 +137,15 @@ module.exports = { params.repository ); + callStack.addCall("vcs.ownership", gitowner); + if (!gitowner.ok) { const sso = new context.sso(); return sso .notOk() .addContent(gitowner) - .addCalls("auth.verifyAuth", user) - .addCalls("vcs.ownership", gitowner); + .assignCalls(callStack); } // Now knowing they own the git repo, and it doesn't exist here, lets publish. @@ -149,6 +156,8 @@ module.exports = { "git" ); + callStack.addCall("vcs.newPackageData", newPack); + if (!newPack.ok) { const sso = new context.sso(); @@ -156,9 +165,7 @@ module.exports = { .notOk() .addContent(newPack) .addMessage(newPack.content) // This is where we trust the output - .addCalls("auth.verifyAuth", user) - .addCalls("vcs.ownership", gitowner) - .addCalls("vcs.newPackageData", newPack); + .assignCalls(callStack); } // Now that we have the name the package will actually take, we want @@ -167,6 +174,8 @@ module.exports = { newPack.content.name ); + callStack.addCall("db.packageNameAvailability", nameAvailable); + if (!nameAvailable.ok) { // We need to ensure the error is not found or otherwise if (nameAvailable.short !== "not_found") { @@ -176,10 +185,7 @@ module.exports = { return sso .notOk() .addContent(nameAvailable) - .addCalls("auth.verifyAuth", user) - .addCalls("vcs.ownership", gitowner) - .addCalls("vcs.newPackageData", newPack) - .addCalls("db.packageNameAvailability", nameAvailable); + .assignCalls(callStack); } // But if the short is in fact "not_found" we can report the package as // not being available at this name @@ -188,10 +194,7 @@ module.exports = { return sso .notOk() .addShort("package_exists") - .addCalls("auth.verifyAuth", user) - .addCalls("vcs.ownership", gitowner) - .addCalls("vcs.newPackageData", newPack) - .addCalls("db.packageNameAvailability", nameAvailable); + .assignCalls(callStack); } // Now with valid package data, we can insert them into the DB @@ -199,17 +202,15 @@ module.exports = { newPack.content ); + callStack.addCall("db.insertNewPackage", insertedNewPack); + if (!insertedNewPack.ok) { const sso = new context.sso(); return sso .notOk() .addContent(insertedNewPack) - .addCalls("auth.verifyAuth", user) - .addCalls("vcs.ownership", gitowner) - .addCalls("vcs.newPackageData", newPack) - .addCalls("db.packageNameAvailability", nameAvailable) - .addCalls("db.insertNewPackage", insertedNewPack); + .assignCalls(callStack); } // Finally we can return what was actually put into the databse. @@ -220,18 +221,15 @@ module.exports = { true ); + callStack.addCall("db.getPackageByName", newDbPack); + if (!newDbPack.ok) { const sso = new context.sso(); return sso .notOk() .addContent(newDbPack) - .addCalls("auth.verifyAuth", user) - .addCalls("vcs.ownership", gitowner) - .addCalls("vcs.newPackageData", newPack) - .addCalls("db.insertNewPackage", insertedNewPack) - .addCalls("db.packageNameAvailability", nameAvailable) - .addCalls("db.getPackageByName", newDbPack); + .assignCalls(callStack); } const packageObjectFull = await context.models.constructPackageObjectFull( diff --git a/src/controllers/postPackagesPackageNameStar.js b/src/controllers/postPackagesPackageNameStar.js index 94d86a8e..615a1e8e 100644 --- a/src/controllers/postPackagesPackageNameStar.js +++ b/src/controllers/postPackagesPackageNameStar.js @@ -33,12 +33,16 @@ module.exports = { }, }, async logic(params, context) { + const callStack = new context.callStack(); + const user = await context.auth.verifyAuth(params.auth, context.database); + callStack.addCall("auth.verifyAuth", user); + if (!user.ok) { const sso = new context.sso(); - return sso.notOk().addContent(user).addCalls("auth.verifyAuth", user); + return sso.notOk().addContent(user).assignCalls(callStack); } const star = await context.database.updateIncrementStar( @@ -46,14 +50,15 @@ module.exports = { params.packageName ); + callStack.addCall("db.updateIncrementStar", star); + if (!star.ok) { const sso = new context.sso(); return sso .notOk() .addContent(star) - .addCalls("auth.verifyAuth", user) - .addCalls("db.updateIncrementStar", star); + .assignCalls(callStack); } // Now with a success we want to return the package back in this query @@ -62,15 +67,15 @@ module.exports = { true ); + callStack.addCall("db.getPackageByName", pack); + if (!pack.ok) { const sso = new context.sso(); return sso .notOk() .addContent(pack) - .addCalls("auth.verifyAuth", user) - .addCalls("db.updateIncrementStar", star) - .addCalls("db.getPackageByName", pack); + .assignCalls(callStack); } pack = await context.models.constructPackageObjectFull(pack.content); diff --git a/src/controllers/postPackagesPackageNameVersions.js b/src/controllers/postPackagesPackageNameVersions.js index 2aaf4980..a019d4d9 100644 --- a/src/controllers/postPackagesPackageNameVersions.js +++ b/src/controllers/postPackagesPackageNameVersions.js @@ -79,6 +79,7 @@ module.exports = { }, async logic(params, context) { + const callStack = new context.callStack(); // On renaming: // When a package is being renamed, we will expect that packageName will // match a previously published package. @@ -88,6 +89,8 @@ module.exports = { const user = await context.auth.verifyAuth(params.auth, context.database); + callStack.addCall("auth.verifyAuth", user); + if (!user.ok) { // TODO LOG const sso = new context.sso(); @@ -96,7 +99,7 @@ module.exports = { .notOk() .addShort("unauthorized") .addContent(user) - .addCalls("auth.verifyAuth", user) + .assignCalls(callStack) .addMessage( "User Authentication Failed when attempting to publish package version!" ); @@ -116,6 +119,8 @@ module.exports = { true ); + callStack.addCall("db.getPackageByName", packExists); + if (!packExists.ok) { // TODO LOG const sso = new context.sso(); @@ -124,8 +129,7 @@ module.exports = { .notOk() .addShort("not_found") .addContent(packExists) - .addCalls("auth.verifyAuth", user) - .addCalls("db.getPackageByName", packExists) + .assignCalls(callStack) .addMessage( "The server was unable to locate your package when publishing a new version." ); @@ -144,15 +148,15 @@ module.exports = { "git" ); + callStack.addCall("vcs.newVersionData", packMetadata); + if (!packMetadata.ok) { const sso = new context.sso(); return sso .notOk() .addContent(packMetadata) - .addCalls("auth.verifyAuth", user) - .addCalls("db.getPackageByName", packExists) - .addCalls("vcs.newVersionData", packMetadata); + .assignCalls(callStack); } const newName = packMetadata.content.name; @@ -164,9 +168,7 @@ module.exports = { return sso .notOk() .addShort("bad_repo") - .addCalls("auth.verifyAuth", user) - .addCalls("db.getPackageByName", packExists) - .addCalls("vcs.newVersionData", packMetadata) + .assignCalls(callStack) .addMessage( "Package name doesn't match local name, with rename false." ); @@ -183,6 +185,8 @@ module.exports = { packMetadata.content ); + callStack.addCall("vcs.ownership", gitowner); + if (!gitowner.ok) { const sso = new context.sso(); @@ -190,10 +194,7 @@ module.exports = { .notOk() .addShort("unauthorized") .addContent(gitowner) - .addCalls("auth.verifyAuth", user) - .addCalls("db.getPackageByName", packExists) - .addCalls("vcs.newVersionData", packMetadata) - .addCalls("vcs.ownership", gitowner) + .assignCalls(callStack) .addMessage("User failed git ownership check!"); } @@ -213,10 +214,7 @@ module.exports = { .notOk() .addShort("server_error") .addContent(isBanned) - .addCalls("auth.verifyAuth", user) - .addCalls("db.getPackageByName", packExists) - .addCalls("vcs.newVersionData", packMetadata) - .addCalls("vcs.ownership", gitowner) + .assignCalls(callStack) .addMessage("This package Name is Banned on the Pulsar Registry"); } @@ -224,6 +222,8 @@ module.exports = { newName ); + callStack.addCall("db.packageNameAvailability", isAvailable); + if (isAvailable.ok) { const sso = new context.sso(); @@ -231,11 +231,7 @@ module.exports = { .notOk() .addShort("server_error") .addContent(isAvailable) - .addCalls("auth.verifyAuth", user) - .addCalls("db.getPackageByName", packExists) - .addCalls("vcs.newVersionData", packMetadata) - .addCalls("vcs.ownership", gitowner) - .addCalls("db.packageNameAvailability", isAvailable) + .assignCalls(callStack) .addMessage(`The Package Name: ${newName} is not available.`); } } @@ -246,6 +242,8 @@ module.exports = { rename ? currentName : null ); + callStack.addCall("db.insertNewPackageVersion", addVer); + if (!addVer.ok) { // TODO Use hardcoded message until we can verify messages from db are safe const sso = new context.sso(); @@ -254,11 +252,7 @@ module.exports = { .notOk() .addShort("server_error") .addContent(addVer) - .addCalls("auth.verifyAuth", user) - .addCalls("db.getPackageByName", packExists) - .addCalls("vcs.newVersionData", packMetadata) - .addCalls("vcs.ownership", gitowner) - .addCalls("db.insertNewPackageVersion", addVer) + .assignCalls(callStack) .addMessage("Failed to add the new package version to the database."); } diff --git a/src/models/callStack.js b/src/models/callStack.js new file mode 100644 index 00000000..14f3e348 --- /dev/null +++ b/src/models/callStack.js @@ -0,0 +1,43 @@ +const { performance } = require("node:perf_hooks"); + +module.exports = +class CallStack { + constructor() { + this.calls = {}; + + this.initialize(); + } + + initialize() { + this.addCall("init", {}); + } + + addCall(id, content) { + this.calls[id] = { + content: this.sanitize(content), + time: performance.now() + }; + } + + // Attempts to remove any sensitive data that may be found within + sanitize(content) { + if (typeof content !== "object") { + return content; + } + + let outContent = {}; + + for (const key in content) { + switch(key) { + case "token": + outContent[key] = "*****"; + break; + default: + outContent[key] = content[key]; + break; + } + } + + return outContent; + } +} diff --git a/src/models/sso.js b/src/models/sso.js index 64c63621..c92756a7 100644 --- a/src/models/sso.js +++ b/src/models/sso.js @@ -75,6 +75,11 @@ module.exports = class SSO { return this; } + assignCalls(content) { + this.calls = content; + return this; + } + addShort(enumValue) { if ( this.short?.length <= 0 &&