Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add optional authorized team configuration #88

Merged
merged 1 commit into from
Dec 18, 2023
Merged
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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ You will need to decide the label that this app looks for, the contents of the i
- `ISSUE_BODY_FILE` This is the file containing the body of the issue created
- `ISSUE_ASSIGNEES` This is a comma separated list of the issue assignees

Optionally, you can define a team name in the `AUTHORIZED_TEAM` variable. The app will consider members of this team authorized to use this app. The app will add a comment on the PR if an user who is not a member of this team attemps to do any of the following:
- Opens a PR with the `TRIGGER_STRING` in the body
- Adds a PR comment with the `TRIGGER_STRING` in the body
- Adds the `EMERGENCY_LABEL` to a PR

The comment will read:
```
@username is not authorized to apply the emergency label.
```

To make the emergency label permanent set `EMERGENCY_LABEL_PERMANENT` to true. Doing this will cause the app to reapply the emergency label if it is removed.
To trigger the label (and therefore everything configured) set `TRIGGER_STRING` to the value you want the app to look for in PRs and PR comments.

Expand Down
112 changes: 105 additions & 7 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ const emergencyLabel = process.env.EMERGENCY_LABEL || 'emergency';
module.exports = (app) => {
//console.log("Yay! The app was loaded!");
app.on("pull_request.labeled", async (context) => {
if (context.payload.label.name == emergencyLabel && context.payload.pull_request.merged == false) {
let authorized = await isAuthorized(context.payload.sender.login, context.payload.organization.login, context.octokit)
if (context.payload.label.name == emergencyLabel
&& context.payload.pull_request.merged == false
&& authorized) {
// emergency label exists and pull request is not merged, so do stuff...
console.log(`${emergencyLabel} label detected`);

Expand Down Expand Up @@ -122,11 +125,25 @@ module.exports = (app) => {
} else {
return true;
}
}
} else if (context.payload.label.name == emergencyLabel
&& context.payload.pull_request.merged == false
&& ! authorized) {
await postUnauthorizedIssueComment(context)
// remove emergency label
await context.octokit.rest.issues.removeLabel({
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name,
issue_number: context.payload.pull_request.number,
name: emergencyLabel
})
}
});

app.on("pull_request.unlabeled", async (context) => {
if (context.payload.label.name == emergencyLabel && process.env.EMERGENCY_LABEL_PERMANENT == 'true') {
if (context.payload.label.name == emergencyLabel
&& process.env.EMERGENCY_LABEL_PERMANENT == 'true'
&& ! context.payload.sender.login.endsWith("[bot]")) {

// emergencyLabel was removed and it should be permanent, so do stuff...
console.log(`Reaplying ${emergencyLabel} label to PR: ${context.payload.pull_request.html_url}`);

Expand Down Expand Up @@ -156,7 +173,10 @@ module.exports = (app) => {
});

app.on("issues.unlabeled", async (context) => {
if (context.payload.label.name == emergencyLabel && process.env.EMERGENCY_LABEL_PERMANENT == 'true') {
if (context.payload.label.name == emergencyLabel
&& process.env.EMERGENCY_LABEL_PERMANENT == 'true'
&& ! context.payload.sender.login.endsWith("[bot]")) {

// emergencyLabel was removed and it should be permanent, so do stuff...
console.log(`Reaplying ${emergencyLabel} label to PR: ${context.payload.issue.html_url}`);

Expand Down Expand Up @@ -186,7 +206,10 @@ module.exports = (app) => {
});

app.on("pull_request.opened", async (context) => {
if (context.payload.pull_request.body.toLocaleLowerCase().includes(process.env.TRIGGER_STRING)) {
let authorized = await isAuthorized(context.payload.sender.login, context.payload.organization.login, context.octokit)
if (context.payload.pull_request.body.toLocaleLowerCase().includes(process.env.TRIGGER_STRING)
&& authorized) {

// Found the trigger string, so add the emergency label to trigger the other stuff...
let errorsArray = [];
await context.octokit.rest.issues.addLabels({
Expand All @@ -209,11 +232,18 @@ module.exports = (app) => {
} else {
return true;
}
} else if (context.payload.pull_request.body.toLocaleLowerCase().includes(process.env.TRIGGER_STRING)
&& ! authorized){
await postUnauthorizedIssueComment(context)
}
});

app.on("issue_comment.created", async (context) => {
if (context.payload.issue.pull_request && context.payload.comment.body.toLocaleLowerCase().includes(process.env.TRIGGER_STRING)) {
let authorized = await isAuthorized(context.payload.sender.login, context.payload.organization.login, context.octokit)
if (context.payload.issue.pull_request
&& context.payload.comment.body.toLocaleLowerCase().includes(process.env.TRIGGER_STRING)
&& authorized) {

// This is a comment on a PR and we found the trigger string, so add the emergency label to trigger the other stuff...
let errorsArray = [];
await context.octokit.rest.issues.addLabels({
Expand All @@ -236,6 +266,74 @@ module.exports = (app) => {
} else {
return true;
}
}
} else if (context.payload.issue.pull_request
&& context.payload.comment.body.toLocaleLowerCase().includes(process.env.TRIGGER_STRING)
&& ! authorized) {
await postUnauthorizedIssueComment(context)
}
});
};

async function isAuthorized(login, org, octokit) {
// check if process.env.AUTHORIZED_TEAM is defined
if (process.env.AUTHORIZED_TEAM == undefined || process.env.AUTHORIZED_TEAM == "") {
console.log("No authorized team specified. Skipping authorization check.")
return true;
}
// if login ends with [bot] then it's a bot and we don't need to check
if (login.endsWith("[bot]")) {
console.log("Bot detected. Skipping authorization check.")
return true;
}
console.log(`Checking if ${login} is a member of ${org}/${process.env.AUTHORIZED_TEAM} team`)
try {
let membership = await octokit.request(`GET /orgs/${org}/teams/${process.env.AUTHORIZED_TEAM}/memberships/${login}`, {
org: org,
team_slug: process.env.AUTHORIZED_TEAM,
username: login
})

if (membership.data.state == 'active') {
console.log( "Membership active")
return true;
} else {
console.log( "Membership not active")
return false;

}
} catch (error) {
if (error.status == 404) {
console.log("Membership not found")
return false;
} else {
console.log(`error: ${error}`);
console.log("Error checking membership. Check the ADMIN_OPS_ORG and ACTIONS_APPROVER_TEAM variables.")
throw new Error("Error checking membership");
}
}
}

async function postUnauthorizedIssueComment(context) {
// Comment on github issue that user is not authorized to apply the emergency label
let errorsArray = [];
let number = context.payload.issue ? context.payload.issue.number : context.payload.pull_request.number
let url = context.payload.issue ? context.payload.issue.html_url : context.payload.pull_request.html_url
await context.octokit.rest.issues.createComment({
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name,
issue_number: number,
body: `@${context.payload.sender.login} is not authorized to apply the emergency label.`
}).then(response => {
console.log(`Commented on issue: ${url}`);
}).catch(error => {
console.log(`Error commenting on issue: ${error} to PR: ${url}`);
errorsArray.push(error);
});

if (errorsArray.length > 0) {
console.log(`Errors: ${errorsArray}`);
throw errorsArray;
} else {
return true;
}
}
4 changes: 4 additions & 0 deletions template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ Parameters:
Description: The string value in a PR or Issue title that triggers appling the emegency label
Default: emergency landing
Type: String
authorizedTeam:
Description: The team who's members are authorized to use this app
Type: String

Resources:
webhooks:
Expand Down Expand Up @@ -108,6 +111,7 @@ Resources:
SLACK_MESSAGE_FILE: !Ref slackMessageFile
EMERGENCY_LABEL_PERMANENT: !Ref emergencyLabelPermanent
TRIGGER_STRING: !Ref triggerString
AUTHORIZED_TEAM: !Ref authorizedTeam

Outputs:
WebhooksUrl:
Expand Down
Loading
Loading