Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DataQueryApi } from './dataQuery'
import { InfoApi } from './info'
import { MessageApi } from './message'
import { User2FAAuthApi } from './user2FAAuth'
import { UserGroupApi } from './userGroup'

export const Api: ExpressInitializer = {
init: (express: Express): void => {
Expand All @@ -17,5 +18,6 @@ export const Api: ExpressInitializer = {
InfoApi.init(express)
MessageApi.init(express)
User2FAAuthApi.init(express)
UserGroupApi.init(express)
},
}
2 changes: 2 additions & 0 deletions src/api/endpoint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { dataQuery } from './dataQuery'
import { info } from './info'
import { message } from './message'
import { user2FAAuth } from './user2FA'
import { userGroup } from './userGroup'

export const ApiEndpoint = {
auth,
Expand All @@ -12,4 +13,5 @@ export const ApiEndpoint = {
info,
message,
user2FAAuth,
userGroup,
}
13 changes: 13 additions & 0 deletions src/api/endpoint/userGroup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { getApiPathSurvey } from './common'

const moduleName = 'user-groups'

export const userGroup = {
userGroups: (surveyId: string): string => getApiPathSurvey(surveyId, moduleName),
userGroupsCount: (surveyId: string): string => getApiPathSurvey(surveyId, moduleName, 'count'),
userGroup: (surveyId: string, groupUuid: string): string => getApiPathSurvey(surveyId, moduleName, groupUuid),
userGroupMembers: (surveyId: string, groupUuid: string): string =>
getApiPathSurvey(surveyId, moduleName, groupUuid, 'members'),
userGroupMember: (surveyId: string, groupUuid: string, userUuid: string): string =>
getApiPathSurvey(surveyId, moduleName, groupUuid, 'members', userUuid),
}
16 changes: 16 additions & 0 deletions src/api/middleware/auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NextFunction, Request, Response } from 'express'
import {
Authorizer,
AuthGroupName,
DownloadAuthTokenPayload,
RecordService,
ServiceRegistry,
Expand Down Expand Up @@ -112,6 +113,18 @@ const requireUserPermission =
}
}

// Only the survey's `surveyAdmin` group (or system admins) may manage user-groups.
// Deliberately checks the auth-group's name rather than its stored `permissions`
// column: AuthGroups.getPermissions() (arena-core) derives permissions from a
// static, non-exported map keyed by group name, not from that DB column.
const canManageUserGroups = (user: User, surveyInfo?: any) => {
if (!user) return false
if (Users.isSystemAdmin(user)) return true
if (!surveyInfo) return false
const authGroup = Users.getAuthGroupBySurveyUuid(surveyInfo.uuid)(user)
return authGroup?.name === AuthGroupName.surveyAdmin
}

const requireLoggedInUser = async (req: Request, _res: Response, next: NextFunction) => {
try {
const user = Requests.getUser(req)
Expand Down Expand Up @@ -196,5 +209,8 @@ export const ApiAuthMiddleware = {
requireCanViewAccessRequestsPermission: requirePermission(Authorizer.canViewUsersAccessRequests),
requireCanEditAccessRequestsPermission: requirePermission(Authorizer.canEditUsersAccessRequests),

// User groups
requireUserGroupManagePermission: requireSurveyPermission(canManageUserGroups),

requireDownloadToken,
}
64 changes: 64 additions & 0 deletions src/api/userGroup/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Express } from 'express'

import { ServiceRegistry, ServiceType, SurveyService } from '@openforis/arena-core'

import { ExpressInitializer } from '../../server'
import { ServerServiceType } from '../../server/arenaServer/serverServiceType'
import { UserGroupService } from '../../service'
import { Requests } from '../../utils'

import { ApiEndpoint } from '../endpoint'
import { ApiAuthMiddleware } from '../middleware'

const { requireUserGroupManagePermission } = ApiAuthMiddleware

const getUserGroupService = (): UserGroupService =>
ServiceRegistry.getInstance().getService(ServerServiceType.userGroup) as UserGroupService

const getSurveyUuid = async (surveyId: number): Promise<string> => {
const surveyService = ServiceRegistry.getInstance().getService(ServiceType.survey) as SurveyService
const survey = await surveyService.get({ surveyId })
return survey.uuid
}

export const UserGroupCreate: ExpressInitializer = {
init: (express: Express): void => {
express.post(
ApiEndpoint.userGroup.userGroups(':surveyId'),
requireUserGroupManagePermission,
async (req, res, next) => {
try {
const { surveyId } = Requests.getParams(req)
const item = req.body

const surveyUuid = await getSurveyUuid(surveyId)

const service = getUserGroupService()
const userGroupInserted = await service.insert({ surveyUuid, item })

res.json(userGroupInserted)
} catch (error) {
next(error)
}
}
)

express.post(
ApiEndpoint.userGroup.userGroupMembers(':surveyId', ':groupUuid'),
requireUserGroupManagePermission,
async (req, res, next) => {
try {
const { groupUuid } = Requests.getParams(req)
const { userUuid } = req.body

const service = getUserGroupService()
await service.addMember({ groupUuid, userUuid })

res.json({ groupUuid, userUuid })
} catch (error) {
next(error)
}
}
)
},
}
54 changes: 54 additions & 0 deletions src/api/userGroup/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Express } from 'express'

