Skip to content
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
52 changes: 52 additions & 0 deletions prisma/migrations/20210724121852-roles/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Migration `20210724121852-roles`

This migration has been generated by Pavlo Strunkin <pashidlos@gmail.com> at 7/24/2021, 3:18:52 PM.
You can check out the [state of the schema](./schema.prisma) after the migration.

## Database Steps

```sql
CREATE TYPE "public"."Role" AS ENUM ('admin', 'editor', 'guest')

ALTER TABLE "User" ADD COLUMN "role" "Role" NOT NULL DEFAULT E'guest'
```

## Changes

```diff
diff --git schema.prisma schema.prisma
migration 20210709133029-275-project-to-testrun-relation..20210724121852-roles
--- datamodel.dml
+++ datamodel.dml
@@ -3,9 +3,9 @@
}
datasource db {
provider = "postgresql"
- url = "***"
+ url = "***"
}
model Build {
id String @id @default(uuid())
@@ -122,8 +122,9 @@
apiKey String @unique
isActive Boolean @default(true)
builds Build[]
baselines Baseline[]
+ role Role @default(guest)
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
}
@@ -140,4 +141,10 @@
pixelmatch
lookSame
odiff
}
+
+enum Role {
+ admin
+ editor
+ guest
+}
```


150 changes: 150 additions & 0 deletions prisma/migrations/20210724121852-roles/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "postgresql"
url = "***"
}

model Build {
id String @id @default(uuid())
ciBuildId String?
number Int?
branchName String?
status String?
testRuns TestRun[]
projectId String
project Project @relation(fields: [projectId], references: [id])
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
user User? @relation(fields: [userId], references: [id])
userId String?
isRunning Boolean?

@@unique([projectId, ciBuildId])
}

model Project {
id String @id @default(uuid())
name String
mainBranchName String @default("master")
builds Build[]
buildsCounter Int @default(0)
maxBuildAllowed Int @default(100)
maxBranchLifetime Int @default(30)
testVariations TestVariation[]
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
// config
autoApproveFeature Boolean @default(false)
imageComparison ImageComparison @default(pixelmatch)
imageComparisonConfig String @default("{ \"threshold\": 0.1, \"ignoreAntialiasing\": true, \"allowDiffDimensions\": false }")

TestRun TestRun[]
@@unique([name])
}

model TestRun {
id String @id @default(uuid())
imageName String
diffName String?
diffPercent Float?
diffTollerancePercent Float @default(0)
pixelMisMatchCount Int?
status TestStatus
buildId String
build Build @relation(fields: [buildId], references: [id])
testVariationId String?
testVariation TestVariation? @relation(fields: [testVariationId], references: [id])
projectId String?
project Project? @relation(fields: [projectId], references: [id])
merge Boolean @default(false)
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
// Test variation data
name String @default("")
browser String?
device String?
os String?
viewport String?
customTags String? @default("")
baselineName String?
comment String?
baseline Baseline?
branchName String @default("master")
baselineBranchName String?
ignoreAreas String @default("[]")
tempIgnoreAreas String @default("[]")
}

model TestVariation {
id String @id @default(uuid())
name String
branchName String @default("master")
browser String @default("")
device String @default("")
os String @default("")
viewport String @default("")
customTags String @default("")
baselineName String?
ignoreAreas String @default("[]")
projectId String
project Project @relation(fields: [projectId], references: [id])
testRuns TestRun[]
baselines Baseline[]
comment String?
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())

@@unique([projectId, name, browser, device, os, viewport, customTags, branchName])
}

model Baseline {
id String @id @default(uuid())
baselineName String
testVariationId String
testVariation TestVariation @relation(fields: [testVariationId], references: [id])
testRunId String?
testRun TestRun? @relation(fields: [testRunId], references: [id])
userId String?
user User? @relation(fields: [userId], references: [id])
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
}

model User {
id String @id @default(uuid())
email String @unique
password String
firstName String?
lastName String?
apiKey String @unique
isActive Boolean @default(true)
builds Build[]
baselines Baseline[]
role Role @default(guest)
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
}

enum TestStatus {
failed
new
ok
unresolved
approved
autoApproved
}

