Skip to content

Commit d382fe1

Browse files
authored
Merge pull request #300 from developmentseed/fix/moderator-access-to-team-profile
Fix access of team moderator to profile keys
2 parents 4641dd4 + b95e96b commit d382fe1

File tree

7 files changed

+100
-13
lines changed

7 files changed

+100
-13
lines changed

app/lib/organization.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,20 @@ async function isMemberOrStaff (organizationId, osmId) {
265265
return result.length > 0
266266
}
267267

268+
/**
269+
* Checks if an osmId is a moderator of any team inside the org
270+
* @param {int} organizationId - organization id
271+
* @param {int} osmId - id of member we are testing
272+
*/
273+
async function isOrgTeamModerator (organizationId, osmId) {
274+
if (!organizationId) throw new PropertyRequiredError('organization id')
275+
if (!osmId) throw new PropertyRequiredError('osm id')
276+
const conn = await db()
277+
const subquery = conn('organization_team').select('team_id').where('organization_id', organizationId)
278+
const isModeratorOfAny = await conn('moderator').whereIn('team_id', subquery).debug()
279+
return isModeratorOfAny.length > 0
280+
}
281+
268282
/**
269283
* Checks if the osm user is an owner of a team
270284
* @param {int} organizationId - organization id
@@ -350,6 +364,7 @@ module.exports = {
350364
isOwner,
351365
isManager,
352366
isMember,
367+
isOrgTeamModerator,
353368
createOrgTeam,
354369
listMyOrganizations,
355370
getOrgStaff,

app/manage/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ function manageRouter (nextApp) {
221221
router.get('/api/profiles/keys/organizations/:id', can('organization:edit'), getProfileKeys('org', 'org'))
222222
router.post('/api/profiles/keys/organizations/:id', can('organization:edit'), createProfileKeys('org', 'org'))
223223

224-
router.get('/api/profiles/keys/organizations/:id/teams', can('organization:edit'), getProfileKeys('org', 'team'))
224+
router.get('/api/profiles/keys/organizations/:id/teams', can('organization:view-team-keys'), getProfileKeys('org', 'team'))
225225
router.post('/api/profiles/keys/organizations/:id/teams', can('organization:edit'), createProfileKeys('org', 'team'))
226226

227227
router.get('/api/profiles/keys/organizations/:id/users', can('organization:member'), getProfileKeys('org', 'user'))

app/manage/permissions/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ const organizationPermissions = {
2626
'organization:edit': require('./edit-org'),
2727
'organization:create-team': require('./create-org-team'),
2828
'organization:member': require('./member-org'),
29-
'organization:view-members': require('./view-org-members')
29+
'organization:view-members': require('./view-org-members'),
30+
'organization:view-team-keys': require('./view-org-team-keys')
3031
}
3132

3233
const clientPermissions = {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const { isOwner, isOrgTeamModerator } = require('../../lib/organization')
2+
3+
/**
4+
* organization:view-team-keys
5+
*
6+
* To edit an organization or delete it, the authenticated user needs
7+
* to be an owner in the organization
8+
*
9+
* @param {int} uid - user id
10+
* @param {Object} params - request parameters
11+
* @param {int} params.id - organization id
12+
* @returns {Promise<boolean>}
13+
*/
14+
async function editOrg (uid, { id }) {
15+
const teamModerator = await isOrgTeamModerator(id, uid)
16+
return teamModerator || isOwner(id, uid)
17+
}
18+
19+
module.exports = editOrg

components/edit-team-form.js

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function renderErrors (errors) {
2929
})
3030
}
3131