import { ServiceRegistry } from '@openforis/arena-core'

import { ExpressInitializer } from '../../server'
import { ServerServiceType } from '../../server/arenaServer/serverServiceType'
import { UserGroupService } from '../../service'
import { Requests } from '../../utils'

import { ApiEndpoint } from '../endpoint'
import { ApiAuthMiddleware } from '../middleware'

const { requireUserGroupManagePermission } = ApiAuthMiddleware

const getUserGroupService = (): UserGroupService =>
ServiceRegistry.getInstance().getService(ServerServiceType.userGroup) as UserGroupService

export const UserGroupDelete: ExpressInitializer = {
init: (express: Express): void => {
express.delete(
ApiEndpoint.userGroup.userGroup(':surveyId', ':groupUuid'),
requireUserGroupManagePermission,
async (req, res, next) => {
try {
const { groupUuid } = Requests.getParams(req)

const service = getUserGroupService()
const userGroupDeleted = await service.deleteItem({ uuid: groupUuid })

res.json(userGroupDeleted)
} catch (error) {
next(error)
}
}
)
Comment thread
SteRiccio marked this conversation as resolved.
Outdated

express.delete(
ApiEndpoint.userGroup.userGroupMember(':surveyId', ':groupUuid', ':userUuid'),
requireUserGroupManagePermission,
async (req, res, next) => {
try {
const { groupUuid, userUuid } = Requests.getParams(req)

const service = getUserGroupService()
await service.removeMember({ groupUuid, userUuid })

res.json({ groupUuid, userUuid })
Comment thread
SteRiccio marked this conversation as resolved.
Outdated
} catch (error) {
next(error)
}
}
)
},
}
16 changes: 16 additions & 0 deletions src/api/userGroup/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Express } from 'express'

import { ExpressInitializer } from '../../server'
import { UserGroupCreate } from './create'
import { UserGroupRead } from './read'
import { UserGroupUpdate } from './update'
import { UserGroupDelete } from './delete'

export const UserGroupApi: ExpressInitializer = {
init: (express: Express): void => {
UserGroupCreate.init(express)
UserGroupRead.init(express)
UserGroupUpdate.init(express)
UserGroupDelete.init(express)
},
}
92 changes: 92 additions & 0 deletions src/api/userGroup/read.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Express } from 'express'

import { ServiceRegistry, ServiceType, SurveyService } from '@openforis/arena-core'

import { ExpressInitializer } from '../../server'
import { ServerServiceType } from '../../server/arenaServer/serverServiceType'
import { UserGroupService } from '../../service'
import { Requests } from '../../utils'

import { ApiEndpoint } from '../endpoint'
import { ApiAuthMiddleware } from '../middleware'

const { requireSurveyViewPermission } = ApiAuthMiddleware

const getUserGroupService = (): UserGroupService =>
ServiceRegistry.getInstance().getService(ServerServiceType.userGroup) as UserGroupService

const getSurveyUuid = async (surveyId: number): Promise<string> => {
const surveyService = ServiceRegistry.getInstance().getService(ServiceType.survey) as SurveyService
const survey = await surveyService.get({ surveyId })
return survey.uuid
}