enum ImageComparison {
pixelmatch
lookSame
odiff
}

enum Role {
admin
editor
guest
}
46 changes: 46 additions & 0 deletions prisma/migrations/20210724121852-roles/steps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "CreateEnum",
"enum": "Role",
"values": [
"admin",
"editor",
"guest"
]
},
{
"tag": "CreateField",
"model": "User",
"field": "role",
"type": "Role",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "role"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "User",
"field": "role"
},
"directive": "default"
},
"argument": "",
"value": "guest"
}
]
}
1 change: 1 addition & 0 deletions prisma/migrations/migrate.lock
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@
20210705154453-baseline-author
20210709115233-gh-275-max-branch-lifetime
20210709133029-275-project-to-testrun-relation
20210724121852-roles
7 changes: 7 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ model User {
isActive Boolean @default(true)
builds Build[]
baselines Baseline[]
role Role @default(guest)
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
}
Expand All @@ -141,3 +142,9 @@ enum ImageComparison {
lookSame
odiff
}

enum Role {
admin
editor
guest
}
57 changes: 33 additions & 24 deletions prisma/seed.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PrismaClient } from '@prisma/client';
import { PrismaClient, Role } from '@prisma/client';
import uuidAPIKey from 'uuid-apikey';
import { genSalt, hash } from 'bcryptjs';

Expand All @@ -20,29 +20,38 @@ seed()
async function createDefaultUser() {
const userList = await prisma.user.findMany();
console.log(userList);
if (userList.length === 0) {
const defaultEmail = 'visual-regression-tracker@example.com';
const defaultPassword = '123456';
const salt = await genSalt(10);
await prisma.user
.create({
data: {
email: defaultEmail,
firstName: 'fname',
lastName: 'lname',
apiKey: uuidAPIKey.create({ noDashes: true }).apiKey,
password: await hash(defaultPassword, salt),
},
})
.then((user) => {
console.log('###########################');
console.log('## CREATING DEFAULT USER ##');
console.log('###########################');
console.log('');
console.log(`The user with the email "${defaultEmail}" and password "${defaultPassword}" was created`);
console.log(`The Api key is: ${user.apiKey}`);
});
}

const defaultEmail = 'visual-regression-tracker@example.com';
const defaultPassword = '123456';
const salt = await genSalt(10);

await prisma.user
.upsert({
where: {
email: defaultEmail,
},
update: {
role: Role.admin,
},
create: {
email: defaultEmail,
firstName: 'fname',
lastName: 'lname',
role: Role.admin,
apiKey: uuidAPIKey.create({ noDashes: true }).apiKey,
password: await hash(defaultPassword, salt),
},
})
.then((user) => {
console.log('###########################');
console.log('####### DEFAULT USER ######');
console.log('###########################');
console.log('');
console.log(
`The user with the email "${defaultEmail}" and password "${defaultPassword}" was created (if not changed before)`
);
console.log(`The Api key is: ${user.apiKey}`);
});
}

async function createDefaultProject() {
Expand Down
46 changes: 46 additions & 0 deletions src/auth/guards/role.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { ExecutionContext, Injectable, CanActivate } from '@nestjs/common';
import { Role, User } from '@prisma/client';
import { Request } from 'express';
import { PrismaService } from '../../prisma/prisma.service';
import { Reflector } from '@nestjs/core';

@Injectable()
export class RoleGuard implements CanActivate {
constructor(private readonly prismaService: PrismaService, private reflector: Reflector) {}

async canActivate(context: ExecutionContext): Promise<boolean> {
const roles = this.reflector.get<Role[]>('roles', context.getHandler());
if (!roles) {
return true;
}

const user = await this.getUser(context);
return this.checkPermission(user);
}

checkPermission = (user: User): boolean => {
switch (user.role) {
case Role.admin: {
return true;
}
case Role.editor: {
// check project permissions later
return true;
}
default:
return false;
}
};

getUser = async (context: ExecutionContext): Promise<User> => {
const request: Request = context.switchToHttp().getRequest();

if (request.user) {
return request.user as User;
}

return this.prismaService.user.findUnique({
where: { apiKey: request.header('apiKey') },
});
};
}
Loading