-
Notifications
You must be signed in to change notification settings - Fork 193
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/ARC-5ku-UI-changes' into ARC-5ku…
…-UI-changes
- Loading branch information
Showing
4 changed files
with
146 additions
and
7 deletions.
There are no files selected for viewing
105 changes: 105 additions & 0 deletions
105
src/rest/middleware/jira-admin/jira-admin-check.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import { encodeSymmetric } from "atlassian-jwt"; | ||
import { Application } from "express"; | ||
import supertest from "supertest"; | ||
import { Installation } from "models/installation"; | ||
import { booleanFlag, BooleanFlags } from "config/feature-flags"; | ||
import { when } from "jest-when"; | ||
import { JiraClient } from "models/jira-client"; | ||
import { getFrontendApp } from "~/src/app"; | ||
|
||
jest.mock("config/feature-flags"); | ||
jest.mock("models/jira-client"); | ||
|
||
const testSharedSecret = "test-secret"; | ||
|
||
describe("Jira Admin Check", () => { | ||
|
||
let app: Application; | ||
let installation: Installation; | ||
|
||
const USER_ACC_ID = "12345"; | ||
|
||
beforeEach(async () => { | ||
|
||
when(booleanFlag).calledWith(BooleanFlags.JIRA_ADMIN_CHECK, jiraHost).mockResolvedValue(true); | ||
|
||
app = getFrontendApp(); | ||
|
||
installation = await Installation.install({ | ||
clientKey: "jira-client-key", | ||
host: jiraHost, | ||
sharedSecret: testSharedSecret | ||
}); | ||
|
||
}); | ||
|
||
const mockPermission = (permissions: string[]) => { | ||
when(JiraClient.getNewClient).calledWith(expect.anything(), expect.anything()) | ||
.mockImplementation((reqInst: Installation) => { | ||
if (reqInst.id === installation.id) { | ||
return { | ||
checkAdminPermissions: jest.fn((userAccountId) => { | ||
if (userAccountId === USER_ACC_ID) { | ||
return { data: { globalPermissions: permissions } }; | ||
} else { | ||
return { data: { globalPermissions: ["ADMINISTER", "OTHER_ROLE"] } }; | ||
} | ||
}) | ||
} as any; | ||
} else { | ||
throw new Error("Wrong installation " + reqInst); | ||
} | ||
}); | ||
|
||
}; | ||
|
||
it("should fail if is not admin", async () => { | ||
|
||
mockPermission([ "OTHER_ROLE" ]); | ||
|
||
const res = await sendRequestWithToken(); | ||
|
||
expect(res.status).toEqual(401); | ||
expect(JSON.parse(res.text)).toEqual(expect.objectContaining({ | ||
errorCode: "INSUFFICIENT_PERMISSION", | ||
message: expect.stringContaining("Forbidden") | ||
})); | ||
|
||
}); | ||
|
||
it("should pass request if is admin", async () => { | ||
|
||
mockPermission([ "ADMINISTER", "OTHER_ROLE" ]); | ||
|
||
const res = await sendRequestWithToken(); | ||
|
||
expect(res.status).toEqual(200); | ||
expect(JSON.parse(res.text)).toEqual({ | ||
redirectUrl: expect.stringContaining("oauth/authorize"), | ||
state: expect.anything() | ||
}); | ||
|
||
}); | ||
|
||
const sendRequestWithToken = async () => { | ||
return await supertest(app) | ||
.get(`/rest/app/cloud/oauth/redirectUrl`) | ||
.set("Authorization", getToken()) | ||
.send(); | ||
}; | ||
|
||
const getToken = ({ | ||
secret = testSharedSecret, | ||
iss = "jira-client-key", | ||
sub = USER_ACC_ID, | ||
exp = Date.now() / 1000 + 10000, | ||
qsh = "context-qsh" } = {}): any => { | ||
return encodeSymmetric({ | ||
qsh, | ||
iss, | ||
sub, | ||
exp | ||
}, secret); | ||
}; | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { NextFunction, Request, Response } from "express"; | ||
import { JiraClient } from "models/jira-client"; | ||
import { booleanFlag, BooleanFlags } from "config/feature-flags"; | ||
import { InsufficientPermissionError, InvalidTokenError } from "config/errors"; | ||
import { errorWrapper } from "../../helper"; | ||
|
||
const ADMIN_PERMISSION = "ADMINISTER"; | ||
export const JiraAdminEnforceMiddleware = errorWrapper("jiraAdminEnforceMiddleware", async (req: Request, res: Response, next: NextFunction): Promise<void | Response> => { | ||
|
||
const { accountId, installation, jiraHost } = res.locals; | ||
|
||
if (!(await booleanFlag(BooleanFlags.JIRA_ADMIN_CHECK, jiraHost))) { | ||
return next(); | ||
} | ||
|
||
if (!accountId) { | ||
throw new InvalidTokenError("Missing userAccountId"); | ||
} | ||
|
||
const jiraClient = await JiraClient.getNewClient(installation, req.log); | ||
|
||
const permissions = await jiraClient.checkAdminPermissions(accountId); | ||
|
||
const isAdmin = permissions.data.globalPermissions.includes(ADMIN_PERMISSION); | ||
if (!isAdmin) { | ||
throw new InsufficientPermissionError("Forbidden - User does not have Jira administer permissions."); | ||
} | ||
|
||
req.log.debug({ isAdmin }, "Admin permissions checked"); | ||
next(); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters