Skip to content

Commit

Permalink
Merge pull request #393 from isomerpages/release/0.2.0
Browse files Browse the repository at this point in the history
Release/0.2.0
  • Loading branch information
alexanderleegs authored Mar 17, 2022
2 parents 1f7b5a7 + 97d9b8f commit 523160c
Show file tree
Hide file tree
Showing 46 changed files with 1,253 additions and 40 deletions.
17 changes: 16 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,22 @@ All notable changes to this project will be documented in this file. Dates are d

Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).

#### 0.1.0
#### [v0.2.0](https://github.com/isomerpages/isomercms-backend/compare/v0.2.0...v0.2.0)

- fix: order of ping [`#392`](https://github.com/isomerpages/isomercms-backend/pull/392)

#### [v0.2.0](https://github.com/isomerpages/isomercms-backend/compare/v0.1.0...v0.2.0)

> 17 March 2022
- Fix: use originalUrl instead of url [`#388`](https://github.com/isomerpages/isomercms-backend/pull/388)
- Refactor/sites [`#341`](https://github.com/isomerpages/isomercms-backend/pull/341)
- Refactor/auth [`#328`](https://github.com/isomerpages/isomercms-backend/pull/328)
- 0.1.0 [`#373`](https://github.com/isomerpages/isomercms-backend/pull/373)

#### v0.1.0

> 3 March 2022
- Fix: prepend unique prefix for release script [`#370`](https://github.com/isomerpages/isomercms-backend/pull/370)
- Fix/retrieve last updated time [`#354`](https://github.com/isomerpages/isomercms-backend/pull/354)
Expand Down
66 changes: 66 additions & 0 deletions fixtures/repoInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const repoInfo = {
name: "repo",
private: false,
description:
"Staging: https://repo-staging.netlify.app | Production: https://repo-prod.netlify.app",
pushed_at: "2021-09-09T02:41:37Z",
permissions: {
admin: true,
maintain: true,
push: true,
triage: true,
pull: true,
},
}

const repoInfo2 = {
name: "repo2",
private: false,
description:
"Staging: https://repo2-staging.netlify.app | Production: https://repo2-prod.netlify.app",
pushed_at: "2021-09-09T02:41:37Z",
permissions: {
admin: true,
maintain: true,
push: true,
triage: true,
pull: true,
},
}

const adminRepo = {
name: "isomercms-backend",
private: false,
description:
"Staging: https://isomercms-backend-staging.netlify.app | Production: https://isomercms-backend-prod.netlify.app",
pushed_at: "2021-09-09T02:41:37Z",
permissions: {
admin: true,
maintain: true,
push: true,
triage: true,
pull: true,
},
}

const noAccessRepo = {
name: "noaccess",
private: false,
description:
"Staging: https://noaccess-staging.netlify.app | Production: https://noaccess-prod.netlify.app",
pushed_at: "2021-09-09T02:41:37Z",
permissions: {
admin: false,
maintain: false,
push: false,
triage: false,
pull: true,
},
}

module.exports = {
repoInfo,
repoInfo2,
adminRepo,
noAccessRepo,
}
32 changes: 32 additions & 0 deletions newmiddleware/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const autoBind = require("auto-bind")

class AuthMiddleware {
constructor({ authMiddlewareService }) {
this.authMiddlewareService = authMiddlewareService
// We need to bind all methods because we don't invoke them from the class directly
autoBind(this)
}

verifyJwt(req, res, next) {
const { cookies, originalUrl: url } = req
const { accessToken, userId } = this.authMiddlewareService.verifyJwt({
cookies,
url,
})
req.accessToken = accessToken
req.userId = userId
return next()
}

whoamiAuth(req, res, next) {
const { cookies, originalUrl: url } = req
const { accessToken, userId } = this.authMiddlewareService.whoamiAuth({
cookies,
url,
})
req.accessToken = accessToken
if (userId) req.userId = userId
return next()
}
}
module.exports = { AuthMiddleware }
12 changes: 12 additions & 0 deletions newmiddleware/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const {
AuthMiddlewareService,
} = require("@services/middlewareServices/AuthMiddlewareService")

const { AuthMiddleware } = require("./auth")

const authMiddlewareService = new AuthMiddlewareService()
const authMiddleware = new AuthMiddleware({ authMiddlewareService })

module.exports = {
authMiddleware,
}
120 changes: 120 additions & 0 deletions newroutes/__tests__/Auth.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
const cookieParser = require("cookie-parser")
const express = require("express")
const request = require("supertest")

const { errorHandler } = require("@middleware/errorHandler")
const { attachReadRouteHandlerWrapper } = require("@middleware/routeHandler")

const { CSRF_COOKIE_NAME, COOKIE_NAME, AuthRouter } = require("../auth")

const { FRONTEND_URL } = process.env
const csrfState = "csrfState"
const cookieToken = "cookieToken"
const accessToken = undefined

describe("Unlinked Pages Router", () => {
const mockAuthService = {
getAuthRedirectDetails: jest.fn(),
getGithubAuthToken: jest.fn(),
getUserId: jest.fn(),
}

const router = new AuthRouter({
authService: mockAuthService,
})

const app = express()
app.use(express.json({ limit: "7mb" }))
app.use(express.urlencoded({ extended: false }))
app.use(cookieParser())

// We can use read route handler here because we don't need to lock the repo
app.get(
"/github-redirect",
attachReadRouteHandlerWrapper(router.authRedirect)
)
app.get("/", attachReadRouteHandlerWrapper(router.githubAuth))
app.delete("/logout", attachReadRouteHandlerWrapper(router.logout))
app.get("/whoami", attachReadRouteHandlerWrapper(router.whoami))
app.use(errorHandler)

beforeEach(() => {
jest.clearAllMocks()
})

describe("authRedirect", () => {
const redirectUrl = "redirectUrl"
it("redirects to the specified github url", async () => {
mockAuthService.getAuthRedirectDetails.mockResolvedValueOnce({
redirectUrl,
cookieToken,
})

const resp = await request(app).get(`/github-redirect`)

expect(mockAuthService.getAuthRedirectDetails).toHaveBeenCalledTimes(1)
expect(resp.status).toEqual(302)
expect(resp.headers.location).toContain(redirectUrl)
expect(resp.headers["set-cookie"]).toEqual(
expect.arrayContaining([expect.stringContaining(CSRF_COOKIE_NAME)])
)
})
})

describe("githubAuth", () => {
const code = "code"
const state = "state"
const token = "token"
it("retrieves the token and redirects back to the correct page after github auth", async () => {
mockAuthService.getGithubAuthToken.mockResolvedValueOnce({
token,
})

const resp = await request(app)
.get(`/?code=${code}&state=${state}`)
.set("Cookie", `${CSRF_COOKIE_NAME}=${csrfState};`)

expect(mockAuthService.getGithubAuthToken).toHaveBeenCalledWith({
csrfState,
code,
state,
})
expect(resp.status).toEqual(302)
expect(resp.headers.location).toContain(`${FRONTEND_URL}/sites`)
expect(resp.headers["set-cookie"]).toEqual(
expect.arrayContaining([expect.stringContaining(COOKIE_NAME)])
)
})
})
describe("logout", () => {
it("removes cookies on logout", async () => {
const resp = await request(app)
.delete(`/logout`)
.set(
"Cookie",
`${CSRF_COOKIE_NAME}=${csrfState};${COOKIE_NAME}=${cookieToken}`
)
.expect(200)

expect(resp.headers["set-cookie"]).toEqual(
expect.arrayContaining([
expect.stringContaining(`${CSRF_COOKIE_NAME}=;`),
expect.stringContaining(`${COOKIE_NAME}=;`),
])
)
})
})

describe("whoami", () => {
const userId = "userId"
it("redirects to the specified github url", async () => {
mockAuthService.getUserId.mockResolvedValueOnce({
userId,
})

await request(app).get(`/whoami`).expect(200)

expect(mockAuthService.getUserId).toHaveBeenCalledWith({ accessToken })
})
})
})
113 changes: 113 additions & 0 deletions newroutes/__tests__/Sites.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
const cookieParser = require("cookie-parser")
const express = require("express")
const request = require("supertest")

const { NotFoundError } = require("@errors/NotFoundError")

const { errorHandler } = require("@middleware/errorHandler")
const { attachReadRouteHandlerWrapper } = require("@middleware/routeHandler")

const { SitesRouter } = require("../sites")

// Can't set request fields - will always be undefined
const userId = undefined
const accessToken = undefined

const siteName = "siteName"

const reqDetails = { siteName, accessToken }

describe("Sites Router", () => {
const mockSitesService = {
getSites: jest.fn(),
checkHasAccess: jest.fn(),
getLastUpdated: jest.fn(),
getStagingUrl: jest.fn(),
}

const router = new SitesRouter({
sitesService: mockSitesService,
})

const app = express()
app.use(express.json({ limit: "7mb" }))
app.use(express.urlencoded({ extended: false }))
app.use(cookieParser())

// We can use read route handler here because we don't need to lock the repo
app.get("/", attachReadRouteHandlerWrapper(router.getSites))
app.get("/:siteName", attachReadRouteHandlerWrapper(router.checkHasAccess))
app.get(
"/:siteName/lastUpdated",
attachReadRouteHandlerWrapper(router.getLastUpdated)
)
app.get(
"/:siteName/stagingUrl",
attachReadRouteHandlerWrapper(router.getStagingUrl)
)
app.use(errorHandler)

beforeEach(() => {
jest.clearAllMocks()
})

describe("getSites", () => {
it("returns the list of sites accessible to the user", async () => {
const sitesResp = ["site1", "site2"]
mockSitesService.getSites.mockResolvedValueOnce(sitesResp)

const resp = await request(app).get(`/`).expect(200)

expect(resp.body).toStrictEqual({ siteNames: sitesResp })
expect(mockSitesService.getSites).toHaveBeenCalledWith({ accessToken })
})
})

describe("checkHasAccess", () => {
it("rejects if user has no access to a site", async () => {
mockSitesService.checkHasAccess.mockRejectedValueOnce(
new NotFoundError("")
)

await request(app).get(`/${siteName}`).expect(404)

expect(mockSitesService.checkHasAccess).toHaveBeenCalledWith(reqDetails, {
userId,
})
})

it("allows if user has access to a site", async () => {
await request(app).get(`/${siteName}`).expect(200)

expect(mockSitesService.checkHasAccess).toHaveBeenCalledWith(reqDetails, {
userId,
})
})
})

describe("getLastUpdated", () => {
it("returns the last updated time", async () => {
const lastUpdated = "last-updated"
mockSitesService.getLastUpdated.mockResolvedValueOnce(lastUpdated)

const resp = await request(app)
.get(`/${siteName}/lastUpdated`)
.expect(200)

expect(resp.body).toStrictEqual({ lastUpdated })
expect(mockSitesService.getLastUpdated).toHaveBeenCalledWith(reqDetails)
})
})

describe("getStagingUrl", () => {
it("returns the last updated time", async () => {
const stagingUrl = "staging-url"
mockSitesService.getStagingUrl.mockResolvedValueOnce(stagingUrl)

const resp = await request(app).get(`/${siteName}/stagingUrl`).expect(200)

expect(resp.body).toStrictEqual({ stagingUrl })
expect(mockSitesService.getStagingUrl).toHaveBeenCalledWith(reqDetails)
})
})
})
Loading

0 comments on commit 523160c

Please sign in to comment.