From 2513980f64c15007f616c18cbca1c2687623c7e0 Mon Sep 17 00:00:00 2001
From: Pascal Kosak <pascal.kosak@ruhr-uni-bochum.de>
Date: Fri, 27 Aug 2021 19:28:54 +0200
Subject: [PATCH] Add Auth, Update Models, Update Endpoints

---
 prisma/schema.prisma                          | 19 ++-----
 src/app.module.ts                             |  5 ++
 src/auth/auth.guard.ts                        | 34 +++++++++++
 src/main.ts                                   |  7 +++
 src/resolvers/agent.resolver.ts               | 37 +++++-------
 src/resolvers/entry.resolver.ts               | 46 ++++++++++++---
 src/resolvers/group.resolver.ts               | 17 +++---
 src/resolvers/models/agent.model.ts           |  4 --
 src/resolvers/models/entry.model.ts           | 17 +++++-
 src/resolvers/models/group.model.ts           |  6 +-
 .../models/unlockable-entry.model.ts          | 17 ------
 src/resolvers/models/unlocked-entry.model.ts  | 26 ---------
 src/resolvers/resolver.module.ts              |  4 --
 src/resolvers/unlockable-list.resolver.ts     | 18 ------
 src/resolvers/unlocked-entry.resolver.ts      | 57 -------------------
 src/telegram/telegram.service.ts              | 17 ++++--
 16 files changed, 140 insertions(+), 191 deletions(-)
 create mode 100644 src/auth/auth.guard.ts
 delete mode 100644 src/resolvers/models/unlockable-entry.model.ts
 delete mode 100644 src/resolvers/models/unlocked-entry.model.ts
 delete mode 100644 src/resolvers/unlockable-list.resolver.ts
 delete mode 100644 src/resolvers/unlocked-entry.resolver.ts

diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 843ab6e..71499ce 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -13,7 +13,7 @@ model Group {
     code    String  @unique
     tokens  Int     @default(0)
 
-    unlocks UnlockEntry[]
+    unlocks Entry[]
 }
 
 model Agent {
@@ -23,28 +23,17 @@ model Agent {
     name    String
 
     entries Entry[]
-    unlocks UnlockEntry[]
 }
 
-model Entry {
-    id      String  @id @default(uuid())
-    content String
-
-    createdAt DateTime @default(now())
-
-    agent   Agent   @relation(fields: [agentId], references: [id])
-    agentId String
-}
-
-
 // Types: 'picture', 'location', 'text'
-model UnlockEntry {
+model Entry {
     id      String  @id @default(uuid())
 
     type    String
+    private Boolean
 
     content String?
-    path    String?
+    image   String?
     lat     String?
     lon     String?
 
diff --git a/src/app.module.ts b/src/app.module.ts
index 848fccb..46d666f 100644
--- a/src/app.module.ts
+++ b/src/app.module.ts
@@ -3,6 +3,7 @@ import { PrismaModule } from './prisma/prisma.module';
 import { TelegramModule } from './telegram/telegram.module';
 import { GraphQLModule } from '@nestjs/graphql';
 import { ResolverModule } from './resolvers/resolver.module';
+import { AuthGuard } from './auth/auth.guard';
 
 @Module({
     imports: [
@@ -12,7 +13,11 @@ import { ResolverModule } from './resolvers/resolver.module';
         GraphQLModule.forRoot({
             autoSchemaFile: true,
             playground: true,
+            context: ({ request }) => ({
+                req: request,
+            }),
         }),
     ],
+    providers: [AuthGuard],
 })
 export class AppModule {}
diff --git a/src/auth/auth.guard.ts b/src/auth/auth.guard.ts
new file mode 100644
index 0000000..b1e3bb5
--- /dev/null
+++ b/src/auth/auth.guard.ts
@@ -0,0 +1,34 @@
+import { CanActivate, ExecutionContext, Injectable } from "@nestjs/common";
+import { GqlExecutionContext } from "@nestjs/graphql";
+import { Request } from "express";
+import { Observable } from "rxjs";
+import { PrismaService } from "src/prisma/prisma.service";
+
+@Injectable()
+export class AuthGuard implements CanActivate {
+
+    constructor(private prismaService: PrismaService) {}
+    
+    async canActivate(context: ExecutionContext): Promise<boolean> {
+        const ctx = GqlExecutionContext.create(context);
+
+        const req: Request = ctx.getContext().req;
+
+        const token = req.headers.authorization;
+
+        if (!token)
+            return false;
+
+        const group = await this.prismaService.group.findFirst({
+            where: { code: token },
+        });
+
+        if (!group)
+            return false;
+
+        req.group = group;
+
+        return true;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
index 617bfd9..4c1caf3 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,4 +1,11 @@
 import { NestFactory } from '@nestjs/core';
+import { Group } from '@prisma/client';
+
+declare module 'express' {
+    interface Request {
+        group: Group;
+    }
+}
 
 import * as dotenv from 'dotenv';
 dotenv.config();
diff --git a/src/resolvers/agent.resolver.ts b/src/resolvers/agent.resolver.ts
index b18bea9..3138bf7 100644
--- a/src/resolvers/agent.resolver.ts
+++ b/src/resolvers/agent.resolver.ts
@@ -1,21 +1,20 @@
-import { Args, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql';
+import { Args, Context, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql';
 import { Agent } from './models/agent.model';
 import { PrismaService } from '../prisma/prisma.service';
 import { GraphQLString } from 'graphql';
-import { NotFoundException } from '@nestjs/common';
+import { NotFoundException, UseGuards } from '@nestjs/common';
+import { AuthGuard } from 'src/auth/auth.guard';
+import { Request } from 'express';
 
 @Resolver(() => Agent)
+@UseGuards(AuthGuard)
 export class AgentResolver {
     constructor(private prismaService: PrismaService) {}
 
     @Query(() => [Agent])
-    listAgents() {
-        return this.prismaService.agent.findMany({
-            include: {
-                unlocks: true,
-                entries: true,
-            },
-        });
+    listAgents(@Context() { req }: { req: Request }) {
+        console.log(req.group)
+        return this.prismaService.agent.findMany();
     }
 
     @Query(() => Agent)
@@ -31,23 +30,17 @@ export class AgentResolver {
         return agent;
     }
 
-    @ResolveField('unlocks')
-    async unlocks(@Parent() entry: Agent) {
-        const result = await this.prismaService.agent.findFirst({
-            where: { id: entry.id },
-            select: { unlocks: true },
-        });
-
-        return result.unlocks;
-    }
-
     @ResolveField('entries')
     async entries(@Parent() entry: Agent) {
         const result = await this.prismaService.agent.findFirst({
-            where: { id: entry.id },
-            select: { entries: true },
+            where: {
+                id: entry.id,
+            },
+            include: { 
+                entries: true,
+            },
         });
 
-        return result.entries;
+
     }
 }
diff --git a/src/resolvers/entry.resolver.ts b/src/resolvers/entry.resolver.ts
index 3918bb8..70bc535 100644
--- a/src/resolvers/entry.resolver.ts
+++ b/src/resolvers/entry.resolver.ts
@@ -1,18 +1,16 @@
-import { Parent, Query, ResolveField, Resolver } from '@nestjs/graphql';
+import { Args, Context, Mutation, Parent, ResolveField, Resolver } from '@nestjs/graphql';
 import { Entry } from './models/entry.model';
 import { PrismaService } from '../prisma/prisma.service';
+import { GraphQLString } from 'graphql';
+import { BadRequestException, NotFoundException, UseGuards } from '@nestjs/common';
+import { Request } from 'express';
+import { AuthGuard } from 'src/auth/auth.guard';
 
 @Resolver(() => Entry)
+@UseGuards(AuthGuard)
 export class EntryResolver {
     constructor(private prismaService: PrismaService) {}
 
-    @Query(() => [Entry])
-    listEntries() {
-        return this.prismaService.entry.findMany({
-            take: 200,
-        });
-    }
-
     @ResolveField('agent')
     async agent(@Parent() entry: Entry) {
         const result = await this.prismaService.entry.findFirst({
@@ -22,4 +20,36 @@ export class EntryResolver {
 
         return result.agent;
     }
+
+    @Mutation(() => Entry)
+    async unlockEntry(
+        @Args({ name: 'id', type: () => GraphQLString }) id: string,
+        @Context() { req }: { req: Request },
+    ) {
+        const unlock = await this.prismaService.entry.findFirst({
+            where: { 
+                id,
+                private: true,
+            },
+        });
+
+        if (!unlock)
+            throw new NotFoundException();
+
+        if (req.group.tokens <= 0)
+            throw new BadRequestException('No remaining tokens');
+
+        await this.prismaService.group.update({
+            where: { id: req.group.id },
+            data: {
+                tokens: req.group.tokens - 1,
+                unlocks: {
+                    connect: { id },
+                },
+            },
+        });
+
+        return unlock;
+
+    }
 }
diff --git a/src/resolvers/group.resolver.ts b/src/resolvers/group.resolver.ts
index 95ab769..781044b 100644
--- a/src/resolvers/group.resolver.ts
+++ b/src/resolvers/group.resolver.ts
@@ -1,5 +1,6 @@
 import {
     Args,
+    Context,
     Mutation,
     Parent,
     Query,
@@ -10,9 +11,12 @@ import { Group } from './models/group.model';
 import { PrismaService } from '../prisma/prisma.service';
 import { GraphQLString } from 'graphql';
 import seedWords from 'mnemonic-words';
-import { NotFoundException } from '@nestjs/common';
+import { NotFoundException, UseGuards } from '@nestjs/common';
+import { AuthGuard } from 'src/auth/auth.guard';
+import { Request } from 'express';
 
 @Resolver(() => Group)
+@UseGuards(AuthGuard)
 export class GroupResolver {
     constructor(private prismaService: PrismaService) {}
 
@@ -41,16 +45,9 @@ export class GroupResolver {
 
     @Query(() => Group)
     async getGroup(
-        @Args({ name: 'code', type: () => GraphQLString }) code: string,
+        @Context() { req }: { req: Request },
     ) {
-        const group = this.prismaService.group.findFirst({
-            where: { code },
-            include: { unlocks: true },
-        });
-
-        if (!group) throw new NotFoundException();
-
-        return group;
+        return req.group;
     }
 
     @ResolveField('unlocks')
diff --git a/src/resolvers/models/agent.model.ts b/src/resolvers/models/agent.model.ts
index 04b30d0..432195c 100644
--- a/src/resolvers/models/agent.model.ts
+++ b/src/resolvers/models/agent.model.ts
@@ -1,5 +1,4 @@
 import { Field, ObjectType } from '@nestjs/graphql';
-import { UnlockableEntry } from './unlockable-entry.model';
 import { Entry } from './entry.model';
 
 @ObjectType()
@@ -13,9 +12,6 @@ export class Agent {
     @Field()
     name: string;
 
-    @Field(() => [UnlockableEntry])
-    unlocks: UnlockableEntry[];
-
     @Field(() => [Entry])
     entries: Entry[];
 }
diff --git a/src/resolvers/models/entry.model.ts b/src/resolvers/models/entry.model.ts
index 419b506..1be72c1 100644
--- a/src/resolvers/models/entry.model.ts
+++ b/src/resolvers/models/entry.model.ts
@@ -7,7 +7,22 @@ export class Entry {
     id: string;
 
     @Field()
-    content: string;
+    private: boolean;
+
+    @Field()
+    locked: boolean;
+
+    @Field()
+    type: string;
+
+    @Field({ nullable: true })
+    content?: string;
+    @Field({ nullable: true })
+    path?: string;
+    @Field({ nullable: true })
+    lat?: string;
+    @Field({ nullable: true })
+    lon?: string;
 
     @Field(() => GraphQLISODateTime)
     createdAt: Date;
diff --git a/src/resolvers/models/group.model.ts b/src/resolvers/models/group.model.ts
index 9dae2c3..0679cc8 100644
--- a/src/resolvers/models/group.model.ts
+++ b/src/resolvers/models/group.model.ts
@@ -1,5 +1,5 @@
 import { Field, Int, ObjectType } from '@nestjs/graphql';
-import { UnlockedEntry } from './unlocked-entry.model';
+import { Entry } from './entry.model';
 
 @ObjectType()
 export class Group {
@@ -15,6 +15,6 @@ export class Group {
     @Field(() => Int)
     tokens: number;
 
-    @Field(() => [UnlockedEntry])
-    unlocks: UnlockedEntry[];
+    @Field(() => [Entry])
+    unlocks: Entry[];
 }
diff --git a/src/resolvers/models/unlockable-entry.model.ts b/src/resolvers/models/unlockable-entry.model.ts
deleted file mode 100644
index 4bdeb9d..0000000
--- a/src/resolvers/models/unlockable-entry.model.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Field, GraphQLISODateTime, ObjectType } from '@nestjs/graphql';
-import { Agent } from './agent.model';
-
-@ObjectType()
-export class UnlockableEntry {
-    @Field()
-    id: string;
-
-    @Field()
-    type: string;
-
-    @Field(() => GraphQLISODateTime)
-    createdAt: Date;
-
-    @Field(() => Agent)
-    agent: Agent;
-}
diff --git a/src/resolvers/models/unlocked-entry.model.ts b/src/resolvers/models/unlocked-entry.model.ts
deleted file mode 100644
index 575b85d..0000000
--- a/src/resolvers/models/unlocked-entry.model.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Field, GraphQLISODateTime, ObjectType } from '@nestjs/graphql';
-import { Agent } from './agent.model';
-
-@ObjectType()
-export class UnlockedEntry {
-    @Field()
-    id: string;
-
-    @Field()
-    type: string;
-
-    @Field({ nullable: true })
-    content?: string;
-    @Field({ nullable: true })
-    path?: string;
-    @Field({ nullable: true })
-    lat?: string;
-    @Field({ nullable: true })
-    lon?: string;
-
-    @Field(() => GraphQLISODateTime)
-    createdAt: Date;
-
-    @Field(() => Agent)
-    agent: Agent;
-}
diff --git a/src/resolvers/resolver.module.ts b/src/resolvers/resolver.module.ts
index 82ae259..982faa9 100644
--- a/src/resolvers/resolver.module.ts
+++ b/src/resolvers/resolver.module.ts
@@ -2,16 +2,12 @@ import { Module } from '@nestjs/common';
 import { GroupResolver } from './group.resolver';
 import { EntryResolver } from './entry.resolver';
 import { AgentResolver } from './agent.resolver';
-import { UnlockableEntryResolver } from './unlockable-list.resolver';
-import { UnlockedEntryResolver } from './unlocked-entry.resolver';
 
 @Module({
     providers: [
         GroupResolver,
         EntryResolver,
-        UnlockableEntryResolver,
         AgentResolver,
-        UnlockedEntryResolver,
     ],
 })
 export class ResolverModule {}
diff --git a/src/resolvers/unlockable-list.resolver.ts b/src/resolvers/unlockable-list.resolver.ts
deleted file mode 100644
index aa2d0c1..0000000
--- a/src/resolvers/unlockable-list.resolver.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
-import { UnlockableEntry } from './models/unlockable-entry.model';
-import { PrismaService } from '../prisma/prisma.service';
-
-@Resolver(() => UnlockableEntry)
-export class UnlockableEntryResolver {
-    constructor(private prismaService: PrismaService) {}
-
-    @ResolveField('agent')
-    async agent(@Parent() entry: UnlockableEntry) {
-        const result = await this.prismaService.unlockEntry.findFirst({
-            where: { id: entry.id },
-            select: { agent: true },
-        });
-
-        return result.agent;
-    }
-}
diff --git a/src/resolvers/unlocked-entry.resolver.ts b/src/resolvers/unlocked-entry.resolver.ts
deleted file mode 100644
index 1ae876c..0000000
--- a/src/resolvers/unlocked-entry.resolver.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
-import { UnlockedEntry } from './models/unlocked-entry.model';
-import { UnlockableEntry } from './models/unlockable-entry.model';
-import { PrismaService } from '../prisma/prisma.service';
-import { GraphQLString } from 'graphql';
-import { BadRequestException, NotFoundException } from '@nestjs/common';
-
-@Resolver(() => UnlockedEntry)
-export class UnlockedEntryResolver {
-    constructor(private prismaService: PrismaService) {}
-
-    @Query(() => [UnlockableEntry])
-    async listUnlockables() {
-        return this.prismaService.unlockEntry.findMany({
-            take: 100,
-            select: {
-                id: true,
-                createdAt: true,
-                agent: true,
-            },
-        });
-    }
-
-    @Mutation(() => UnlockedEntry)
-    async unlockEntry(
-        @Args({ name: 'id', type: () => GraphQLString }) id: string,
-        @Args({ name: 'groupCode', type: () => GraphQLString })
-        groupCode: string,
-    ) {
-        const unlock = await this.prismaService.unlockEntry.findFirst({
-            where: { id },
-        });
-
-        if (!unlock) throw new NotFoundException();
-
-        const group = await this.prismaService.group.findFirst({
-            where: { code: groupCode },
-        });
-
-        if (!group) throw new NotFoundException();
-
-        if (group.tokens <= 0)
-            throw new BadRequestException('No remaining tokens');
-
-        await this.prismaService.group.update({
-            where: { code: groupCode },
-            data: {
-                tokens: group.tokens - 1,
-                unlocks: {
-                    connect: { id },
-                },
-            },
-        });
-
-        return unlock;
-    }
-}
diff --git a/src/telegram/telegram.service.ts b/src/telegram/telegram.service.ts
index ea9ce35..16674c1 100644
--- a/src/telegram/telegram.service.ts
+++ b/src/telegram/telegram.service.ts
@@ -60,6 +60,8 @@ export class TelegramService {
             data: {
                 agentId: agent.id,
                 content: match[1],
+                private: false,
+                type: 'text',
             },
         });
 
@@ -108,7 +110,7 @@ export class TelegramService {
             where: { agentId: agent.id },
         });
 
-        await this.prismaService.unlockEntry.deleteMany({
+        await this.prismaService.entry.deleteMany({
             where: { agentId: agent.id },
         });
 
@@ -137,7 +139,7 @@ export class TelegramService {
             where: { agentId: agent.id },
         });
 
-        const unlockEntries = await this.prismaService.unlockEntry.count({
+        const unlockEntries = await this.prismaService.entry.count({
             where: { agentId: agent.id },
         });
 
@@ -175,11 +177,12 @@ export class TelegramService {
 
             await new Promise((resolve) => pipe.on('finish', resolve));
 
-            await this.prismaService.unlockEntry.create({
+            await this.prismaService.entry.create({
                 data: {
                     type: 'picture',
                     agentId: agent.id,
-                    path: `${id}.jpg`,
+                    image: `${id}.jpg`,
+                    private: true,
                 },
             });
 
@@ -188,11 +191,12 @@ export class TelegramService {
                 `Accepted Photo`,
             ));
         } else if (metadata.type === 'text') {
-            await this.prismaService.unlockEntry.create({
+            await this.prismaService.entry.create({
                 data: {
                     type: 'text',
                     agentId: agent.id,
                     content: msg.text,
+                    private: true,
                 },
             });
 
@@ -201,12 +205,13 @@ export class TelegramService {
                 'Accepted Text',
             ));
         } else if (metadata.type === 'location') {
-            await this.prismaService.unlockEntry.create({
+            await this.prismaService.entry.create({
                 data: {
                     agentId: agent.id,
                     type: 'location',
                     lat: msg.location.latitude.toString(),
                     lon: msg.location.longitude.toString(),
+                    private: true,
                 },
             });
 
-- 
GitLab