Skip to content

Commit

Permalink
Feat/login flow (#477)
Browse files Browse the repository at this point in the history
* Feat: add SessionData class

* Refactor: swap out auth middleware to use sessionData

* Refactor: modify sessionData in rollbackRouteHandler

* Feat: update middleware and auth services

* Chore: swap out v1 routes

Using a 1-1 swap here for v1 routes, since these are mostly outdated or due for refactor

* Refactor: swap id used in logger

* Feat: add handler to attach site name to sessionData

* Chore: modify v2 routes and services to use sessionData

* Chore: swap whoamiAuth to verifyJwt and useSiteAccessTokenIfAvailable with checkHasAccess

* Test: add fixtures for new middleware

* Fix: tests

* Fix: allow e2e test user

* Chore: update v1 endpoint

* Fix: migrate auth middle to ts

* Chore: rename to usersessiondata

* Refactor: split sessionData into separate classes

* Chore: replace sessionData

* Chore: replace githubsessiondata

* Chore: add jsdoc for classes

* Chore: update routes and services to pass appropriate sessionData objects

* Fix: tests

* Fix: specify request types

* Chore: remove unnecessary comment

* Fix: simplify getGithubParamsWithSite

* Feat/site member verification for email (#479)

* Feat: add IsomerAdmins database table and migrations

* Feat: add access token via interceptor if missing

* Feat: add isomerAdminsService

* Feat: add hasAccessToSite to usersService

* Feat: shift site membership check to authorizationMiddlewareService

* Chore: replace authMiddleware.checkHasAccess with authorizationMiddleware.checkIsSiteMember

* Chore: migrate authmiddlewareservice to typescript

* Fix: rename auth middleware to authentication middleware

* Fix: move e2e_isomer_id into constants

* Chore: add cookie types

* Fix: more concise check for isSiteMember

* FIx: rebase errors

* Fix: remove unused identityAuthService dependency

* Fix: rename AuthService import as identityAuthService

* Nit: separate type definition

* Feat/email login flow (#480)

* build(deps): bump file-type from 16.5.3 to 16.5.4 (#475)

Bumps [file-type](https://github.com/sindresorhus/file-type) from 16.5.3 to 16.5.4.
- [Release notes](https://github.com/sindresorhus/file-type/releases)
- [Commits](sindresorhus/file-type@v16.5.3...v16.5.4)

---
updated-dependencies:
- dependency-name: file-type
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix: package.json & package-lock.json to reduce vulnerabilities (#476)

The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-SEQUELIZE-2959225

* build(deps): bump vm2 from 3.9.5 to 3.9.7 (#350)

Bumps [vm2](https://github.com/patriksimek/vm2) from 3.9.5 to 3.9.7.
- [Release notes](https://github.com/patriksimek/vm2/releases)
- [Changelog](https://github.com/patriksimek/vm2/blob/master/CHANGELOG.md)
- [Commits](patriksimek/vm2@3.9.5...3.9.7)

---
updated-dependencies:
- dependency-name: vm2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Chore: remove site links from description (#482)

* Fix: update resource room (#481)

* 0.10.0

* fix: remove unnecessary update step (#487)

* 0.10.1

* Chore: update commit message to include user id

* Feat: add login and verify endpoints

* Fix: model relations and alias

* Feat: add findSitesByUserId

* Feat: add site retrieval for email and admin users

* Fix: hasAccessToSite

* Fix: update email/mobile by isomer id

* Chore: update error message

* Fix: await check for whitelist

* Chore: add mockSessionData for email login

* Fix: SiteService behaviour for email users with no whitelisted sites

* Test: update sitesservice tests

* Test: add new authservice tests and fix existing tests

* Fix: update user model to allow null in github field

* Fix: update test fixture

* Fix: update user test suite

* Chore: remove unused endpoint

* Fix: rebase errors

* Chore: remove unnecessary message in test

* Chore: remove unnecessary userId field

* Nit: rename variable

* Refactor: shift site retrieval for email users into helper method

* Chore: spacing and remove unused var

* Fix: tests

* Tests: add new authorizationMiddlewareService test

* fix: remove resources_name and add support for url (#490)

* fix: remove resources_name and add support for url

* fix: display url parameter as domain but store with https scheme

* fix: resolve failing tests

* Chore: flip conditional

* Refactor: shift order of getSites to make it easier to understand

* Test: add new auth router tests

* Feat: add integration tests for getSites

* Fix: failing requests for getLastUpdated and getStagingUrl

* Nit: add comment

* Nit: test name and var name

* chore(mocks/axios): remove extra stuff

* test(sites.spec): refactor specs for clarity

* Fix: update settings

* Nit: update comment

* Fix: tests

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Snyk bot <[email protected]>
Co-authored-by: Hsu Zhong Jun <[email protected]>
Co-authored-by: seaerchin <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Snyk bot <[email protected]>
Co-authored-by: Hsu Zhong Jun <[email protected]>
Co-authored-by: seaerchin <[email protected]>

* Fix: e2e bypass of authorization middleware

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Snyk bot <[email protected]>
Co-authored-by: Hsu Zhong Jun <[email protected]>
Co-authored-by: seaerchin <[email protected]>
  • Loading branch information
5 people committed Mar 8, 2023
1 parent 915c4fb commit ec9d525
Show file tree
Hide file tree
Showing 121 changed files with 2,969 additions and 1,468 deletions.
3 changes: 0 additions & 3 deletions src/__mocks__/axios.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import mockAxios from "jest-mock-axios"

mockAxios.interceptors.request.use(jest.fn())
mockAxios.interceptors.response.use(jest.fn())

export default mockAxios
24 changes: 24 additions & 0 deletions src/classes/GithubSessionData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export interface GithubSessionDataProps {
currentCommitSha: string
treeSha: string
}

class GithubSessionData {
private currentCommitSha: GithubSessionDataProps["currentCommitSha"]

private treeSha: GithubSessionDataProps["treeSha"]

constructor({ currentCommitSha, treeSha }: GithubSessionDataProps) {
this.currentCommitSha = currentCommitSha
this.treeSha = treeSha
}

getGithubState() {
return {
currentCommitSha: this.currentCommitSha,
treeSha: this.treeSha,
}
}
}

export default GithubSessionData
54 changes: 54 additions & 0 deletions src/classes/UserSessionData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
export interface IsomerUserProps {
isomerUserId: string
email: string
}
export type GithubUserProps = IsomerUserProps & {
githubId: string
accessToken: string
}

export type SessionDataProps = IsomerUserProps | GithubUserProps

/**
* Object containing user information retrieved from the isomercms cookie.
* Not to be used as a general context object.
*/
class UserSessionData {
readonly githubId?: GithubUserProps["githubId"]

readonly accessToken?: GithubUserProps["accessToken"]

readonly isomerUserId: SessionDataProps["isomerUserId"]

readonly email: SessionDataProps["email"]

private isGithubProps(
sessionDataProps: SessionDataProps
): sessionDataProps is GithubUserProps {
return (sessionDataProps as GithubUserProps).githubId !== undefined
}

constructor(props: SessionDataProps) {
if (this.isGithubProps(props)) {
this.githubId = props.githubId
this.accessToken = props.accessToken
}
this.isomerUserId = props.isomerUserId
this.email = props.email
}

isEmailUser() {
return !this.githubId
}

getGithubParams() {
return {
githubId: this.githubId,
accessToken: this.accessToken,
isomerUserId: this.isomerUserId,
email: this.email,
}
}
}

export default UserSessionData
27 changes: 27 additions & 0 deletions src/classes/UserWithSiteSessionData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import UserSessionData, { SessionDataProps } from "./UserSessionData"

export type UserWithSiteSessionDataProps = SessionDataProps & {
siteName: string
}

/**
* Object containing user information retrieved from the isomercms cookie, and the site being accessed.
* Not to be used as a general context object.
*/
class UserWithSiteSessionData extends UserSessionData {
readonly siteName: string

constructor(props: UserWithSiteSessionDataProps) {
super(props)
this.siteName = props.siteName
}

getGithubParamsWithSite() {
return {
...super.getGithubParams(),
siteName: this.siteName,
}
}
}

export default UserWithSiteSessionData
3 changes: 3 additions & 0 deletions src/classes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./UserSessionData"
export * from "./UserWithSiteSessionData"
export * from "./GithubSessionData"
3 changes: 3 additions & 0 deletions src/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ export enum RedirectionTypes {
CNAME = "CNAME",
A = "A",
}
export const E2E_ISOMER_ID = "-1"
export const E2E_TEST_EMAIL = "test@e2e"
export const E2E_TEST_CONTACT = "12345678"
36 changes: 36 additions & 0 deletions src/database/migrations/20220726094614-create-isomer-admin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable("isomer_admins", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.BIGINT,
},
user_id: {
allowNull: false,
type: Sequelize.BIGINT,
references: {
model: "users",
key: "id",
},
onUpdate: "CASCADE",
onDelete: "CASCADE",
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.fn("NOW"),
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.fn("NOW"),
},
})
},

down: async (queryInterface) => {
await queryInterface.dropTable("isomer_admins")
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.changeColumn("users", "github_id", {
allowNull: true,
unique: true,
type: Sequelize.TEXT,
validate: {
notEmpty: true,
},
})
},

async down(queryInterface, Sequelize) {
await queryInterface.changeColumn("users", "github_id", {
allowNull: false,
unique: true,
type: Sequelize.TEXT,
validate: {
notEmpty: true,
},
})
},
}
32 changes: 32 additions & 0 deletions src/database/models/IsomerAdmin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {
Column,
CreatedAt,
DataType,
ForeignKey,
Model,
Table,
UpdatedAt,
} from "sequelize-typescript"

import { User } from "@database/models/User"

@Table({ tableName: "isomer_admins" })
export class IsomerAdmin extends Model {
@Column({
autoIncrement: true,
primaryKey: true,
allowNull: false,
type: DataType.BIGINT,
})
id!: number

@ForeignKey(() => User)
@Column
userId!: number

@CreatedAt
createdAt!: Date

@UpdatedAt
updatedAt!: Date
}
5 changes: 4 additions & 1 deletion src/database/models/Site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export class Site extends Model {
onUpdate: "CASCADE",
onDelete: "CASCADE",
through: () => SiteMember,
as: "site_members",
})
users!: User[]

Expand All @@ -81,7 +82,9 @@ export class Site extends Model {
@ForeignKey(() => User)
creatorId!: number

@BelongsTo(() => User)
@BelongsTo(() => User, {
as: "site_creator",
})
creator!: User

@HasOne(() => Launch)
Expand Down
9 changes: 6 additions & 3 deletions src/database/models/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class User extends Model {
email?: string | null

@Column({
allowNull: false,
allowNull: true,
unique: true,
type: DataType.TEXT,
validate: {
Expand Down Expand Up @@ -62,14 +62,17 @@ export class User extends Model {
@DeletedAt
deletedAt?: Date

@BelongsToMany(() => User, {
@BelongsToMany(() => Site, {
onUpdate: "CASCADE",
onDelete: "CASCADE",
through: () => SiteMember,
as: "site_members",
})
sites!: Site[]

@HasMany(() => Site)
@HasMany(() => Site, {
as: "sites_created",
})
sitesCreated?: Site[]

@HasMany(() => Launch)
Expand Down
1 change: 1 addition & 0 deletions src/database/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from "@database/models/Repo"
export * from "@database/models/Deployment"
export * from "@database/models/Launch"
export * from "@database/models/Redirection"
export * from "@database/models/IsomerAdmin"
14 changes: 14 additions & 0 deletions src/fixtures/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,25 @@ const express = require("express")

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

const {
mockUserSessionData,
mockUserWithSiteSessionData,
mockGithubSessionData,
} = require("./sessionData")

const attachSessionData = (req, res, next) => {
res.locals.userSessionData = mockUserSessionData
res.locals.userWithSiteSessionData = mockUserWithSiteSessionData
res.locals.githubSessionData = mockGithubSessionData
next()
}

function generateRouter(router) {
const app = express()
app.use(express.json({ limit: "7mb" }))
app.use(express.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(attachSessionData)
app.use(router)
app.use(errorHandler)
return app
Expand Down
45 changes: 45 additions & 0 deletions src/fixtures/sessionData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import GithubSessionData from "@root/classes/GithubSessionData"
import UserSessionData from "@root/classes/UserSessionData"
import UserWithSiteSessionData from "@root/classes/UserWithSiteSessionData"

export const mockAccessToken = "mockAccessToken"
export const mockGithubId = "mockGithubId"
export const mockIsomerUserId = "1"
export const mockEmail = "mockEmail"
export const mockTreeSha = "mockTreeSha"
export const mockCurrentCommitSha = "mockCurrentCommitSha"
export const mockSiteName = "mockSiteName"

export const mockGithubState = {
treeSha: mockTreeSha,
currentCommitSha: mockCurrentCommitSha,
}

export const mockUserSessionData = new UserSessionData({
githubId: mockGithubId,
accessToken: mockAccessToken,
isomerUserId: mockIsomerUserId,
email: mockEmail,
})

export const mockUserWithSiteSessionData = new UserWithSiteSessionData({
githubId: mockGithubId,
accessToken: mockAccessToken,
isomerUserId: mockIsomerUserId,
email: mockEmail,
siteName: mockSiteName,
})

export const mockGithubSessionData = new GithubSessionData({
treeSha: mockTreeSha,
currentCommitSha: mockCurrentCommitSha,
})
export const mockSessionDataEmailUser = new UserSessionData({
isomerUserId: mockIsomerUserId,
email: mockEmail,
})
export const mockSessionDataEmailUserWithSite = new UserWithSiteSessionData({
isomerUserId: mockIsomerUserId,
email: mockEmail,
siteName: mockSiteName,
})
Loading

0 comments on commit ec9d525

Please sign in to comment.