Skip to content

Commit

Permalink
Merge pull request #182 from chingu-x/dev
Browse files Browse the repository at this point in the history
Merge into main for alpha test
  • Loading branch information
cherylli authored Aug 14, 2024
2 parents c249642 + 96bd109 commit 1f663be
Show file tree
Hide file tree
Showing 24 changed files with 874 additions and 60 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ jobs:
JWT_SECRET: jwtsecret
AT_SECRET: atsecret
RT_SECRET: rtsecret
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
DISCORD_CLIENT_SECRET: ${{ secrets.DISCORD_CLIENT_SECRET }}
DISCORD_CALLBACK_URL: /api/v1/auth/discord/redirect

steps:
- name: Checkout code
Expand Down
8 changes: 3 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,13 @@ Another example [here](https://co-pilot.dev/changelog)
- Add voyages unit test, also had to update all files (seed, tests, services) to meet strict null rule due to prismaMock requirements ([#163](https://github.com/chingu-x/chingu-dashboard-be/pull/163))
- Add e2e tests for users controller ([#165](https://github.com/chingu-x/chingu-dashboard-be/pull/165))
- Add GET endpoint for check-in form responses ([#166](https://github.com/chingu-x/chingu-dashboard-be/pull/166))

- Add weekly sprint checkin forms for product owner and scrum master ([#167](https://github.com/chingu-x/chingu-dashboard-be/pull/167))
- Add e2e test for features controller ([#168](https://github.com/chingu-x/chingu-dashboard-be/pull/168))
- Add endpoint to reseed the database ([#170](https://github.com/chingu-x/chingu-dashboard-be/pull/170))



- Add new @unverified decorator to 4 routes, updated permission guard ([#171](https://github.com/chingu-x/chingu-dashboard-be/pull/171))

- Add CASL permissions for Tech endpoint ([#174](https://github.com/chingu-x/chingu-dashboard-be/pull/174))
- Add CASL permissions for Team Resource endpoint ([#177](https://github.com/chingu-x/chingu-dashboard-be/pull/177))
- Add units tests for the users controller & services([#179](https://github.com/chingu-x/chingu-dashboard-be/pull/178))

### Changed

Expand Down Expand Up @@ -82,6 +78,7 @@ Another example [here](https://co-pilot.dev/changelog)
- Updated response for GET teams/:teamId/techs to include isSelected value for techs [#173](https://github.com/chingu-x/chingu-dashboard-be/pull/173)
- Refactor ideation endpoints to remove redundant teamId params [#175](https://github.com/chingu-x/chingu-dashboard-be/pull/175)
- Squashed migration files into one [#176](https://github.com/chingu-x/chingu-dashboard-be/pull/176)
- Update prisma schema to include oauth [#181](https://github.com/chingu-x/chingu-dashboard-be/pull/181)


### Fixed
Expand All @@ -92,6 +89,7 @@ Another example [here](https://co-pilot.dev/changelog)
- Fix bug with reading roles after reseeding causes the db to not recognize the tokens stored by the user's browser ([#134](https://github.com/chingu-x/chingu-dashboard-be/pull/134))
- Fix form responses giving error and not inserting values when the boolean value is false ([#156](https://github.com/chingu-x/chingu-dashboard-be/pull/156))
- Fix a bug for check on voyageTeamMemberId ([#159](https://github.com/chingu-x/chingu-dashboard-be/pull/159))
- Fix users unit test failing due to a schema change

### Removed

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ NODE_ENV=development

FRONTEND_URL=https://chingu-dashboard-git-dev-chingu-dashboard.vercel.app/

DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=
DISCORD_CALLBACK_URL=http://localhost:8000/api/v1/auth/discord/redirect

# .env.test
DATABASE_URL={your test database connection string}
NODE_ENV=test
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"lint-staged": "^15.1.0",
"node-mailjet": "^6.0.4",
"passport": "^0.6.0",
"passport-discord": "^0.1.4",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"reflect-metadata": "^0.1.13",
Expand All @@ -76,6 +77,7 @@
"@types/express": "^4.17.17",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/passport-discord": "^0.1.13",
"@types/passport-jwt": "^3.0.11",
"@types/passport-local": "^1.0.37",
"@types/supertest": "^2.0.12",
Expand Down
45 changes: 45 additions & 0 deletions prisma/migrations/20240812035737_add_oauth_tables/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
Warnings:
- You are about to drop the column `discordId` on the `User` table. All the data in the column will be lost.
- You are about to drop the column `githubId` on the `User` table. All the data in the column will be lost.
- You are about to drop the column `linkedinId` on the `User` table. All the data in the column will be lost.
- You are about to drop the column `twitterId` on the `User` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "User" DROP COLUMN "discordId",
DROP COLUMN "githubId",
DROP COLUMN "linkedinId",
DROP COLUMN "twitterId";

-- CreateTable
CREATE TABLE "OAuthProvider" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "OAuthProvider_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "UserOAuthProfile" (
"userId" UUID NOT NULL,
"providerId" INTEGER NOT NULL,
"providerUserId" TEXT NOT NULL,
"providerUsername" TEXT,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "UserOAuthProfile_pkey" PRIMARY KEY ("userId","providerId")
);

-- CreateIndex
CREATE UNIQUE INDEX "UserOAuthProfile_providerId_providerUserId_key" ON "UserOAuthProfile"("providerId", "providerUserId");

-- AddForeignKey
ALTER TABLE "UserOAuthProfile" ADD CONSTRAINT "UserOAuthProfile_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "UserOAuthProfile" ADD CONSTRAINT "UserOAuthProfile_providerId_fkey" FOREIGN KEY ("providerId") REFERENCES "OAuthProvider"("id") ON DELETE CASCADE ON UPDATE CASCADE;
3 changes: 3 additions & 0 deletions prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"
30 changes: 26 additions & 4 deletions prisma/schema/user.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ model User {
firstName String?
lastName String?
avatar String?
githubId String?
discordId String?
twitterId String?
linkedinId String?
gender Gender? @relation(fields: [genderId], references: [id], onDelete: SetNull, onUpdate: Cascade)
genderId Int?
countryCode String?
Expand All @@ -41,6 +37,7 @@ model User {
resetToken ResetToken?
emailVerificationToken EmailVerificationToken?
roles UserRole[]
oAuthProfiles UserOAuthProfile[]
}

model Role {
Expand All @@ -65,3 +62,28 @@ model UserRole {
@@unique(fields: [userId, roleId], name: "userRoleKey")
}

model OAuthProvider {
id Int @id @default(autoincrement())
name String
createdAt DateTime @default(now()) @db.Timestamptz()
updatedAt DateTime @updatedAt
userOAuthProfiles UserOAuthProfile[]
}

model UserOAuthProfile {
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String @db.Uuid
provider OAuthProvider @relation(fields: [providerId], references: [id], onDelete: Cascade)
providerId Int
providerUserId String
providerUsername String?
createdAt DateTime @default(now()) @db.Timestamptz()
updatedAt DateTime @updatedAt
@@id(fields: [userId, providerId], name: "oauthProfileId")
@@unique(fields: [providerId, providerUserId], name: "providerUserKey")
}
23 changes: 23 additions & 0 deletions prisma/seed/oauth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { prisma } from "./prisma-client";

const populateOAuthProviders = async () => {
await prisma.oAuthProvider.createMany({
data: [
{
name: "discord",
},
{
name: "github",
},
],
});
};

const populateOAuthUserProfiles = async () => {};

export const populateOAuth = async () => {
await populateOAuthProviders();
await populateOAuthUserProfiles();
};

console.log("OAuth Providers and userProfile populated");
18 changes: 16 additions & 2 deletions prisma/seed/responses/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,28 @@ const getTeamMembers = async (teamMemberId: number) => {
select: {
member: {
select: {
discordId: true,
oAuthProfiles: {
select: {
provider: {
select: {
name: true,
},
},
providerUsername: true,
},
},
},
},
},
},
},
});
return team!.voyageTeamMembers.map((m) => m.member.discordId);
return team!.voyageTeamMembers.map(
(m) =>
m.member.oAuthProfiles.find(
(profile) => profile.provider.name === "discord",
)?.providerUsername,
);
};

/*
Expand Down
14 changes: 0 additions & 14 deletions prisma/seed/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ export const populateUsers = async () => {
emailVerified: true,
firstName: "Jessica",
lastName: "Williamson",
githubId: "jess-github",
discordId: "jess-discord",
twitterId: "jess-twitter",
linkedinId: "jess-linkedin",
avatar: "https://gravatar.com/avatar/3bfaef00e02a22f99e17c66e7a9fdd31?s=400&d=robohash&r=x",
timezone: "Australia/Melbourne",
countryCode: "AU",
Expand Down Expand Up @@ -51,8 +47,6 @@ export const populateUsers = async () => {
emailVerified: true,
firstName: "Larry",
lastName: "Castro",
githubId: "larryc-github",
discordId: "larryc-discord",
avatar: "https://gravatar.com/avatar/3bfaef00e02a22f99e17c66e7a9fdd31?s=400&d=monsterid&r=x",
timezone: "America/Chicago",
comment: "Member seems to be inactive",
Expand Down Expand Up @@ -86,8 +80,6 @@ export const populateUsers = async () => {
emailVerified: true,
firstName: "Leonarda",
lastName: "Rowe",
githubId: "leo-github",
discordId: "leo-discord",
avatar: "https://gravatar.com/avatar/3bfaef00e02a22f99e17c66e7a9fdd31?s=400&d=identicon&r=x",
timezone: "America/Los_Angeles",
comment: "This is a random admin comment",
Expand All @@ -114,8 +106,6 @@ export const populateUsers = async () => {
emailVerified: true,
firstName: "Joso",
lastName: "Mađar",
githubId: "joso-github",
discordId: "joso-discord",
avatar: "https://gravatar.com/avatar/3bfaef00e02a22f99e17c66e7a9fdd31?s=400&d=wavatar&r=x",
timezone: "Europe/Zagreb",
comment: "This is a random admin comment",
Expand Down Expand Up @@ -173,10 +163,6 @@ export const populateUsers = async () => {
emailVerified: false,
firstName: "Yoshi",
lastName: "Amano",
githubId: "yoshi-github",
discordId: "yoshi-discord",
twitterId: "yoshi-twitter",
linkedinId: "yoshi-linkedin",
avatar: "https://gravatar.com/avatar/3bfaef00e02a22f99e17c66e7a9fdd31?s=400&d=robohash&r=x",
timezone: "Australia/Melbourne",
countryCode: "AU",
Expand Down
23 changes: 23 additions & 0 deletions src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Body,
Controller,
Delete,
Get,
HttpCode,
HttpStatus,
Post,
Expand Down Expand Up @@ -42,6 +43,7 @@ import { CheckAbilities } from "../global/decorators/abilities.decorator";
import { Action } from "../ability/ability.factory/ability.factory";
import { CustomRequest } from "../global/types/CustomRequest";
import { Response } from "express";
import { DiscordAuthGuard } from "./guards/discord-auth.guard";

@ApiTags("Auth")
@Controller("auth")
Expand Down Expand Up @@ -346,4 +348,25 @@ export class AuthController {
async resetPassword(@Body() resetPasswordDto: ResetPasswordDto) {
return this.authService.resetPassword(resetPasswordDto);
}

@ApiOperation({
summary: "discord oauth",
description:
"This does not work on swagger. Open this link from a web browser, a discord popup should appear. `{BaseURL}/api/v1/auth/discord/login` ",
})
@UseGuards(DiscordAuthGuard)
@Public()
@Get("/discord/login")
handleDiscordLogin() {
return { msg: "Discord Authentication" };
}

@UseGuards(DiscordAuthGuard)
@Public()
@Get("/discord/redirect")
handleDiscordRedirect() {
return { msg: "Discord Redirect" };
}

// TODO: Discord logout, will probably just be in the normal logout route
}
14 changes: 13 additions & 1 deletion src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { JwtModule } from "@nestjs/jwt";
import { AtStrategy } from "./strategies/at.strategy";
import { RtStrategy } from "./strategies/rt.strategy";
import * as process from "process";
import { DiscordStrategy } from "./strategies/discord.strategy";
import { DiscordAuthService } from "./discord-auth.service";

@Module({
imports: [
Expand All @@ -17,7 +19,17 @@ import * as process from "process";
secret: process.env.JWT_SECRET,
}),
],
providers: [AuthService, LocalStrategy, AtStrategy, RtStrategy],
providers: [
AuthService,
LocalStrategy,
AtStrategy,
RtStrategy,
DiscordStrategy,
{
provide: "DISCORD_OAUTH",
useClass: DiscordAuthService,
},
],
controllers: [AuthController],
exports: [AuthService],
})
Expand Down
22 changes: 22 additions & 0 deletions src/auth/discord-auth.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Injectable } from "@nestjs/common";
import { IAuthProvider } from "../global/interfaces/oauth.interface";
import { PrismaService } from "../prisma/prisma.service";
import { DiscordUser } from "../global/types/auth.types";

@Injectable()
export class DiscordAuthService implements IAuthProvider {
constructor(private prisma: PrismaService) {}
async validateUser(user: DiscordUser) {
const userInDb = await this.prisma.findUserByOAuthId(
"discord",
user.discordId,
);
console.log(
`discord-auth.service.ts (14): userInDb = ${JSON.stringify(userInDb)}`,
);
}

createUser() {}

findUserById() {}
}
14 changes: 14 additions & 0 deletions src/auth/guards/discord-auth.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ExecutionContext, Injectable } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";

@Injectable()
export class DiscordAuthGuard extends AuthGuard("discord") {
async canActivate(context: ExecutionContext): Promise<boolean> {
const activate = (await super.canActivate(context)) as boolean;
console.log(`discord-auth.guard.ts (8): activate = ${activate}`);
const request = context.switchToHttp().getRequest();
console.log(`discord-auth.guard.ts (10): request = ${request}`);
await super.logIn(request);
return activate;
}
}
Loading

0 comments on commit 1f663be

Please sign in to comment.