diff --git a/.env.test b/.env.test deleted file mode 100644 index 4bad637..0000000 --- a/.env.test +++ /dev/null @@ -1 +0,0 @@ -MONGODB_CONNECTION_STRING=mongodb://127.0.0.1:27017/osogolf_test \ No newline at end of file diff --git a/backend/authorize.js b/backend/authorize.js new file mode 100644 index 0000000..ec9829f --- /dev/null +++ b/backend/authorize.js @@ -0,0 +1,38 @@ +'use strict'; + +const Archetype = require('archetype'); +const oso = require('../oso'); + +const AuthorizeParams = new Archetype({ + sessionId: { + $type: 'string', + $required: true + }, + userId: { + $type: 'string', + $required: true + }, + action: { + $type: 'string', + $required: true + }, + resourceType: { + $type: 'string', + $required: true + }, + resourceId: { + $type: 'string', + $required: true + } +}).compile('AuthorizeParams'); + +module.exports = async function authorize(params) { + params = new AuthorizeParams(params); + const authorized = await oso.authorize( + { type: 'User', id: `${params.sessionId}_${params.userId}` }, + params.action, + { type: params.resourceType, id: params.resourceId } + ); + + return { authorized }; +}; \ No newline at end of file diff --git a/backend/deleteFact.js b/backend/deleteFact.js new file mode 100644 index 0000000..db90cbb --- /dev/null +++ b/backend/deleteFact.js @@ -0,0 +1,61 @@ +'use strict'; + +const Archetype = require('archetype'); +const assert = require('assert'); +const oso = require('../oso'); + +const DeleteFactParams = new Archetype({ + sessionId: { + $type: 'string', + $required: true + }, + factType: { + $type: 'string', + $required: true, + $enum: ['role', 'attribute'] + }, + userId: { + $type: 'string', + $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'role') + }, + role: { + $type: 'string', + $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'role') + }, + resourceType: { + $type: 'string', + $required: true + }, + resourceId: { + $type: 'string', + $required: true + }, + attribute: { + $type: 'string', + $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'attribute') + }, + attributeValue: { + $type: 'boolean', + $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'attribute') + } +}).compile('DeleteFactParams'); + +module.exports = async function deleteFact(params) { + params = new DeleteFactParams(params); + if (params.factType === 'role') { + const resourceId = params.resourceType === 'Repository' ? `${params.sessionId}_${params.resourceId}` : params.resourceId; + await oso.delete( + 'has_role', + { type: 'User', id: `${params.sessionId}_${params.userId}` }, + params.role, + { type: params.resourceType, id: resourceId } + ); + } else { + await oso.delete( + params.attribute, + { type: 'Repository', id: `${params.sessionId}_${params.resourceId}` }, + { type: 'Boolean', id: !!params.attributeValue + '' } + ); + } + return { ok: true }; +}; \ No newline at end of file diff --git a/backend/facts.js b/backend/facts.js new file mode 100644 index 0000000..09312c9 --- /dev/null +++ b/backend/facts.js @@ -0,0 +1,48 @@ +'use strict'; + +const Archetype = require('archetype'); +const oso = require('../oso'); + +const FactsParams = new Archetype({ + sessionId: { + $type: 'string', + $required: true + }, + userId: { + $type: ['string'], + $required: true + } +}).compile('FactsParams'); + +module.exports = async function facts(params) { + params = new FactsParams(params); + const facts = []; + for (const userId of params.userId) { + const factsForUser = await oso.get( + 'has_role', + { type: 'User', id: `${params.sessionId}_${userId}` }, + null, + null + ); + facts.push(...factsForUser); + } + for (const repo of ['osohq/sample-apps', 'osohq/nodejs-client', 'osohq/configs']) { + let factsForRepo = await oso.get( + 'is_protected', + { type: 'Repository', id: `${params.sessionId}_${repo}` }, + null, + null + ); + facts.push(...factsForRepo); + + factsForRepo = await oso.get( + 'is_public', + { type: 'Repository', id: `${params.sessionId}_${repo}` }, + null, + null + ); + facts.push(...factsForRepo); + } + + return { facts }; +}; \ No newline at end of file diff --git a/backend/leaderboard.js b/backend/leaderboard.js new file mode 100644 index 0000000..cfb4f5d --- /dev/null +++ b/backend/leaderboard.js @@ -0,0 +1,20 @@ +'use strict'; + +const Player = require('../db/player'); +const connect = require('../db/connect'); + +module.exports = async function leaderboard() { + await connect(); + + const players = await Player + .find({ levelsCompleted: { $gt: 0 } }) + .select({ email: 0 }) + .sort({ + levelsCompleted: -1, + par: 1, + gameplayTimeMS: 1 + }); + + + return { players }; +}; \ No newline at end of file diff --git a/backend/resumeGame.js b/backend/resumeGame.js new file mode 100644 index 0000000..d54325c --- /dev/null +++ b/backend/resumeGame.js @@ -0,0 +1,24 @@ +'use strict'; + +const Archetype = require('archetype'); +const Player = require('../db/player'); +const connect = require('../db/connect'); + +const ResumeGameParams = new Archetype({ + sessionId: { + $type: 'string', + $required: true + } +}).compile('ResumeGameParams'); + +module.exports = async function resumeGame(params) { + const { sessionId } = new ResumeGameParams(params); + + await connect(); + + const player = await Player.findOne({ + sessionId + }); + + return { player }; +}; diff --git a/backend/startGame.js b/backend/startGame.js new file mode 100644 index 0000000..98caced --- /dev/null +++ b/backend/startGame.js @@ -0,0 +1,56 @@ +'use strict'; + +const Archetype = require('archetype'); +const Player = require('../db/player'); +const connect = require('../db/connect'); +const oso = require('../oso'); + +const StartGameParams = new Archetype({ + sessionId: { + $type: 'string', + $required: true + }, + name: { + $type: 'string', + $required: true + }, + email: { + $type: 'string', + $required: true + } +}).compile('StartGameParams'); + +module.exports = async function handler(params) { + const { sessionId, name, email } = new StartGameParams(params); + + await connect(); + + const player = await Player.create({ + sessionId, + name, + email + }); + + await oso.tell( + 'has_relation', + { type: 'Repository', id: `${sessionId}_osohq/configs` }, + 'organization', + { type: 'Organization', id: 'osohq' } + ); + + await oso.tell( + 'has_relation', + { type: 'Repository', id: `${sessionId}_osohq/sample-apps` }, + 'organization', + { type: 'Organization', id: 'osohq' } + ); + + await oso.tell( + 'has_relation', + { type: 'Repository', id: `${sessionId}_osohq/nodejs-client` }, + 'organization', + { type: 'Organization', id: 'osohq' } + ); + + return { player }; +}; diff --git a/backend/tell.js b/backend/tell.js new file mode 100644 index 0000000..32cf3bd --- /dev/null +++ b/backend/tell.js @@ -0,0 +1,76 @@ +'use strict'; + +const Archetype = require('archetype'); +const assert = require('assert'); +const oso = require('../oso'); + +const TellParams = new Archetype({ + sessionId: { + $type: 'string', + $required: true + }, + factType: { + $type: 'string', + $required: true, + $enum: ['role', 'attribute'] + }, + userId: { + $type: 'string', + $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'role') + }, + role: { + $type: 'string', + $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'role') + }, + resourceType: { + $type: 'string', + $required: (doc) => doc.role !== 'superadmin' + }, + resourceId: { + $type: 'string', + $required: (doc) => doc.role !== 'superadmin' + }, + attribute: { + $type: 'string', + $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'attribute') + }, + attributeValue: { + $type: 'boolean', + $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'attribute') + } +}).compile('TellParams'); + +module.exports = async function handler(params) { + const validatedParams = new TellParams(params); + assert.ok( + validatedParams.attribute == null || ['is_public', 'is_protected'].includes(validatedParams.attribute), + 'Invalid attribute' + ); + + if (validatedParams.factType === 'role') { + const resourceId = validatedParams.resourceType === 'Repository' ? `${validatedParams.sessionId}_${validatedParams.resourceId}` : validatedParams.resourceId; + + if (validatedParams.role === 'superadmin') { + await oso.tell( + 'has_role', + { type: 'User', id: `${validatedParams.sessionId}_${validatedParams.userId}` }, + validatedParams.role + ); + } else { + await oso.tell( + 'has_role', + { type: 'User', id: `${validatedParams.sessionId}_${validatedParams.userId}` }, + validatedParams.role, + { type: validatedParams.resourceType, id: resourceId } + ); + } + } else { + await oso.tell( + validatedParams.attribute, + { type: 'Repository', id: `${validatedParams.sessionId}_${validatedParams.resourceId}` }, + { type: 'Boolean', id: !!validatedParams.attributeValue + '' } + ); + } + + return { ok: true }; +}; \ No newline at end of file diff --git a/backend/verifySolutionForLevel.js b/backend/verifySolutionForLevel.js new file mode 100644 index 0000000..86dfa6c --- /dev/null +++ b/backend/verifySolutionForLevel.js @@ -0,0 +1,88 @@ +'use strict'; + +const Archetype = require('archetype'); +const Player = require('../db/player'); +const assert = require('assert'); +const connect = require('../db/connect'); +const levels = require('../levels'); +const oso = require('../oso'); + +const VerifySolutionForLevelParams = new Archetype({ + sessionId: { + $type: 'string', + $required: true + }, + level: { + $type: 'number', + $required: true, + $validate: v => assert.ok(v > 0 && v <= levels.length) + } +}).compile('VerifySolutionForLevelParams'); + +const constraintsByLevel = require('../../levels').map(level => level.constraints); + +const parByLevel = require('../../levels').map(level => level.par); + +module.exports = async function handler(params) { + const { sessionId, level } = new VerifySolutionForLevelParams(params); + + await connect(); + + const player = await Player.findOne({ sessionId }).orFail(); + + const constraints = constraintsByLevel[level - 1]; + let pass = true; + for (const constraint of constraints) { + const resourceId = constraint.resourceType === 'Repository' ? + `${sessionId}_${constraint.resourceId}` : + constraint.resourceId; + const authorized = await oso.authorize( + { type: 'User', id: `${sessionId}_${constraint.userId}` }, + constraint.action, + { type: constraint.resourceType, id: resourceId } + ); + if (authorized !== !constraint.shouldFail) { + pass = false; + } + } + if (!pass) { + throw new Error('Did not pass'); + } + + const userIds = new Set(constraints.map(constraint => constraint.userId)); + const facts = []; + for (const userId of userIds) { + const factsForUser = await oso.get( + 'has_role', + { type: 'User', id: `${sessionId}_${userId}` }, + null, + null + ); + facts.push(...factsForUser); + } + for (const repo of ['osohq/sample-apps', 'osohq/nodejs-client', 'osohq/configs']) { + let factsForRepo = await oso.get( + 'is_protected', + { type: 'Repository', id: `${sessionId}_${repo}` }, + null, + null + ); + facts.push(...factsForRepo); + + factsForRepo = await oso.get( + 'is_public', + { type: 'Repository', id: `${sessionId}_${repo}` }, + null, + null + ); + facts.push(...factsForRepo); + } + + player.levelsCompleted = player.levelsCompleted + 1; + player.parPerLevel[level - 1] = facts.length - parByLevel[level - 1]; + player.par = player.parPerLevel.reduce((sum, v) => sum + v); + player.gameplayTimeMS = Date.now() - player.startTime.valueOf(); + await player.save(); + + return { player }; +}; diff --git a/pages/api/authorize.js b/pages/api/authorize.js index 4444469..40a8939 100644 --- a/pages/api/authorize.js +++ b/pages/api/authorize.js @@ -1,41 +1,11 @@ 'use strict'; -import Archetype from 'archetype'; -import oso from '../../oso'; - -const AuthorizeParams = new Archetype({ - sessionId: { - $type: 'string', - $required: true - }, - userId: { - $type: 'string', - $required: true - }, - action: { - $type: 'string', - $required: true - }, - resourceType: { - $type: 'string', - $required: true - }, - resourceId: { - $type: 'string', - $required: true - } -}).compile('AuthorizeParams'); +import authorize from '../../backend/authorize'; export default async function handler(req, res) { try { - const params = new AuthorizeParams(req.query); - const authorized = await oso.authorize( - { type: 'User', id: `${params.sessionId}_${params.userId}` }, - params.action, - { type: params.resourceType, id: params.resourceId } - ); - - return res.status(200).json({ authorized }); + const result = await authorize(req.query); + return res.status(200).json(result); } catch (error) { console.error(error.stack); res.status(500).json({ message: error.message }); diff --git a/pages/api/delete-fact.js b/pages/api/delete-fact.js new file mode 100644 index 0000000..809b549 --- /dev/null +++ b/pages/api/delete-fact.js @@ -0,0 +1,13 @@ +'use strict'; + +import deleteFact from '../../backend/deleteFact'; + +export default async function handler(req, res) { + try { + const result = await deleteFact(req.body); + res.status(200).json(result); + } catch (error) { + console.error(error.stack); + return res.status(500).json({ message: error.message }); + } +}; \ No newline at end of file diff --git a/pages/api/deleteFact.js b/pages/api/deleteFact.js deleted file mode 100644 index 943b8f7..0000000 --- a/pages/api/deleteFact.js +++ /dev/null @@ -1,66 +0,0 @@ -'use strict'; - -import Archetype from 'archetype'; -import assert from 'assert'; -import oso from '../../oso'; - -const DeleteFactParams = new Archetype({ - sessionId: { - $type: 'string', - $required: true - }, - factType: { - $type: 'string', - $required: true, - $enum: ['role', 'attribute'] - }, - userId: { - $type: 'string', - $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'role') - }, - role: { - $type: 'string', - $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'role') - }, - resourceType: { - $type: 'string', - $required: true - }, - resourceId: { - $type: 'string', - $required: true - }, - attribute: { - $type: 'string', - $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'attribute') - }, - attributeValue: { - $type: 'boolean', - $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'attribute') - } -}).compile('DeleteFactParams'); - -export default async function handler(req, res) { - try { - const params = new DeleteFactParams(req.body); - if (params.factType === 'role') { - const resourceId = params.resourceType === 'Repository' ? `${params.sessionId}_${params.resourceId}` : params.resourceId; - await oso.delete( - 'has_role', - { type: 'User', id: `${params.sessionId}_${params.userId}` }, - params.role, - { type: params.resourceType, id: resourceId } - ); - } else { - await oso.delete( - params.attribute, - { type: 'Repository', id: `${params.sessionId}_${params.resourceId}` }, - { type: 'Boolean', id: !!params.attributeValue + '' } - ); - } - return res.status(200).json({ ok: true }); - } catch (error) { - console.error(error.stack); - return res.status(500).json({ message: error.message }); - } -}; \ No newline at end of file diff --git a/pages/api/facts.js b/pages/api/facts.js index ac5946c..dfe2f1a 100644 --- a/pages/api/facts.js +++ b/pages/api/facts.js @@ -1,51 +1,11 @@ 'use strict'; -import Archetype from 'archetype'; -import oso from '../../oso'; - -const FactsParams = new Archetype({ - sessionId: { - $type: 'string', - $required: true - }, - userId: { - $type: ['string'], - $required: true - } -}).compile('FactsParams'); +import facts from '../../backend/facts'; export default async function handler(req, res) { try { - const params = new FactsParams(req.body); - const facts = []; - for (const userId of params.userId) { - const factsForUser = await oso.get( - 'has_role', - { type: 'User', id: `${params.sessionId}_${userId}` }, - null, - null - ); - facts.push(...factsForUser); - } - for (const repo of ['osohq/sample-apps', 'osohq/nodejs-client', 'osohq/configs']) { - let factsForRepo = await oso.get( - 'is_protected', - { type: 'Repository', id: `${params.sessionId}_${repo}` }, - null, - null - ); - facts.push(...factsForRepo); - - factsForRepo = await oso.get( - 'is_public', - { type: 'Repository', id: `${params.sessionId}_${repo}` }, - null, - null - ); - facts.push(...factsForRepo); - } - - return res.status(200).json({ facts }); + const result = await facts(req.body); + res.status(200).json(result); } catch (error) { console.error(error.stack); res.status(500).json({ message: error.message }); diff --git a/pages/api/leaderboard.js b/pages/api/leaderboard.js index e7c60d2..376d060 100644 --- a/pages/api/leaderboard.js +++ b/pages/api/leaderboard.js @@ -1,24 +1,12 @@ 'use strict'; -import Archetype from 'archetype'; -import Player from '../../db/player'; -import connect from '../../db/connect'; +import leaderboard from '../../backend/leaderboard'; export default async function handler(req, res) { try { - await connect(); - - const players = await Player - .find({ levelsCompleted: { $gt: 0 } }) - .select({ email: 0 }) - .sort({ - levelsCompleted: -1, - par: 1, - gameplayTimeMS: 1 - }); + const result = await leaderboard(); - - return res.status(200).json({ players }); + return res.status(200).json(result); } catch (error) { console.error(error.stack); res.status(500).json({ message: error.message }); diff --git a/pages/api/resume-game.js b/pages/api/resume-game.js index 2ce67dd..dc12fbe 100644 --- a/pages/api/resume-game.js +++ b/pages/api/resume-game.js @@ -1,27 +1,12 @@ 'use strict'; -import Archetype from 'archetype'; -import Player from '../../db/player'; -import connect from '../../db/connect'; - -const ResumeGameParams = new Archetype({ - sessionId: { - $type: 'string', - $required: true - } -}).compile('ResumeGameParams'); +import resumeGame from '../../backend/resumeGame'; export default async function handler(req, res) { try { - const { sessionId } = new ResumeGameParams(req.query); - - await connect(); - - const player = await Player.findOne({ - sessionId - }); + const result = await resumeGame(req.query); - return res.status(200).json({ player }); + return res.status(200).json(result); } catch (error) { console.error(error.stack); res.status(500).json({ message: error.message }); diff --git a/pages/api/start-game.js b/pages/api/start-game.js new file mode 100644 index 0000000..82c1b01 --- /dev/null +++ b/pages/api/start-game.js @@ -0,0 +1,14 @@ +'use strict'; + +import startGame from '../../backend/startGame'; + +export default async function handler(req, res) { + try { + const result = await startGame(req.body); + + return res.status(200).json(result); + } catch (error) { + console.error(error.stack); + res.status(500).json({ message: error.message }); + } +}; \ No newline at end of file diff --git a/pages/api/startGame.js b/pages/api/startGame.js deleted file mode 100644 index 6cbf5c1..0000000 --- a/pages/api/startGame.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict'; - -import Archetype from 'archetype'; -import Player from '../../db/player'; -import connect from '../../db/connect'; -import oso from '../../oso'; - -const StartGameParams = new Archetype({ - sessionId: { - $type: 'string', - $required: true - }, - name: { - $type: 'string', - $required: true - }, - email: { - $type: 'string', - $required: true - } -}).compile('StartGameParams'); - -export default async function handler(req, res) { - try { - const { sessionId, name, email } = new StartGameParams(req.body); - - await connect(); - - const player = await Player.create({ - sessionId, - name, - email - }); - - await oso.tell( - 'has_relation', - { type: 'Repository', id: `${sessionId}_osohq/configs` }, - 'organization', - { type: 'Organization', id: 'osohq' } - ); - - await oso.tell( - 'has_relation', - { type: 'Repository', id: `${sessionId}_osohq/sample-apps` }, - 'organization', - { type: 'Organization', id: 'osohq' } - ); - - await oso.tell( - 'has_relation', - { type: 'Repository', id: `${sessionId}_osohq/nodejs-client` }, - 'organization', - { type: 'Organization', id: 'osohq' } - ); - - - return res.status(200).json({ player }); - } catch (error) { - console.error(error.stack); - res.status(500).json({ message: error.message }); - } -}; \ No newline at end of file diff --git a/pages/api/tell.js b/pages/api/tell.js index 0a777cf..8688dcd 100644 --- a/pages/api/tell.js +++ b/pages/api/tell.js @@ -1,78 +1,11 @@ 'use strict'; -import Archetype from 'archetype'; -import assert from 'assert'; -import oso from '../../oso'; - -const TellParams = new Archetype({ - sessionId: { - $type: 'string', - $required: true - }, - factType: { - $type: 'string', - $required: true, - $enum: ['role', 'attribute'] - }, - userId: { - $type: 'string', - $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'role') - }, - role: { - $type: 'string', - $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'role') - }, - resourceType: { - $type: 'string', - $required: (doc) => doc.role !== 'superadmin' - }, - resourceId: { - $type: 'string', - $required: (doc) => doc.role !== 'superadmin' - }, - attribute: { - $type: 'string', - $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'attribute') - }, - attributeValue: { - $type: 'boolean', - $validate: (v, type, doc) => assert.ok(v != null || doc.factType !== 'attribute') - } -}).compile('TellParams'); +import tell from '../../backend/tell'; export default async function handler(req, res) { try { - const params = new TellParams(req.body); - assert.ok( - params.attribute == null || ['is_public', 'is_protected'].includes(params.attribute), - 'Invalid attribute' - ); - - if (params.factType === 'role') { - const resourceId = params.resourceType === 'Repository' ? `${params.sessionId}_${params.resourceId}` : params.resourceId; - - if (params.role === 'superadmin') { - await oso.tell( - 'has_role', - { type: 'User', id: `${params.sessionId}_${params.userId}` }, - params.role - ); - } else { - await oso.tell( - 'has_role', - { type: 'User', id: `${params.sessionId}_${params.userId}` }, - params.role, - { type: params.resourceType, id: resourceId } - ); - } - } else { - await oso.tell( - params.attribute, - { type: 'Repository', id: `${params.sessionId}_${params.resourceId}` }, - { type: 'Boolean', id: !!params.attributeValue + '' } - ); - } - return res.status(200).json({ ok: true }); + const result = await tell(req.body); + return res.status(200).json(result); } catch (error) { console.error(error.stack); res.status(500).json({ message: error.message }); diff --git a/pages/api/test.js b/pages/api/test.js deleted file mode 100644 index debe500..0000000 --- a/pages/api/test.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function handler(req, res) { - res.status(200).json({ hello: 'world' }); -} \ No newline at end of file diff --git a/pages/api/verify-solution-for-level.js b/pages/api/verify-solution-for-level.js new file mode 100644 index 0000000..6c2cd54 --- /dev/null +++ b/pages/api/verify-solution-for-level.js @@ -0,0 +1,13 @@ +'use strict'; + +import verifySolutionForLevel from '../../backend/verifySolutionForLevel'; + +export default async function handler(req, res) { + try { + const result = await verifySolutionForLevel(req.body); + return res.status(200).json(result); + } catch (error) { + console.error(error.stack); + res.status(500).json({ message: error.message }); + } +}; \ No newline at end of file diff --git a/pages/api/verifySolutionForLevel.js b/pages/api/verifySolutionForLevel.js deleted file mode 100644 index 46a2710..0000000 --- a/pages/api/verifySolutionForLevel.js +++ /dev/null @@ -1,93 +0,0 @@ -'use strict'; - -import Archetype from 'archetype'; -import Player from '../../db/player'; -import assert from 'assert'; -import connect from '../../db/connect'; -import levels from '../../levels'; -import oso from '../../oso'; - -const VerifySolutionForLevelParams = new Archetype({ - sessionId: { - $type: 'string', - $required: true - }, - level: { - $type: 'number', - $required: true, - $validate: v => assert.ok(v > 0 && v <= levels.length) - } -}).compile('VerifySolutionForLevelParams'); - -const constraintsByLevel = require('../../levels').map(level => level.constraints); - -const parByLevel = require('../../levels').map(level => level.par); - -export default async function handler(req, res) { - try { - const { sessionId, level } = new VerifySolutionForLevelParams(req.body); - - await connect(); - - const player = await Player.findOne({ sessionId }).orFail(); - - const constraints = constraintsByLevel[level - 1]; - let pass = true; - for (const constraint of constraints) { - const resourceId = constraint.resourceType === 'Repository' ? - `${sessionId}_${constraint.resourceId}` : - constraint.resourceId; - const authorized = await oso.authorize( - { type: 'User', id: `${sessionId}_${constraint.userId}` }, - constraint.action, - { type: constraint.resourceType, id: resourceId } - ); - if (authorized !== !constraint.shouldFail) { - pass = false; - } - } - if (!pass) { - throw new Error('Did not pass'); - } - - const userIds = new Set(constraints.map(constraint => constraint.userId)); - const facts = []; - for (const userId of userIds) { - const factsForUser = await oso.get( - 'has_role', - { type: 'User', id: `${sessionId}_${userId}` }, - null, - null - ); - facts.push(...factsForUser); - } - for (const repo of ['osohq/sample-apps', 'osohq/nodejs-client', 'osohq/configs']) { - let factsForRepo = await oso.get( - 'is_protected', - { type: 'Repository', id: `${sessionId}_${repo}` }, - null, - null - ); - facts.push(...factsForRepo); - - factsForRepo = await oso.get( - 'is_public', - { type: 'Repository', id: `${sessionId}_${repo}` }, - null, - null - ); - facts.push(...factsForRepo); - } - - player.levelsCompleted = player.levelsCompleted + 1; - player.parPerLevel[level - 1] = facts.length - parByLevel[level - 1]; - player.par = player.parPerLevel.reduce((sum, v) => sum + v); - player.gameplayTimeMS = Date.now() - player.startTime.valueOf(); - await player.save(); - - return res.status(200).json({ player }); - } catch (error) { - console.error(error.stack); - res.status(500).json({ message: error.message }); - } -}; \ No newline at end of file diff --git a/src/app-component/app-component.js b/src/app-component/app-component.js index 5e1dc5c..60c56af 100644 --- a/src/app-component/app-component.js +++ b/src/app-component/app-component.js @@ -71,7 +71,7 @@ module.exports = app => app.component('app-component', { this.state.showNextLevelButton = passed; }, async verifySolutionForLevel() { - const { player } = await axios.post('/api/verifySolutionForLevel', { + const { player } = await axios.post('/api/verify-solution-for-level', { sessionId: this.state.sessionId, level: this.state.level }).then(res => res.data); diff --git a/src/components.js b/src/components.js index 458575e..55a7c3b 100644 --- a/src/components.js +++ b/src/components.js @@ -1,6 +1,6 @@ 'use strict'; -// This file is auto-generated. Do not modify this file directly. + // This file is auto-generated. Do not modify this file directly. exports['add-role-fact'] = require('./add-role-fact/add-role-fact.js'); exports['app-component'] = require('./app-component/app-component.js'); exports['async-button'] = require('./async-button/async-button.js'); diff --git a/src/level/level.js b/src/level/level.js index 0850ba4..f0392db 100644 --- a/src/level/level.js +++ b/src/level/level.js @@ -206,7 +206,7 @@ module.exports = app => app.component('level', { return `User ${fact.userId} has role ${fact.role} on ${fact.resourceType} ${fact.resourceId}`; }, async deleteFact(fact) { - await axios.put('/api/deleteFact', { + await axios.put('/api/delete-fact', { sessionId: this.state.sessionId, ...fact }).then(res => res.data); @@ -220,7 +220,7 @@ module.exports = app => app.component('level', { return this.state.results[index].pass ? '/images/check-green.svg' : '/images/error-red.svg'; }, async verifySolutionForLevel() { - const { player } = await axios.post('/api/verifySolutionForLevel', { + const { player } = await axios.post('/api/verify-solution-for-level', { sessionId: this.state.sessionId, level: this.state.level }).then(res => res.data); diff --git a/src/splash-screen/splash-screen.js b/src/splash-screen/splash-screen.js index bfeff2e..405eaf5 100644 --- a/src/splash-screen/splash-screen.js +++ b/src/splash-screen/splash-screen.js @@ -24,7 +24,7 @@ module.exports = app => app.component('splash-screen', { return; } - const { player } = await axios.post('/api/startGame', { + const { player } = await axios.post('/api/start-game', { sessionId: this.state.sessionId, name: this.name, email: this.email diff --git a/test/deleteFact.test.js b/test/deleteFact.test.js deleted file mode 100644 index a3f339c..0000000 --- a/test/deleteFact.test.js +++ /dev/null @@ -1,60 +0,0 @@ -'use strict'; - -const Player = require('../db/player'); -const assert = require('assert'); -const { before, after, describe, it } = require('mocha'); -const mongoose = require('mongoose'); -const connect = require('../db/connect'); -const { deleteFact } = require('../netlify/functions/deleteFact'); - -describe('deleteFact', function() { - before(async function() { - await connect(); - }); - - after(async function() { - await mongoose.disconnect(); - }); - - it('deletes group role facts', async function() { - await Player.deleteMany({}); - let player = await Player.create({ - sessionId: '111', - email: 'test@osohq.com', - contextFacts: [ - [ - 'has_role', - { type: 'Group', id: 'superstars' }, - 'editor', - { type: 'Repository', id: 'osohq/sample-apps' } - ], - [ - 'has_role', - { type: 'User', id: 'Idris' }, - 'editor', - { type: 'Repository', id: 'acme/website' } - ] - ] - }); - - await deleteFact({ - sessionId: '111', - factType: 'role', - actorType: 'Group', - userId: 'superstars', - role: 'editor', - resourceType: 'Repository', - resourceId: 'osohq/sample-apps' - }); - - player = await Player.findById(player._id).orFail(); - assert.deepStrictEqual(player.toObject().contextFacts, [ - [ - 'has_role', - { type: 'User', id: 'Idris' }, - 'editor', - { type: 'Repository', id: 'acme/website' } - ] - ]); - }); -}); \ No newline at end of file diff --git a/test/setup.js b/test/setup.js deleted file mode 100644 index cdd2384..0000000 --- a/test/setup.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -const dotenv = require('dotenv'); -const path = require('path'); - -dotenv.config({ - path: path.resolve(path.join(__dirname, '..'), '.env.test') -}); \ No newline at end of file