32-
export default function EditTeamForm ({ initialValues, onSubmit, staff, isCreateForm, extraTags = [], profileValues }) {
32+
export default function EditTeamForm ({ initialValues, onSubmit, staff, isCreateForm, orgTeamTags = [], teamTags = [], profileValues }) {
3333
if (profileValues) {
3434
initialValues.tags = {}
3535
profileValues.forEach(({ id, value }) => {
@@ -42,14 +42,34 @@ export default function EditTeamForm ({ initialValues, onSubmit, staff, isCreate
4242
onSubmit={onSubmit}
4343
render={({ status, isSubmitting, submitForm, values, errors, setFieldValue, setErrors, setStatus }) => {
4444
let uniqueOrgs
45-
let extraFields
45+
let extraOrgTeamFields = []
46+
let extraTeamFields = []
4647
if (staff && isCreateForm) {
4748
uniqueOrgs = uniqBy(prop('organization_id'), staff.map(({ name, organization_id }) => {
4849
return { name, organization_id }
4950
}))
5051
}
51-
if (extraTags.length > 0) {
52-
extraFields = extraTags.map(({ id, name, required, description }) => {
52+
if (orgTeamTags.length > 0) {
53+
extraOrgTeamFields = orgTeamTags.map(({ id, name, required, description }) => {
54+
return (
55+
<div className='form-control form-control__vertical' key={`extra-tag-${id}`}>
56+
<label htmlFor={`extra-tag-${id}`}>{name}
57+
{required ? <span className='form--required'>*</span> : ''}
58+
{description ? descriptionPopup(description) : ''}
59+
</label>
60+
<Field
61+
type='text'
62+
name={`tags.key-${id}`}
63+
required={required}
64+
value={values.tags[`key-${id}`]}
65+
/>
66+
</div>
67+
)
68+
})
69+
}
70+
71+
if (teamTags.length > 0) {
72+
extraTeamFields = teamTags.map(({ id, name, required, description }) => {
5373
return (
5474
<div className='form-control form-control__vertical' key={`extra-tag-${id}`}>
5575
<label htmlFor={`extra-tag-${id}`}>{name}
@@ -112,10 +132,17 @@ export default function EditTeamForm ({ initialValues, onSubmit, staff, isCreate
112132
)
113133
: ''
114134
}
115-
{extraTags.length > 0
135+
{extraOrgTeamFields.length > 0
116136
? <>
117137
<h2>Org Attributes</h2>
118-
{extraFields}
138+
{extraOrgTeamFields}
139+
</>
140+
: ''
141+
}
142+
{extraTeamFields.length > 0
143+
? <>
144+
<h2>Other Team Attributes</h2>
145+
{extraTeamFields}
119146
</>
120147
: ''
121148
}

lib/profiles-api.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,28 @@ export async function getOrgTeamAttributes (id) {
7070
}
7171
}
7272

73+
/**
74+
* getTeamAttributess
75+
* Get attributes for team attributes
76+
*
77+
* @returns {Array[Object]} - list of team details
78+
*/
79+
export async function getTeamAttributes (id) {
80+
let res = await fetch(join(URL, 'keys', 'teams', `${id}`), {
81+
method: 'GET',
82+
headers: {
83+
'Content-Type': 'application/json; charset=utf-8'
84+
}
85+
})
86+
if (res.status === 200) {
87+
return res.json()
88+
} else {
89+
const err = new Error('could not retrieve team member attributes')
90+
err.status = res.status
91+
throw err
92+
}
93+
}
94+
7395
/**
7496
* modifyAttribute
7597
* Modify attribute given a profile key

pages/team-edit.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import getConfig from 'next/config'
77
import EditTeamForm from '../components/edit-team-form'
88
import Button from '../components/button'
99
import theme from '../styles/theme'
10-
import { getOrgTeamAttributes, getTeamProfile } from '../lib/profiles-api'
10+
import { getOrgTeamAttributes, getTeamAttributes, getTeamProfile } from '../lib/profiles-api'
1111
const { publicRuntimeConfig } = getConfig()
1212

1313
export default class TeamEdit extends Component {
@@ -32,16 +32,18 @@ export default class TeamEdit extends Component {
3232
const { id } = this.props
3333
try {
3434
let team = await getTeam(id)
35-
let teamAttributes = []
35+
let teamAttributes = await getTeamAttributes(id) || []
36+
let orgTeamAttributes = []
3637
let profileValues = []
3738
profileValues = await getTeamProfile(id)
3839
if (team.org) {
39-
teamAttributes = await getOrgTeamAttributes(team.org.organization_id)
40+
orgTeamAttributes = await getOrgTeamAttributes(team.org.organization_id)
4041
}
4142
this.setState({
4243
team,
4344
profileValues,
4445
teamAttributes,
46+
orgTeamAttributes,
4547
loading: false
4648
})
4749
} catch (e) {
@@ -112,7 +114,7 @@ export default class TeamEdit extends Component {
112114
}
113115

114116
render () {
115-
const { team, error, teamAttributes, profileValues } = this.state
117+
const { team, error, teamAttributes, orgTeamAttributes, profileValues } = this.state
116118

117119
if (error) {
118120
if (error.status >= 400 && error.status < 500) {
@@ -142,7 +144,8 @@ export default class TeamEdit extends Component {
142144
<EditTeamForm
143145
initialValues={pick(['name', 'bio', 'hashtag', 'editing_policy', 'location', 'privacy'], team)}
144146
profileValues={profileValues}
145-
extraTags={teamAttributes}
147+
teamTags={teamAttributes}
148+
orgTeamTags={orgTeamAttributes}
146149
onSubmit={async (values, actions) => {
147150
try {
148151
let tags = Object.keys(values.tags).map(key => {

0 commit comments

Comments
 (0)