Skip to content

Commit

Permalink
Migrate last handlers, support RAW endpoint types for increased contr…
Browse files Browse the repository at this point in the history
…ol of HTTP handling
  • Loading branch information
confused-Techie committed Sep 14, 2023
1 parent 97bdd56 commit 39d961a
Show file tree
Hide file tree
Showing 7 changed files with 324 additions and 133 deletions.
4 changes: 4 additions & 0 deletions src/controllers/endpoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ module.exports = [
require("./deletePackagesPackageName.js"),
require("./deletePackagesPackageNameStar.js"),
require("./deletePackagesPackageNameVersionsVersionName.js"),
require("./getLogin.js"),
require("./getOauth.js"),
require("./getPackages.js"),
require("./getPackagesFeatured.js"),
require("./getPackagesPackageName.js"),
require("./getPackagesPackageNameStargazers.js"),
require("./getPackagesPackageNameVersionsVersionName.js"),
require("./getPackagesPackageNameVersionsVersionNameTarball.js"),
require("./getPackagesSearch.js"),
require("./getPat.js"),
require("./getRoot.js"),
require("./getStars.js"),
require("./getThemes.js"),
Expand Down
44 changes: 44 additions & 0 deletions src/controllers/getLogin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module.exports = {
docs: {

},
endpoint: {
method: "GET",
paths: [ "/api/login" ],
rateLimit: "auth",
successStatus: 200,
options: {
Allow: "GET",
"X-Content-Type-Options": "nosniff"
},
endpointKind: "raw"
},

async logic(req, res, context) {
// The first point of contact to log into the app.
// Since this will be the endpoint for a user to login, we need
// to redirect to GH.
// @see https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps

// Generate a random key
const stateKey = context.utils.generateRandomString(64);

// Before redirect, save the key into the db
const saveStateKey = await context.database.authStoreStateKey(stateKey);

if (!saveStateKey.ok) {
res.status(500).json({
message: "Application Error: Failed to generate secure state key."
});
context.logger.httpLog(req, res);
return;
}

res.status(302).redirect(
`https://github.com/login/oauth/authorize?client_id=${context.config.GH_CLIENTID}&redirect_uri=${context.config.GH_REDIRECTURI}&state=${stateKey}&scope=public_repo%20read:org`
);

context.logger.httpLog(req, res);
return;
}
};
124 changes: 124 additions & 0 deletions src/controllers/getOauth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
const superagent = require("superagent");

module.exports = {
docs: {

},
endpoint: {
method: "GET",
paths: [ "/api/oauth" ],
rateLimit: "auth",
successStatus: 200,
options: {
Allow: "GET",
"X-Content-Type-Options": "nosniff"
},
endpointKind: "raw"
},

async logic(req, res, context) {
let params = {
state: req.query.state ?? "",
code: req.query.code ?? ""
};

// First we want to ensure that the received state key is valid
const validStateKey = await context.database.authCheckAndDeleteStateKey(params.state);

if (!validStateKey.ok) {
res.status(500).json({
message: "Application Error: Invalid State Key provided."
});
context.logger.httpLog(req, res);
return;
}

// Retrieve access token
const initialAuth = await superagent
.post("https://github.com/login/oauth/access_token")
.query({
code: params.code,
redirect_uri: context.config.GH_REDIRECTURI,
client_id: context.config.GH_CLIENTID,
client_secret: context.config.GH_CLIENTSECRET
});

const accessToken = iniitalAuth.body?.access_token;

if (accessToken === null || initialAuth.body?.token_type === null) {
res.status(500).json({
message: "Application Error: Authentication to GitHub failed."
});
context.logger.httpLog(req, res);
return;
}

try {
// Request the user data using the access token
const userData = await superagent
.get("https://api.github.com/user")
.set({ Authorization: `Bearer ${accessToken}` })
.set({ "User-Agent": context.config.GH_USERAGENT });

if (userData.status !== 200) {
res.status(500).json({
message: `Application Error: Received HTTP Status ${userData.status}`
});
context.logger.httpLog(req, res);
return;
}

// Now retrieve the user data that we need to store into the DB
const username = userData.body.login;
const userId = userData.body.node_id;
const userAvatar = userData.body.avatar_url;

const userExists = await context.database.getUserByNodeID(userId);

if (userExists.ok) {
// This means that the user does in fact already exist.
// And from there they are likely reauthenticating,
// But since we don't save any type of auth tokens, the user just needs
// a new one and we should return their new one to them.

// Now we redirect to the frontend site
res.redirect(`https://web.pulsar-edit.dev/users?token=${accessToken}`);
context.logger.httpLog(req, res);
return;
}

// The user does not exist, so we save its data into the DB
let createdUser = await context.database.insertNewUser(
username,
userId,
userAvatar
);

if (!createdUser.ok) {
res.status(500).json({
message: "Application Error: Creating the user account failed!"
});
context.logger.httpLog(req, res);
return;
}

// Before returning, lets append their access token
createdUser.content.token = accessToken;

// Now re redirect to the frontend site
res.redirect(
`https://web.pulsar-edit.dev/users?token=${createdUser.content.token}`
);
context.logger.httpLog(req, res);
return;

} catch(err) {
context.logger.generic(2, "/api/oauth Caught an Error!", { type: "error", err: err });
res.status(500).json({
message: "Application Error: The server encountered an error processing the request."
});
context.logger.httpLog(req, res);
return;
}
}
};
98 changes: 98 additions & 0 deletions src/controllers/getPat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
const superagent = require("superagent");

module.exports = {
docs: {

},
endpoint: {
method: "GET",
paths: [ "/api/pat" ],
rateLimit: "auth",
successStatus: 200,
options: {
Allow: "GET",
"X-Content-Type-Options": "nosniff"
},
endpointKind: "raw"
},

async logic(req, res, context) {
let params = {
token: req.query.token ?? ""
};

if (params.token === "") {
res.status(404).json({
message: "Not Found: Parameter 'token' is empty."
});
context.logger.httpLog(req, res);
return;
}

try {
const userData = await superagent
.get("https://api.github.com/user")
.set({ Authorization: `Bearer ${params.token}` })
.set({ "User-Agent": context.config.GH_USERAGENT });

if (userData.status !== 200) {
context.logger.generic(2, "User Data request to GitHub failed!", {
type: "object",
obj: userData
});
res.status(500).json({
message: `Application Error: Received HTTP Status ${userData.status} when contacting GitHub!`
});
context.logger.httpLog(req, res);
return;
}

// Now to build a valid user object
const username = userData.body.login;
const userId = userData.body.node_id;
const userAvatar = userData.body.avatar_url;

const userExists = await context.database.getUserByNodeID(userId);

if (userExists.ok) {
// If we plan to allow updating the user name or image, we would do so here

// Now to redirect to the frontend site.
res.redirect(`https://web.pulsar-edit.dev/users?token=${params.token}`);
context.logger.httpLog(req, res);
return;
}

let createdUser = await context.database.insertNewUser(
username,
userId,
userAvatar
);

if (!createdUser.ok) {
context.logger.generic(2, `Creating user failed! ${username}`);
res.status(500).json({
message: "Application Error: Creating the user account failed!"
});
context.logger.httpLog(req, res);
return;
}

// Before returning, lets append their PAT token
createdUser.content.token = params.token;

res.redirect(
`https://web.pulsar-edit.dev/users?token=${createdUser.cotnent.token}`
);
context.logger.httpLog(req, res);
return;
} catch(err) {
context.logger.generic(2, "/api/pat Caught an Error!", { type: "error", err: err });
res.status(500).json({
message: "Application Error: The server encountered an error processing the request."
});
context.logger.httpLog(req, res);
return;
}
}
};
48 changes: 0 additions & 48 deletions src/handlers/nonMigratedHandlers.js

This file was deleted.

Loading

0 comments on commit 39d961a

Please sign in to comment.