Skip to content
This repository has been archived by the owner on Aug 19, 2022. It is now read-only.

Commit

Permalink
Nested Teams Endpoint (#477)
Browse files Browse the repository at this point in the history
* add nested teams to udaru core

* add nested endpoint

* add e2e test for nested team limit

* updating version and changes.md, updated pbac to 0.3.0

* use the buildParams directly to create base resource for user requests

* return 404 if nested team is not found
  • Loading branch information
jimmymintzer authored and dberesford committed Mar 7, 2018
1 parent a8a33a6 commit 1371578
Show file tree
Hide file tree
Showing 14 changed files with 369 additions and 23 deletions.
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 4.2.0 - February 15, 2018
Features, enhancements:
- Nested teams endpoint [commit] (https://github.com/nearform/udaru/pull/477)
- Team search endpoint [commit] (https://github.com/nearform/udaru/pull/473)
- Updated PBAC to version 0.3.0 (lodash vulnerability)

## 4.0.1 - February 15, 2018
Fixes:
- Migration scripts 006 & 007 (removed public schema)
Expand Down
2 changes: 1 addition & 1 deletion docs/swagger/swagger-json.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions lib/config/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const Actions = {
ReplaceTeamMember: 'authorization:teams:user:replace',
RemoveTeamMember: 'authorization:teams:user:remove',
AllTeam: 'authorization:teams:*',
ListNestedTeams: 'authorization:teams:nestedlist',

// user
CreateUser: 'authorization:users:create',
Expand Down
3 changes: 2 additions & 1 deletion lib/core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ function buildUdaruCore (dbPool, config) {
replaceUsers: teamOps.replaceUsersInTeam,
deleteMembers: teamOps.deleteTeamMembers,
deleteMember: teamOps.deleteTeamMember,
search: teamOps.search
search: teamOps.search,
listNestedTeams: teamOps.listNestedTeams
},

users: {
Expand Down
14 changes: 14 additions & 0 deletions lib/core/lib/mapping.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,20 @@ function mapTeamSimple (row) {

mapTeam.simple = mapTeamSimple

function mapNestedTeam (row) {
return {
id: row.id,
name: row.name,
description: row.description,
parentId: row.parent_id,
path: row.path,
organizationId: row.org_id,
usersCount: parseInt(row.users_count, 10)
}
}

mapTeam.listNestedTeam = mapNestedTeam

module.exports = {
organization: mapOrganization,
policy: mapPolicy,
Expand Down
52 changes: 52 additions & 0 deletions lib/core/lib/ops/teamOps.js
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,57 @@ function buildTeamOps (db, config) {
cb()
})
},
/**
* List a nested team in an organization
*
* @param {Object} params { organizationId, id, limit, page } where page is 1-indexed
* @param {Function} cb
*/
listNestedTeams: function listNestedTeams (params, cb) {
let { organizationId, id, limit, page } = params

Joi.validate({ organizationId, id, page, limit }, validationRules.listNestedTeams, function (err) {
if (err) return cb(Boom.badRequest(err))

let sqlQuery = SQL`
WITH total AS (
SELECT COUNT(*) AS cnt
FROM teams
WHERE org_id = ${organizationId} AND team_parent_id = ${id}
)
SELECT
teams.id,
teams.name,
teams.description,
teams.team_parent_id as parent_id,
teams.path,
teams.org_id,
t.cnt::INTEGER AS total,
COUNT(team_members.team_id) AS users_count
FROM teams
LEFT JOIN team_members ON team_members.team_id = teams.id
INNER JOIN total AS t ON 1=1
WHERE org_id = ${organizationId} AND team_parent_id = ${id}
GROUP BY teams.id, teams.name, teams.description, teams.team_parent_id, teams.path, teams.org_id, t.cnt
ORDER BY UPPER(name)
`

if (limit) {
sqlQuery.append(SQL` LIMIT ${limit}`)
}
if (limit && page) {
let offset = (page - 1) * limit
sqlQuery.append(SQL` OFFSET ${offset}`)
}

db.query(sqlQuery, function (err, result) {
if (err) return cb(Boom.badImplementation(err))

let total = result.rows.length > 0 ? result.rows[0].total : 0
return cb(null, result.rows.map(mapping.team.listNestedTeam), total)
})
})
},

/**
* Nest/Un-nest a team
Expand Down Expand Up @@ -855,6 +906,7 @@ function buildTeamOps (db, config) {
teamOps.deleteTeam.validate = validationRules.deleteTeam
teamOps.moveTeam.validate = validationRules.moveTeam
teamOps.addTeamPolicies.validate = validationRules.addTeamPolicies
teamOps.listNestedTeams.validate = validationRules.listNestedTeams

return teamOps
}
Expand Down
6 changes: 6 additions & 0 deletions lib/core/lib/ops/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ const teams = {
limit: validationRules.limit,
organizationId: validationRules.organizationId
},
listNestedTeams: {
page: validationRules.page,
limit: validationRules.limit,
organizationId: validationRules.organizationId,
id: validationRules.teamId
},
createTeam: {
id: Joi.string().regex(/^[0-9a-zA-Z_]+$/).max(128).description('The ID to be used for the new team. Only alphanumeric characters and underscore are supported'),
parentId: validationRules.teamId.optional().allow(null),
Expand Down
43 changes: 43 additions & 0 deletions lib/plugin/routes/public/teams.js
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,49 @@ exports.register = function (server, options, next) {
}
})

server.route({
method: 'GET',
path: '/authorization/teams/{id}/nested',
handler: function (request, reply) {
const { organizationId } = request.udaru
const limit = request.query.limit || server.udaruConfig.get('authorization.defaultPageSize')
const page = request.query.page || 1
const { id } = request.params

request.udaruCore.teams.read({ id, organizationId }, (err, data) => {
if (err) return reply(err)

request.udaruCore.teams.listNestedTeams({ organizationId, id, limit, page }, (err, data, total) => {
reply(err, err
? null
: {
data,
total,
page: page,
limit: limit
})
})
})
},
config: {
validate: {
query: _.pick(validation.listNestedTeams, ['page', 'limit']),
params: _.pick(validation.listNestedTeams, ['id']),
headers
},
description: 'Fetch a nested team given its identifier',
notes: 'The GET /authorization/teams/{id}/nested endpoint returns a list of team data.\n',
tags: ['api', 'teams'],
plugins: {
auth: {
action: Action.ListNestedTeams,
getParams: (request) => ({ teamId: request.params.id })
}
},
response: { schema: swagger.NestedPagedTeams }
}
})

next()
}

Expand Down
12 changes: 3 additions & 9 deletions lib/plugin/security/hapi-auth-validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,10 @@ function buildAuthValidation (authorization) {
})
}

function buildResourcesForUser (udaru, builder, userId, organizationId, done) {
const buildParams = {
userId,
teamId: '*',
organizationId
}

function buildResourcesForUser (udaru, builder, buildParams, organizationId, done) {
const resources = [builder(buildParams)]

udaru.users.read({ id: userId, organizationId: organizationId }, (err, user) => {
udaru.users.read({ id: buildParams.userId, organizationId: organizationId }, (err, user) => {
if (err && err.output.statusCode === 404) return done(null, resources)
if (err) return done(err)

Expand Down Expand Up @@ -83,7 +77,7 @@ function buildAuthValidation (authorization) {
const buildParams = Object.assign({}, { organizationId }, requestParams)

if (resourceType === 'users' && buildParams.userId) {
return buildResourcesForUser(udaru, resourceBuilder, buildParams.userId, organizationId, done)
return buildResourcesForUser(udaru, resourceBuilder, buildParams, organizationId, done)
}

done(null, [resourceBuilder(buildParams)])
Expand Down
15 changes: 15 additions & 0 deletions lib/plugin/swagger.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ const Team = Joi.object({

const Teams = Joi.array().items(Team).description('items').label('Teams')

const NestedTeam = Joi.object({
id: Joi.string().description('Team ID'),
name: Joi.string().description('Team name'),
description: Joi.string().description('Team description'),
parentId: Joi.string().description('Parent Team ID'),
path: Joi.string(),
organizationId: Joi.string().description('Organization ID to which the team belongs to'),
usersCount: Joi.number().description('Number of team users. Sub team users not counted.')
}).label('Nested Team')

const NestedTeams = Joi.array().items(NestedTeam).description('items').label('Nested Teams')

const User = Joi.object({
id: Joi.string().description('User ID'),
name: Joi.string().description('User name'),
Expand Down Expand Up @@ -99,6 +111,7 @@ const List = (data) => {

const PagedPolicies = List(Policies).label('PagedPolicies')
const PagedTeams = List(Teams).label('PagedTeams').description('Note: teams users and policies are not populated in paged teams list')
const NestedPagedTeams = List(NestedTeams).label('NestedPagedTeams').description('Note: teams users and policies are not populated in nested paged teams list')
const PagedTeamRefs = List(TeamRefs).label('PagedTeamRefs')
const PagedUsers = List(Users).label('PagedUsers')
const PagedOrganizations = List(Organizations).label('PagedOrganizations')
Expand Down Expand Up @@ -131,10 +144,12 @@ module.exports = {
UserActions,
UserActionsOnResources,
Team,
NestedTeam,
TeamRef,
Policy,
PagedPolicies,
PagedTeams,
NestedPagedTeams,
PagedTeamRefs,
PagedUsers,
PagedOrganizations,
Expand Down
26 changes: 16 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "udaru",
"version": "4.0.1",
"version": "4.2.0",
"description": "A policy based authorization module",
"license": "MIT",
"author": "nearForm Ltd",
Expand Down Expand Up @@ -70,7 +70,7 @@
"jsonfile": "^3.0.1",
"lodash": "^4.17.5",
"minimist": "^1.2.0",
"pbac": "0.2.0",
"pbac": "0.3.0",
"pg": "^7.4.1",
"pino": "^4.10.3",
"postgrator": "^2.10.3",
Expand Down
Loading

0 comments on commit 1371578

Please sign in to comment.