-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Karol Wąsowski <wasowski02@protonmail.com> Co-authored-by: sormys <szymonp1806@gmail.com>
- Loading branch information
1 parent
e4be279
commit ae27bf9
Showing
5 changed files
with
222 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import admin from 'firebase-admin' | ||
import { readFileSync } from 'fs' | ||
|
||
const serviceAccount = JSON.parse( | ||
readFileSync('./src/serviceAccount.json', 'utf-8') | ||
) | ||
|
||
admin.initializeApp({ | ||
credential: admin.credential.cert({ | ||
privateKey: serviceAccount.private_key, | ||
clientEmail: serviceAccount.client_email, | ||
projectId: serviceAccount.project_id, | ||
}), | ||
}) | ||
|
||
export const verifyFCMToken = async (fcmToken) => { | ||
if (process.env.JEST_WORKER_ID !== undefined) { | ||
// We don't want to verify tokens when testing | ||
return true | ||
} else { | ||
let sentSuccessfully = true | ||
await admin | ||
.messaging() | ||
.send( | ||
{ | ||
token: fcmToken, | ||
}, | ||
true | ||
) | ||
.catch(() => { | ||
sentSuccessfully = false | ||
}) | ||
return sentSuccessfully | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { getClient } from '../utils/databaseConnection' | ||
import { rateLimiter } from '../utils/rateLimiter' | ||
import { celebrate, Joi, Segments } from 'celebrate' | ||
import { sendFirebaseMessage, verifyFCMToken } from '../utils/firebase' | ||
import sha256 from 'crypto-js/sha256' | ||
|
||
import express, { type Router } from 'express' | ||
const router: Router = express.Router() | ||
|
||
router.get( | ||
'/kickPlayer', | ||
rateLimiter, | ||
celebrate({ | ||
[Segments.QUERY]: Joi.object().keys({ | ||
creatorToken: Joi.string() | ||
.required() | ||
.min(1) | ||
.max(250) | ||
.label('creatorToken'), | ||
playerToken: Joi.string().required().min(1).max(250).label('playerToken'), | ||
}), | ||
}), | ||
async (req, res) => { | ||
if ( | ||
req.query.creatorToken === req.query.playerToken || | ||
!(await verifyFCMToken(req.query.creatorToken)) | ||
) { | ||
return res.sendStatus(400) | ||
} | ||
|
||
const client = getClient() | ||
client | ||
.connect() | ||
.then(async () => { | ||
// Define queries | ||
const getGameQuery = 'SELECT game_id FROM Games WHERE game_master=$1' | ||
const getPlayersQuery = 'SELECT token FROM Players WHERE game_id=$1' | ||
const deletePlayerQuery = 'DELETE FROM Players WHERE token=$1' | ||
|
||
// Check if game exists | ||
const getGameResult = await client.query(getGameQuery, [ | ||
req.query.creatorToken, | ||
]) | ||
|
||
if (getGameResult.rowCount === 0) { | ||
return res.sendStatus(400) | ||
} | ||
const gameId = getGameResult.rows[0].game_id | ||
|
||
// Verify player is in game and kick | ||
const getPlayersResult = await client.query(getPlayersQuery, [gameId]) | ||
let playerInGame = false | ||
let kickedPlayerToken = '' | ||
|
||
getPlayersResult.rows.forEach((row) => { | ||
if (sha256(row.token).toString() === req.query.playerToken) { | ||
playerInGame = true | ||
kickedPlayerToken = row.token | ||
} | ||
}) | ||
|
||
if (!playerInGame) { | ||
return res.sendStatus(400) | ||
} | ||
|
||
await client.query(deletePlayerQuery, [kickedPlayerToken]) | ||
|
||
// Notify players about the changes | ||
const message = { | ||
data: { | ||
type: 'playerKicked', | ||
playerHash: req.query.playerToken, | ||
}, | ||
token: '', | ||
} | ||
getPlayersResult.rows.forEach(async (row) => { | ||
message.token = row.token | ||
await sendFirebaseMessage(message) | ||
}) | ||
|
||
// TODO: Fix game state | ||
|
||
return res.sendStatus(200) | ||
}) | ||
.catch(async (err) => { | ||
console.log(err.stack) | ||
return res.sendStatus(500) | ||
}) | ||
.finally(async () => { | ||
await client.end() | ||
}) | ||
} | ||
) | ||
|
||
export default router |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { app } from '../app' | ||
import request from 'supertest' | ||
import { getClient } from '../utils/databaseConnection' | ||
import sha256 from 'crypto-js/sha256' | ||
import { type newGameInfo } from '../app' | ||
|
||
test('Kick player, wrong args', (done) => { | ||
request(app).get('/kickPlayer').expect(400).end(done) | ||
request(app).get('/kickPlayer?creatorToken=2137').expect(400).end(done) | ||
request(app).get('/kickPlayer?playerToken=1337').expect(400).end(done) | ||
|
||
request(app) | ||
.get('/kickPlayer?playerToken=1337'.concat('&creatorToken=1337')) | ||
.expect(400) | ||
.end(done) | ||
}) | ||
|
||
test('Kick player, correct arguments', async () => { | ||
const gameMasterToken = 'TESTKICK' | ||
const playerToken = 'TESTKICK2' | ||
const gameMasterNick = 'NICKKICK' | ||
const playerNick = 'NICKKICK2' | ||
|
||
const res = await request(app) | ||
.get( | ||
'/createGame/?creatorToken=' | ||
.concat(gameMasterToken) | ||
.concat('&nickname=') | ||
.concat(gameMasterNick) | ||
) | ||
.expect(200) | ||
const gameId = (res.body as newGameInfo).gameKey | ||
|
||
// Creator exists, but the player does not | ||
await request(app) | ||
.get( | ||
'/kickPlayer?creatorToken=' | ||
.concat(gameMasterToken) | ||
.concat('&playerToken=2137') | ||
) | ||
.expect(400) | ||
|
||
const verifyNoPlayerQuery = 'SELECT token FROM Players WHERE token=$1' | ||
|
||
const client = getClient() | ||
await client | ||
.connect() | ||
.then(async () => { | ||
await request(app) | ||
.get( | ||
'/joinGame/?playerToken=' | ||
.concat(playerToken) | ||
.concat('&nickname=') | ||
.concat(playerNick) | ||
.concat('&gameId=') | ||
.concat(gameId.toString()) | ||
) | ||
.expect(200) | ||
|
||
await request(app) | ||
.get( | ||
'/kickPlayer?'.concat( | ||
'creatorToken=' | ||
.concat(gameMasterToken) | ||
.concat('&playerToken='.concat(sha256(playerToken).toString())) | ||
) | ||
) | ||
.expect(200) | ||
|
||
await client.query(verifyNoPlayerQuery, [playerToken]).then((res) => { | ||
expect(res.rowCount).toEqual(0) | ||
}) | ||
}) | ||
.finally(async () => { | ||
const deleteGameQuery = 'DELETE FROM Games WHERE game_master = $1' | ||
await client.query(deleteGameQuery, [gameMasterToken]).catch((err) => { | ||
console.log(err.stack) | ||
}) | ||
const deletePlayerQuery = | ||
'DELETE FROM Players WHERE token = $1 or token = $2' | ||
await client | ||
.query(deletePlayerQuery, [playerToken, gameMasterToken]) | ||
.catch((err) => { | ||
console.log(err.stack) | ||
}) | ||
await client.end() | ||
}) | ||
}, 20000) |