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 8d1e1f31..c4eadb32 100644 --- a/src/controllers/postPackages.js +++ b/src/controllers/postPackages.js @@ -70,12 +70,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. @@ -123,14 +129,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. @@ -141,6 +148,8 @@ module.exports = { "git" ); + callStack.addCall("vcs.newPackageData", newPack); + if (!newPack.ok) { const sso = new context.sso(); @@ -148,9 +157,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 @@ -159,6 +166,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") { @@ -168,10 +177,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 @@ -180,10 +186,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 @@ -191,17 +194,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. @@ -212,18 +213,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 53104262..d9f64f33 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 c834de48..96d0aa5e 100644 --- a/src/controllers/postPackagesPackageNameVersions.js +++ b/src/controllers/postPackagesPackageNameVersions.js @@ -71,6 +71,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. @@ -80,6 +81,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(); @@ -88,7 +91,7 @@ module.exports = { .notOk() .addShort("unauthorized") .addContent(user) - .addCalls("auth.verifyAuth", user) + .assignCalls(callStack) .addMessage( "User Authentication Failed when attempting to publish package version!" ); @@ -108,6 +111,8 @@ module.exports = { true ); + callStack.addCall("db.getPackageByName", packExists); + if (!packExists.ok) { // TODO LOG const sso = new context.sso(); @@ -116,8 +121,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." ); @@ -136,15 +140,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; @@ -156,9 +160,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." ); @@ -175,6 +177,8 @@ module.exports = { packMetadata.content ); + callStack.addCall("vcs.ownership", gitowner); + if (!gitowner.ok) { const sso = new context.sso(); @@ -182,10 +186,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!"); } @@ -205,10 +206,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"); } @@ -216,6 +214,8 @@ module.exports = { newName ); + callStack.addCall("db.packageNameAvailability", isAvailable); + if (isAvailable.ok) { const sso = new context.sso(); @@ -223,11 +223,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.`); } } @@ -238,6 +234,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(); @@ -246,11 +244,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 &&