mirror of
https://github.com/status-im/status-github-bot.git
synced 2025-01-14 15:25:13 +00:00
Take access token expiration into account
This commit is contained in:
parent
490c7f38b5
commit
52a626d645
@ -85,7 +85,7 @@ async function assignPullRequestToReview(gitHubContext, githubPayload, robot) {
|
|||||||
robot.logger.debug(`Created card: ${ghcard.data.url}`, ghcard.data.id);
|
robot.logger.debug(`Created card: ${ghcard.data.url}`, ghcard.data.id);
|
||||||
|
|
||||||
// Send message to Slack
|
// Send message to Slack
|
||||||
robot.messageRoom(githubConfig.slack.notification.room, `Assigned PR to ``${reviewColumnName}`` in ``${projectBoardName}`` project\n${githubPayload.pull_request.html_url}`);
|
robot.messageRoom(githubConfig.slack.notification.room, `Assigned PR to ${reviewColumnName} in ${projectBoardName} project\n${githubPayload.pull_request.html_url}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
robot.logger.error(`Couldn't create project card for the PR: ${err}`, column.id, githubPayload.pull_request.id);
|
robot.logger.error(`Couldn't create project card for the PR: ${err}`, column.id, githubPayload.pull_request.id);
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ async function assignIssueToBountyAwaitingForApproval(gitHubContext, githubPaylo
|
|||||||
robot.logger.debug(`Created card: ${ghcard.data.url}`, ghcard.data.id);
|
robot.logger.debug(`Created card: ${ghcard.data.url}`, ghcard.data.id);
|
||||||
|
|
||||||
// Send message to Slack
|
// Send message to Slack
|
||||||
robot.messageRoom(githubConfig.slack.notification.room, `Assigned issue to ``${approvalColumnName}`` in ``${projectBoardName}`` project\n${githubPayload.issue.html_url}`);
|
robot.messageRoom(githubConfig.slack.notification.room, `Assigned issue to ${approvalColumnName} in ${projectBoardName} project\n${githubPayload.issue.html_url}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
robot.logger.error(`Couldn't create project card for the issue: ${err}`, column.id, githubPayload.issue.id);
|
robot.logger.error(`Couldn't create project card for the issue: ${err}`, column.id, githubPayload.issue.id);
|
||||||
}
|
}
|
||||||
|
@ -6,18 +6,33 @@
|
|||||||
// Author:
|
// Author:
|
||||||
// PombeirP
|
// PombeirP
|
||||||
|
|
||||||
|
let initializing = false;
|
||||||
|
|
||||||
module.exports = function(robot) {
|
module.exports = function(robot) {
|
||||||
|
|
||||||
robot.brain.on('loaded', function() {
|
robot.brain.on('loaded', async function() {
|
||||||
const gitHubContext = require('./github-context.js')();
|
if (initializing) {
|
||||||
|
return;
|
||||||
appID = robot.brain.get("github-app_id");
|
}
|
||||||
if (appID) {
|
|
||||||
gitHubContext.initialize(robot, appID);
|
|
||||||
|
|
||||||
robot.logger.debug("Bot ready");
|
initializing = true;
|
||||||
} else {
|
|
||||||
robot.logger.debug("Bot waiting to be installed");
|
try {
|
||||||
|
const gitHubContext = require('./github-context.js')();
|
||||||
|
|
||||||
|
appID = robot.brain.get("github-app_id");
|
||||||
|
installationID = robot.brain.get("github-installation_id");
|
||||||
|
if (installationID) {
|
||||||
|
await gitHubContext.initialize(robot, appID, installationID);
|
||||||
|
|
||||||
|
robot.logger.debug("Bot ready");
|
||||||
|
} else {
|
||||||
|
robot.logger.debug("Bot waiting to be installed");
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
robot.logger.error(err);
|
||||||
|
} finally {
|
||||||
|
initializing = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ const GitHubApi = require('github');
|
|||||||
|
|
||||||
let cachedRobotNameRegex;
|
let cachedRobotNameRegex;
|
||||||
let initialized = false;
|
let initialized = false;
|
||||||
|
let initializing = false;
|
||||||
const githubAPI = new GitHubApi({
|
const githubAPI = new GitHubApi({
|
||||||
timeout: 15000,
|
timeout: 15000,
|
||||||
requestMedia: 'application/vnd.github.v3+json'
|
requestMedia: 'application/vnd.github.v3+json'
|
||||||
@ -23,44 +24,23 @@ module.exports = function() {
|
|||||||
|
|
||||||
config() { return githubConfig; },
|
config() { return githubConfig; },
|
||||||
|
|
||||||
initialize(robot, integrationID) {
|
async initialize(robot, integrationID, installationID) {
|
||||||
if (initialized) { return; }
|
if (initialized || initializing) { return; }
|
||||||
|
initializing = true;
|
||||||
|
|
||||||
githubConfig = loadConfig(robot, './github.yaml')
|
try {
|
||||||
|
if (githubConfig == null) {
|
||||||
|
githubConfig = loadConfig(robot, './github.yaml')
|
||||||
|
}
|
||||||
|
|
||||||
|
await ensureValidToken(robot, integrationID, installationID);
|
||||||
|
|
||||||
const token = robot.brain.get('github-token');
|
|
||||||
if (token) {
|
|
||||||
initialized = true;
|
initialized = true;
|
||||||
process.env.HUBOT_GITHUB_TOKEN = token;
|
} catch (err) {
|
||||||
robot.logger.debug("Reused cached GitHub token");
|
// Do nothing
|
||||||
githubAPI.authenticate({ type: 'token', token });
|
} finally {
|
||||||
return;
|
initializing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const jwtLib = require('jwt-simple');
|
|
||||||
|
|
||||||
// Private key contents
|
|
||||||
let privateKey = process.env.GITHUB_PEM;
|
|
||||||
|
|
||||||
const now = Math.round(Date.now() / 1000);
|
|
||||||
// Generate the JWT
|
|
||||||
const payload = {
|
|
||||||
// issued at time
|
|
||||||
iat: now,
|
|
||||||
// JWT expiration time (10 minute maximum)
|
|
||||||
exp: now + (1 * 60),
|
|
||||||
// GitHub App's identifier
|
|
||||||
iss: integrationID
|
|
||||||
};
|
|
||||||
|
|
||||||
jwt = jwtLib.encode(payload, privateKey, 'RS256');
|
|
||||||
githubAPI.authenticate({
|
|
||||||
type: 'integration',
|
|
||||||
token: jwt
|
|
||||||
});
|
|
||||||
robot.logger.debug("Configured integration authentication with JWT", jwt);
|
|
||||||
|
|
||||||
initialized = true;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
equalsRobotName(robot, str) {
|
equalsRobotName(robot, str) {
|
||||||
@ -68,6 +48,41 @@ module.exports = function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function ensureValidToken(robot, integrationID, installationID) {
|
||||||
|
const token = getToken(robot);
|
||||||
|
if (token) {
|
||||||
|
process.env.HUBOT_GITHUB_TOKEN = token;
|
||||||
|
robot.logger.debug("Reused cached GitHub token");
|
||||||
|
githubAPI.authenticate({ type: 'token', token });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const jwtLib = require('jwt-simple');
|
||||||
|
|
||||||
|
// Private key contents
|
||||||
|
let privateKey = process.env.GITHUB_PEM;
|
||||||
|
|
||||||
|
const now = Math.round(Date.now() / 1000);
|
||||||
|
// Generate the JWT
|
||||||
|
const payload = {
|
||||||
|
// issued at time
|
||||||
|
iat: now,
|
||||||
|
// JWT expiration time (10 minute maximum)
|
||||||
|
exp: now + (1 * 60),
|
||||||
|
// GitHub App's identifier
|
||||||
|
iss: integrationID
|
||||||
|
};
|
||||||
|
|
||||||
|
jwt = jwtLib.encode(payload, privateKey, 'RS256');
|
||||||
|
githubAPI.authenticate({
|
||||||
|
type: 'integration',
|
||||||
|
token: jwt
|
||||||
|
});
|
||||||
|
robot.logger.debug("Configured integration authentication with JWT", jwt);
|
||||||
|
|
||||||
|
await createAccessToken(robot, githubAPI, installationID);
|
||||||
|
}
|
||||||
|
|
||||||
function getRegexForRobotName(robot) {
|
function getRegexForRobotName(robot) {
|
||||||
// This comes straight out of Hubot's Robot.coffee
|
// This comes straight out of Hubot's Robot.coffee
|
||||||
// - they didn't get a nice way of extracting that method though
|
// - they didn't get a nice way of extracting that method though
|
||||||
@ -99,4 +114,60 @@ function loadConfig(robot, fileName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getToken(robot) {
|
||||||
|
const expiresAt = robot.brain.get('github-token-expires-at');
|
||||||
|
token = robot.brain.get('github-token');
|
||||||
|
if (expiresAt) {
|
||||||
|
expiresAt = Date.parse(expiresAt);
|
||||||
|
if (Date.now() >= expiresAt - 60 * 1000) {
|
||||||
|
robot.logger.debug("Cached GitHub token has expired");
|
||||||
|
token = null; // Token has expired
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If no expiration is set, assume this is an old deployment and invalidate the token
|
||||||
|
token = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
function expireGitHubToken(robot) {
|
||||||
|
const gitHubContext = require('./github-context.js')();
|
||||||
|
|
||||||
|
robot.brain.set('github-token', null);
|
||||||
|
process.env.HUBOT_GITHUB_TOKEN = null;
|
||||||
|
|
||||||
|
appID = robot.brain.get("github-app_id");
|
||||||
|
installationID = robot.brain.get("github-installation_id");
|
||||||
|
if (installationID) {
|
||||||
|
gitHubContext.ensureValidToken(robot, appID, installationID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createAccessToken(robot, github, id) {
|
||||||
|
try {
|
||||||
|
robot.logger.debug("Creating GitHub access token for installation");
|
||||||
|
|
||||||
|
response = await github.apps.createInstallationToken({ installation_id: id });
|
||||||
|
|
||||||
|
process.env.HUBOT_GITHUB_TOKEN = response.data.token;
|
||||||
|
robot.brain.set('github-token', response.data.token);
|
||||||
|
robot.brain.set('github-token-expires-at', response.data.expires_at);
|
||||||
|
|
||||||
|
expiresAt = Date.parse(response.data.expires_at);
|
||||||
|
setTimeout(expireGitHubToken, (expiresAt - 60 * 1000) - Date.now(), robot);
|
||||||
|
|
||||||
|
github.authenticate({
|
||||||
|
type: 'token',
|
||||||
|
token: response.data.token
|
||||||
|
});
|
||||||
|
|
||||||
|
robot.logger.debug(`Created GitHub access token for installation, expires at ${response.data.expires_at}`);
|
||||||
|
} catch (err) {
|
||||||
|
robot.logger.error(`Couldn't create installation token: ${err}`, id);
|
||||||
|
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -28,10 +28,10 @@ module.exports = function(robot) {
|
|||||||
// App was installed on an organization
|
// App was installed on an organization
|
||||||
robot.logger.info(`Initializing installation for app with ID ${githubPayload.installation.app_id} and installation ID ${githubPayload.installation.id}`);
|
robot.logger.info(`Initializing installation for app with ID ${githubPayload.installation.app_id} and installation ID ${githubPayload.installation.id}`);
|
||||||
|
|
||||||
robot.brain.set('github-app_install-payload', JSON.stringify(githubPayload));
|
|
||||||
robot.brain.set('github-app_id', githubPayload.installation.app_id);
|
robot.brain.set('github-app_id', githubPayload.installation.app_id);
|
||||||
|
robot.brain.set('github-installation_id', githubPayload.installation.id);
|
||||||
|
|
||||||
gitHubContext.initialize(robot, githubPayload.installation.app_id);
|
await gitHubContext.initialize(robot, githubPayload.installation.app_id, githubPayload.installation.id);
|
||||||
|
|
||||||
var perms = githubPayload.installation.permissions;
|
var perms = githubPayload.installation.permissions;
|
||||||
if (perms.repository_projects !== 'write') { robot.logger.error(formatPermMessage('repository_projects', 'write')); }
|
if (perms.repository_projects !== 'write') { robot.logger.error(formatPermMessage('repository_projects', 'write')); }
|
||||||
@ -43,14 +43,14 @@ module.exports = function(robot) {
|
|||||||
robot.logger.error("Please enable 'pull_request' events in the app configuration on github.com");
|
robot.logger.error("Please enable 'pull_request' events in the app configuration on github.com");
|
||||||
}
|
}
|
||||||
|
|
||||||
await createAccessToken(robot, gitHubContext.api(), githubPayload.installation.id);
|
|
||||||
break;
|
break;
|
||||||
case "deleted":
|
case "deleted":
|
||||||
// App was uninstalled from an organization
|
// App was uninstalled from an organization
|
||||||
robot.logger.info(`Removing installation for app with ID ${githubPayload.installation.app_id} and installation ID ${githubPayload.installation.id}`);
|
robot.logger.info(`Removing installation for app with ID ${githubPayload.installation.app_id} and installation ID ${githubPayload.installation.id}`);
|
||||||
|
|
||||||
robot.brain.set('github-app_id', null);
|
robot.brain.set('github-app_id', null);
|
||||||
robot.brain.set('github-app_install-payload', null);
|
robot.brain.set('github-installation_id', null);
|
||||||
|
robot.brain.set('github-token-expires-at', null);
|
||||||
robot.brain.set('github-token', null);
|
robot.brain.set('github-token', null);
|
||||||
process.env.HUBOT_GITHUB_TOKEN = null;
|
process.env.HUBOT_GITHUB_TOKEN = null;
|
||||||
break;
|
break;
|
||||||
@ -60,20 +60,4 @@ module.exports = function(robot) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
async function createAccessToken(robot, github, id) {
|
var formatPermMessage = (permName, perm) => `Please enable '${permName}' ${perm} permission in the app configuration on github.com`;
|
||||||
try {
|
|
||||||
response = await github.apps.createInstallationToken({ installation_id: id });
|
|
||||||
|
|
||||||
robot.brain.set('github-token', response.data.token);
|
|
||||||
// TODO: Set Redis expiration date to value from response.data.expires_at
|
|
||||||
process.env.HUBOT_GITHUB_TOKEN = response.data.token;
|
|
||||||
github.authenticate({
|
|
||||||
type: 'token',
|
|
||||||
token: response.data.token
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
robot.logger.error(`Couldn't create installation token: ${err}`, id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var formatPermMessage = (permName, perm) => `Please enable '${permName}' ${perm} permission in the app configuration on github.com`;
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user