export const UserGroupRead: ExpressInitializer = {
init: (express: Express): void => {
express.get(
ApiEndpoint.userGroup.userGroupsCount(':surveyId'),
requireSurveyViewPermission,
async (req, res, next) => {
try {
const { surveyId } = Requests.getParams(req)
const surveyUuid = await getSurveyUuid(surveyId)

const service = getUserGroupService()
const count = await service.count({ surveyUuid })

res.json({ count })
} catch (error) {
next(error)
}
}
)

express.get(ApiEndpoint.userGroup.userGroups(':surveyId'), requireSurveyViewPermission, async (req, res, next) => {
try {
const { surveyId } = Requests.getParams(req)
const surveyUuid = await getSurveyUuid(surveyId)

const service = getUserGroupService()
const list = await service.getAll({ surveyUuid })

res.json({ list })
} catch (error) {
next(error)
}
})

express.get(
ApiEndpoint.userGroup.userGroup(':surveyId', ':groupUuid'),
requireSurveyViewPermission,
async (req, res, next) => {
try {
const { groupUuid } = Requests.getParams(req)

const service = getUserGroupService()
const userGroup = await service.getByUuid({ uuid: groupUuid })

res.json(userGroup)
Comment thread
SteRiccio marked this conversation as resolved.
Outdated
} catch (error) {
next(error)
}
}
)

express.get(
ApiEndpoint.userGroup.userGroupMembers(':surveyId', ':groupUuid'),
requireSurveyViewPermission,
async (req, res, next) => {
try {
const { groupUuid } = Requests.getParams(req)

const service = getUserGroupService()
const list = await service.getMembers({ groupUuid })

res.json({ list })
} catch (error) {
next(error)
}
}
)
},
}
38 changes: 38 additions & 0 deletions src/api/userGroup/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Express } from 'express'

import { ServiceRegistry } from '@openforis/arena-core'

import { ExpressInitializer } from '../../server'
import { ServerServiceType } from '../../server/arenaServer/serverServiceType'
import { UserGroupService } from '../../service'
import { Requests } from '../../utils'

import { ApiEndpoint } from '../endpoint'
import { ApiAuthMiddleware } from '../middleware'

const { requireUserGroupManagePermission } = ApiAuthMiddleware

const getUserGroupService = (): UserGroupService =>
ServiceRegistry.getInstance().getService(ServerServiceType.userGroup) as UserGroupService

export const UserGroupUpdate: ExpressInitializer = {
init: (express: Express): void => {
express.put(
ApiEndpoint.userGroup.userGroup(':surveyId', ':groupUuid'),
requireUserGroupManagePermission,
async (req, res, next) => {
try {
const { groupUuid } = Requests.getParams(req)
const { props } = req.body

const service = getUserGroupService()
const userGroupUpdated = await service.update({ uuid: groupUuid, props })

res.json(userGroupUpdated)
Comment thread
SteRiccio marked this conversation as resolved.
Outdated
} catch (error) {
next(error)
}
}
)
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use strict'

var dbm
var type
var seed
var fs = require('fs')
var path = require('path')
var Promise

exports.setup = function (options, seedLink) {
dbm = options.dbmigrate
type = dbm.dataType
seed = seedLink
Promise = options.Promise
}

exports.up = function (db) {
var filePath = path.join(__dirname, 'sqls', '20260713124754-create-user-group-tables-up.sql')
return new Promise(function (resolve, reject) {
fs.readFile(filePath, { encoding: 'utf-8' }, function (err, data) {
if (err) return reject(err)
console.log('received data: ' + data)
resolve(data)
})
}).then(function (data) {
return db.runSql(data)
})
}

exports.down = function (db) {
var filePath = path.join(__dirname, 'sqls', '20260713124754-create-user-group-tables-down.sql')
return new Promise(function (resolve, reject) {
fs.readFile(filePath, { encoding: 'utf-8' }, function (err, data) {
if (err) return reject(err)
console.log('received data: ' + data)
resolve(data)
})
}).then(function (data) {
return db.runSql(data)
})
}

exports._meta = {
version: 1,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DROP TABLE IF EXISTS user_group_user;
DROP TABLE IF EXISTS user_group;
Loading
Loading