diff --git a/harena-manager.postman_collection.json b/harena-manager.postman_collection.json index 4ddd402..39d10be 100644 --- a/harena-manager.postman_collection.json +++ b/harena-manager.postman_collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "08ac4a21-3c4d-4d90-a981-9567ab291246", + "_postman_id": "e94367f3-5fe8-4628-9973-b1d5951cd65a", "name": "harena-manager", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, @@ -182,6 +182,62 @@ }, "response": [] }, + { + "name": "/auth/login_event session", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var response = pm.response.json();", + "console.log(response)", + "pm.environment.set(\"user-token\", response.token);", + "pm.environment.set(\"user-refreshToken\", response.refreshToken);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "eventId", + "value": "", + "type": "text", + "disabled": true + }, + { + "key": "username", + "value": "", + "type": "text", + "disabled": true + }, + { + "key": "login", + "value": "", + "type": "text", + "disabled": true + } + ] + }, + "url": { + "raw": "{{api-base-url}}/auth/login_event", + "host": [ + "{{api-base-url}}" + ], + "path": [ + "auth", + "login_event" + ] + } + }, + "response": [] + }, { "name": "/auth/logout session", "event": [ @@ -2520,12 +2576,12 @@ "formdata": [ { "key": "acronym", - "value": "unicamp", + "value": "progn", "type": "text" }, { "key": "title", - "value": "Universidade Estadual de Campinas", + "value": "Prognosis Game", "type": "text" }, { @@ -2988,6 +3044,49 @@ }, "response": [] }, + { + "name": "/group/managers", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "groupId", + "value": "", + "description": "group id (hash code)", + "type": "text" + } + ] + }, + "url": { + "raw": "{{api-base-url}}/group/managers", + "host": [ + "{{api-base-url}}" + ], + "path": [ + "group", + "managers" + ] + } + }, + "response": [] + }, { "name": "/group/user (wip)", "event": [ @@ -3116,6 +3215,40 @@ } }, "response": [] + }, + { + "name": "/group/link/manager", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "groupId", + "value": "", + "type": "text" + }, + { + "key": "userId", + "value": "", + "type": "text" + } + ] + }, + "url": { + "raw": "{{api-base-url}}/group/link/manager", + "host": [ + "{{api-base-url}}" + ], + "path": [ + "group", + "link", + "manager" + ] + } + }, + "response": [] } ] }, @@ -3284,7 +3417,7 @@ }, { "key": "open", - "value": "Title of this Event", + "value": "", "description": "boolean indicating if the event allows participation of non registered users", "type": "text" }, @@ -3367,6 +3500,24 @@ } }, "response": [] + }, + { + "name": "/events", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{api-base-url}}/event/list", + "host": [ + "{{api-base-url}}" + ], + "path": [ + "event", + "list" + ] + } + }, + "response": [] } ] }, @@ -3498,7 +3649,7 @@ "type": "text" }, { - "key": "emaiResponsible", + "key": "emailResponsible", "value": "", "description": "email of the person responsible", "type": "text" @@ -3594,6 +3745,44 @@ }, "response": [] }, + { + "name": "/term/list", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "formdata", + "formdata": [] + }, + "url": { + "raw": "{{api-base-url}}/term/list", + "host": [ + "{{api-base-url}}" + ], + "path": [ + "term", + "list" + ] + } + }, + "response": [] + }, { "name": "/term/users", "event": [ diff --git a/src/adonisjs/app/Controllers/Http/GroupController.js b/src/adonisjs/app/Controllers/Http/GroupController.js index 976993e..e6b42ef 100644 --- a/src/adonisjs/app/Controllers/Http/GroupController.js +++ b/src/adonisjs/app/Controllers/Http/GroupController.js @@ -3,6 +3,7 @@ const Group = use('App/Models/Group') const User = use('App/Models/v1/User') const UsersGroup = use('App/Models/v1/UsersGroup') +const ManagersGroup = use('App/Models/v1/ManagersGroup') const Database = use('Database') const uuidv4 = require('uuid/v4') @@ -33,22 +34,63 @@ class GroupController { } } + async canManageGroup (user, groupId) { + let canManage = await ManagersGroup + .query() + .where('user_id', user.id) + .where('group_id', groupId) + .first() + + if (!canManage) { + const roles = (await user.roles().fetch()).toJSON() + for (const r of roles) + if (r.slug == 'admin') + canManage = true + } + + return canManage + } + async linkUser ({ request, auth, response }) { try { const { userId, groupId } = request.post() const user = await User.find(userId) - const canLinkUser = await UsersGroup - .query() - .where('user_id', auth.user.id) - .where('group_id', groupId) - .first() - if(canLinkUser && user){ + + const canManage = await this.canManageGroup(auth.user, groupId) + + if (canManage && user) { await user.groups().attach(groupId) return response.json(user.username + ' successfully added to the group!') - }else if(!canLinkUser){ - return response.status(500).json('Error. You must be part of the group to be able to add another user.') - }else{ - return response.status(500).json('Error. Could not find the user to be added into the group.') + } else if (!canManage) { + return response.status(500).json( + 'Error. You must have the right to be able to add another user.') + } else { + return response.status(500).json( + 'Error. Could not find the user to be added into the group.') + } + + } catch (e) { + console.log(e) + return response.status(e.status).json({ message: e.toString() }) + } + } + + async linkManager ({ request, auth, response }) { + try { + const { userId, groupId } = request.post() + const user = await User.find(userId) + const canManage = await this.canManageGroup(auth.user, groupId) + + if (canManage && user) { + await user.groupManagers().attach(groupId) + return response.json(user.username + + ' successfully added as manager to the group!') + } else if (!canManage) { + return response.status(500).json( + 'Error. You must have the right to be able to add another manager.') + } else { + return response.status(500) + .json('Error. Could not find the user to be added into the group.') } } catch (e) { @@ -123,7 +165,10 @@ class GroupController { async listUsers ({ request, auth, response }) { try { const groupId = request.input('groupId') - if(await Group.find(groupId)){ + + const canManage = await this.canManageGroup(auth.user, groupId) + + if (canManage && await Group.find(groupId)) { const result = await Database .select('users.username','user_id','group_id','groups.title as group_title') .from('users_groups') @@ -132,16 +177,43 @@ class GroupController { .where ('users_groups.group_id', groupId) return response.json(result) - } - else { + } else if (!canManage) { + return response.status(500).json( + 'Error. You must have the right to be able to list group users.') + } else { return response.status(500).json('Error. Could not find selected group.') } } catch (e) { console.log(e) return response.status(e.status).json({ message: e.toString() }) } + } + + async listManagers ({ request, auth, response }) { + try { + const groupId = request.input('groupId') + const canManage = await this.canManageGroup(auth.user, groupId) + if (canManage && await Group.find(groupId)) { + const result = await Database + .select('users.username','user_id','group_id','groups.title as group_title') + .from('managers_groups') + .join('groups','managers_groups.group_id','groups.id') + .join('users', 'managers_groups.user_id', 'users.id') + .where ('managers_groups.group_id', groupId) + + return response.json(result) + } else if (!canManage) { + return response.status(500).json( + 'Error. You must have the right to be able to list group managers.') + } else { + return response.status(500).json('Error. Could not find selected group.') + } + } catch (e) { + console.log(e) + return response.status(e.status).json({ message: e.toString() }) + } } async removeUser ({ request, auth, response }){ diff --git a/src/adonisjs/app/Controllers/Http/v1/ArtifactController.js b/src/adonisjs/app/Controllers/Http/v1/ArtifactController.js index 4e9015f..977ed27 100644 --- a/src/adonisjs/app/Controllers/Http/v1/ArtifactController.js +++ b/src/adonisjs/app/Controllers/Http/v1/ArtifactController.js @@ -19,10 +19,14 @@ class ArtifactController { // See this for more on MIM types: https://docs.openx.com/Content/publishers/adunit_linearvideo_mime_types.html this.validationOptions = { size: '100mb', - types: ['image', 'video'], - extnames: ['png', 'jpg', 'jpeg', 'gif', 'svg', 'mp4', 'avi', 'wmv'] + types: ['image', 'video'] + // do not afford case insensitive check + // extnames: ['png', 'jpg', 'jpeg', 'gif', 'svg', 'mp4', 'avi', 'wmv'] } + this.validExtensions = + ['png', 'jpg', 'jpeg', 'gif', 'svg', 'mp4', 'avi', 'wmv', 'mov'] + this.relativePath = '/resources/artifacts/' this.baseUrl = Env.getOrFail('APP_URL') } @@ -31,71 +35,85 @@ class ArtifactController { try { const file = request.file('file', this.validationOptions) - const questId = request.input('questId') - const caseId = request.input('caseId', null) - - const artifactId = request.input('id') || await uuid4() - const artifactFileName = artifactId + '.' + file.extname - let fs_path = Helpers.publicPath(this.relativePath) + if (file == null) + return response.status(500).json({message: 'File to upload is missing'}) + else { + const extension = file.extname.toLowerCase() + + if (file.size > 100000000) + return response.status(500) + .json({ message: 'Size exceeds the maximum (100 MB)' }) + else if (!this.validExtensions.includes(extension)) { + return response.status(500) + .json({ message: 'Extension ' + file.extname + ' not accepted' }) + } - const artifact = new Artifact() - artifact.id = artifactId + const questId = request.input('questId') + const caseId = request.input('caseId', null) - const bodyMessage = { - filename: artifactFileName, - size_in_bytes: file.size, - type: file.type, - subtype: file.subtype, - extension: file.extname, - status: file.status - } + const artifactId = request.input('id') || await uuid4() + const artifactFileName = artifactId + '.' + extension + let fs_path = Helpers.publicPath(this.relativePath) - if (caseId != null && questId == null) { - var c = await Case.find(caseId) + const artifact = new Artifact() + artifact.id = artifactId - if (c == null) { return response.json({ message: 'Case id not found' }) } + const bodyMessage = { + filename: artifactFileName, + size_in_bytes: file.size, + type: file.type, + subtype: file.subtype, + extension: extension, + status: file.status + } - fs_path += 'cases/' + caseId + '/' - const case_relative_path = this.relativePath + 'cases/' + caseId + '/' + if (caseId != null && questId == null) { + var c = await Case.find(caseId) - artifact.relative_path = case_relative_path + artifactFileName + if (c == null) { return response.json({ message: 'Case id not found' }) } - const ca = new CaseArtifacts() - ca.case_id = c.id + fs_path += 'cases/' + caseId + '/' + const case_relative_path = this.relativePath + 'cases/' + caseId + '/' - await ca.artifact().associate(artifact) + artifact.relative_path = case_relative_path + artifactFileName - bodyMessage.case = c - bodyMessage.url = this.baseUrl + artifact.relative_path - } + const ca = new CaseArtifacts() + ca.case_id = c.id - if (questId != null && caseId == null) { - var quest = await Quest.find(questId) + await ca.artifact().associate(artifact) - if (quest == null) { - return response.json({ message: 'Quest id not found' }) + bodyMessage.case = c + bodyMessage.url = this.baseUrl + artifact.relative_path } - const questRelativePath = this.relativePath + 'quests/' + quest.id + '/' - artifact.relative_path = questRelativePath + artifactFileName + if (questId != null && caseId == null) { + var quest = await Quest.find(questId) - await quest.artifact().associate(artifact) + if (quest == null) { + return response.json({ message: 'Quest id not found' }) + } + const questRelativePath = this.relativePath + 'quests/' + quest.id + '/' - fs_path += 'quests/' + quest.id + '/' + artifact.relative_path = questRelativePath + artifactFileName - bodyMessage.quest = quest - bodyMessage.url = this.baseUrl + artifact.relative_path - } + await quest.artifact().associate(artifact) - if (questId == null && caseId == null) { - artifact.relative_path = this.relativePath + artifactFileName - bodyMessage.url = this.baseUrl + artifact.relative_path - } + fs_path += 'quests/' + quest.id + '/' + + bodyMessage.quest = quest + bodyMessage.url = this.baseUrl + artifact.relative_path + } - await file.move(fs_path, { name: artifactFileName, overwrite: false }) - await auth.user.artifacts().save(artifact) + if (questId == null && caseId == null) { + artifact.relative_path = this.relativePath + artifactFileName + bodyMessage.url = this.baseUrl + artifact.relative_path + } + + await file.move(fs_path, { name: artifactFileName, overwrite: false }) + await auth.user.artifacts().save(artifact) - return response.json(bodyMessage) + return response.json(bodyMessage) + } } catch (e) { console.log('============ Artifact catch error') console.log(e) diff --git a/src/adonisjs/app/Controllers/Http/v1/AuthController.js b/src/adonisjs/app/Controllers/Http/v1/AuthController.js index 901d311..d61cfca 100644 --- a/src/adonisjs/app/Controllers/Http/v1/AuthController.js +++ b/src/adonisjs/app/Controllers/Http/v1/AuthController.js @@ -4,6 +4,7 @@ const Logger = use('Logger') const User = use('App/Models/v1/User') const Institution = use('App/Models/v1/Institution') +const Event = use('App/Models/v1/Event') class AuthController { async checkToken ({ request, auth, response }) { @@ -54,6 +55,57 @@ class AuthController { } } + async loginEvent ({ request, auth, response, session }) { + Logger.info('login attempt via v1/auth/loginEvent (SESSION)') + let eventRel = null + const event_id = request.input('eventId') + if (event_id != null) + eventRel = await Event.find(event_id) + + if (eventRel != null) { + const username = request.input('username') + const login = request.input('login') + let user = null + try { + if (username != null) + user = await User.findBy('username', username) + else if (login != null) + user = await User.findBy('login', login) + + if (user != null) { + const roles = (await user.roles().fetch()).toJSON() + for (const r of roles) { + if (r.slug == 'admin') + return response.status(500).json({ + type: 'unauthorized', + message: 'Not authorized role login'}) + } + if (await auth.remember(true).login(user)) {} + return response.json({user: user, response: 'Login successful'}) + } else if (username != null) + return response.status(200).json({response: 'Nome incorreto'}) + else + return response.status(200).json({response: 'Login incorrect'}) + } catch (e) { + if (e.code === 'E_CANNOT_LOGIN') { + try { + // console.log('=============== Another was session found, logging out old session') + await auth.logout() + if (await auth.remember(true).login(user)) {} + return response.json({user: user, response: 'Login successful'}) + } catch (e) { + console.log(e) + } + } + return response.status(e.status).json({ message: e.message }) + } + } else { + return response.status(500).json({ + type: 'unauthorized', + message: 'Not authorized login'}) + } + } + async logout ({ auth, response }) { try { await auth.logout() diff --git a/src/adonisjs/app/Controllers/Http/v1/EventController.js b/src/adonisjs/app/Controllers/Http/v1/EventController.js index e24ee8b..090068a 100644 --- a/src/adonisjs/app/Controllers/Http/v1/EventController.js +++ b/src/adonisjs/app/Controllers/Http/v1/EventController.js @@ -77,6 +77,15 @@ class EventController { } } + async listEvents ({ request, response}) { + try { + const event = await Event.all() + return response.json(event) + } catch (e) { + return response.status(500).json({ message: e.message }) + } + } + } module.exports = EventController diff --git a/src/adonisjs/app/Controllers/Http/v1/TermController.js b/src/adonisjs/app/Controllers/Http/v1/TermController.js index a6ecc96..f5a7139 100644 --- a/src/adonisjs/app/Controllers/Http/v1/TermController.js +++ b/src/adonisjs/app/Controllers/Http/v1/TermController.js @@ -6,6 +6,15 @@ const User = use('App/Models/v1/User') const UsersTerm = use('App/Models/v1/UsersTerm') class TermController { + + async listTerms ({ request, response}) { + try { + const cases = await Term.all() + return response.json(cases) + } catch (e) { + return response.status(500).json({ message: e.message }) + } + } async store ({ request, response, auth }) { const trx = await Database.beginTransaction() try { diff --git a/src/adonisjs/app/Models/v1/ManagersGroup.js b/src/adonisjs/app/Models/v1/ManagersGroup.js new file mode 100644 index 0000000..b26e1ea --- /dev/null +++ b/src/adonisjs/app/Models/v1/ManagersGroup.js @@ -0,0 +1,12 @@ +'use strict' + +/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */ +const Model = use('Model') + +class ManagersGroup extends Model { + static get incrementing () { + return false + } +} + +module.exports = ManagersGroup diff --git a/src/adonisjs/app/Models/v1/User.js b/src/adonisjs/app/Models/v1/User.js index e4fad65..73f3841 100644 --- a/src/adonisjs/app/Models/v1/User.js +++ b/src/adonisjs/app/Models/v1/User.js @@ -26,6 +26,13 @@ class User extends Model { .withTimestamps() } + groupManagers () { + return this + .belongsToMany('App/Models/Group') + .pivotTable('managers_groups') + .withTimestamps() + } + artifacts () { return this.hasMany('App/Models/v1/Artifact') } diff --git a/src/adonisjs/database/migrations/1648414628567_managers_groups_schema.js b/src/adonisjs/database/migrations/1648414628567_managers_groups_schema.js new file mode 100644 index 0000000..cf3df2d --- /dev/null +++ b/src/adonisjs/database/migrations/1648414628567_managers_groups_schema.js @@ -0,0 +1,22 @@ +'use strict' + +/** @type {import('@adonisjs/lucid/src/Schema')} */ +const Schema = use('Schema') + +class ManagersGroupsSchema extends Schema { + up () { + this.create('managers_groups', (table) => { + table.uuid('user_id').references('id').inTable('users').index('user_id') + table.uuid('group_id').references('id').inTable('groups').index('group_id') + table.primary(['group_id', 'user_id']) + + table.timestamps() + }) + } + + down () { + this.drop('managers_groups') + } +} + +module.exports = ManagersGroupsSchema diff --git a/src/adonisjs/start/routes.js b/src/adonisjs/start/routes.js index d5e5a5c..49e696d 100644 --- a/src/adonisjs/start/routes.js +++ b/src/adonisjs/start/routes.js @@ -45,9 +45,10 @@ Route.group(() => { |---------------------------------------------------------------------------------------------- */ Route.group(() => { - Route.post('login', 'v1/AuthController.login') - Route.post('logout', 'v1/AuthController.logout').middleware(['auth']) - Route.get('check', 'v1/AuthController.checkToken') + Route.post('login', 'v1/AuthController.login') + Route.post('login_event', 'v1/AuthController.loginEvent') + Route.post('logout', 'v1/AuthController.logout').middleware(['auth']) + Route.get('check', 'v1/AuthController.checkToken') }).prefix('/api/v1/auth') @@ -174,12 +175,14 @@ Route.group(() => { |---------------------------------------------------------------------------------------------- */ Route.group(() => { - Route.post( '', 'GroupController.store') - Route.post( 'link/user', 'GroupController.linkUser') - Route.delete( 'user', 'GroupController.removeUser') - Route.get( 'cases', 'GroupController.listCases') - Route.get( 'users', 'GroupController.listUsers') - Route.get( '', 'GroupController.listGroups') + Route.post( '', 'GroupController.store') + Route.post( 'link/user', 'GroupController.linkUser') + Route.post( 'link/manager', 'GroupController.linkManager') + Route.delete( 'user', 'GroupController.removeUser') + Route.get( 'cases', 'GroupController.listCases') + Route.get( 'users', 'GroupController.listUsers') + Route.get( 'managers', 'GroupController.listManagers') + Route.get( '', 'GroupController.listGroups') // Route.get( 'list', 'v1/CategoryController.listCategories') // Route.put( ':id', 'v1/CategoryController.update') @@ -204,7 +207,8 @@ Route.group(() => { |---------------------------------------------------------------------------------------------- */ Route.group(() => { - Route.post( '', 'v1/EventController.store') + Route.post( '', 'v1/EventController.store').middleware(['auth', 'is:admin']) + Route.get( 'list', 'v1/EventController.listEvents').middleware(['auth', 'is:admin']) }).prefix('/api/v1/event').middleware('auth') /* @@ -218,6 +222,7 @@ Route.group(() => { Route.post( 'link/user', 'v1/TermController.linkUser') Route.get( 'user', 'v1/TermController.showTermUser') Route.get( 'users', 'v1/TermController.listTermUsers').middleware(['auth', 'is:admin']) + Route.get( 'list', 'v1/TermController.listTerms').middleware(['auth', 'is:admin']) }).prefix('/api/v1/term') /*