Skip to content

Commit 46802ae

Browse files
author
vikasrohit
authored
Merge pull request #861 from topcoder-platform/feature/git-558-explict-access-check
feat: git#558 - Unauthorize Copilot Can Access Challenges
2 parents e824c82 + b4f3dbb commit 46802ae

File tree

7 files changed

+60
-33
lines changed

7 files changed

+60
-33
lines changed

src/actions/challenges.js

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ import {
2828
UPLOAD_ATTACHMENT_FAILURE,
2929
UPLOAD_ATTACHMENT_PENDING,
3030
UPLOAD_ATTACHMENT_SUCCESS,
31-
LOAD_CHALLENGE_RESOURCES_PENDING,
32-
LOAD_CHALLENGE_RESOURCES_SUCCESS,
33-
LOAD_CHALLENGE_RESOURCES_FAILURE,
3431
CREATE_CHALLENGE_RESOURCE,
3532
DELETE_CHALLENGE_RESOURCE,
3633
REMOVE_ATTACHMENT,
@@ -40,7 +37,8 @@ import {
4037
UPDATE_CHALLENGE_DETAILS_FAILURE,
4138
CREATE_CHALLENGE_PENDING,
4239
CREATE_CHALLENGE_SUCCESS,
43-
CREATE_CHALLENGE_FAILURE
40+
CREATE_CHALLENGE_FAILURE,
41+
LOAD_CHALLENGE_RESOURCES
4442
} from '../config/constants'
4543
import { loadProject } from './projects'
4644

@@ -396,27 +394,11 @@ export function loadChallengeTerms () {
396394
}
397395

398396
export function loadResources (challengeId) {
399-
return async (dispatch) => {
400-
dispatch({
401-
type: LOAD_CHALLENGE_RESOURCES_PENDING,
402-
challengeResources: {}
403-
})
404-
397+
return (dispatch, getState) => {
405398
if (challengeId) {
406-
return fetchResources(challengeId).then((resources) => {
407-
dispatch({
408-
type: LOAD_CHALLENGE_RESOURCES_SUCCESS,
409-
challengeResources: resources
410-
})
411-
}).catch(() => {
412-
dispatch({
413-
type: LOAD_CHALLENGE_RESOURCES_FAILURE
414-
})
415-
})
416-
} else {
417-
dispatch({
418-
type: LOAD_CHALLENGE_RESOURCES_SUCCESS,
419-
challengeResources: null
399+
return dispatch({
400+
type: LOAD_CHALLENGE_RESOURCES,
401+
payload: fetchResources(challengeId)
420402
})
421403
}
422404
}

src/components/ChallengeEditor/ChallengeView/index.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,16 @@ import PhaseInput from '../../PhaseInput'
2121
import LegacyLinks from '../../LegacyLinks'
2222
import AssignedMemberField from '../AssignedMember-Field'
2323

24-
const ChallengeView = ({ projectDetail, challenge, metadata, challengeResources, token, isLoading, challengeId, assignedMemberDetails }) => {
24+
const ChallengeView = ({
25+
projectDetail,
26+
challenge,
27+
metadata,
28+
challengeResources,
29+
token,
30+
isLoading,
31+
challengeId,
32+
assignedMemberDetails,
33+
enableEdit }) => {
2534
const selectedType = _.find(metadata.challengeTypes, { id: challenge.typeId })
2635
const challengeTrack = _.find(metadata.challengeTracks, { id: challenge.trackId })
2736

@@ -64,7 +73,7 @@ const ChallengeView = ({ projectDetail, challenge, metadata, challengeResources,
6473
</div>
6574
<div className={styles.title}>View Details</div>
6675
<div className={cn(styles.actionButtons, styles.button, styles.actionButtonsRight)}>
67-
<PrimaryButton text={'Edit'} type={'info'} submit link={`./edit`} />
76+
{ enableEdit && <PrimaryButton text={'Edit'} type={'info'} submit link={`./edit`} /> }
6877
<PrimaryButton text={'Back'} type={'info'} submit link={`..`} />
6978
</div>
7079
<div className={styles.container}>
@@ -202,7 +211,8 @@ ChallengeView.propTypes = {
202211
isLoading: PropTypes.bool.isRequired,
203212
challengeId: PropTypes.string.isRequired,
204213
challengeResources: PropTypes.arrayOf(PropTypes.object),
205-
assignedMemberDetails: PropTypes.shape()
214+
assignedMemberDetails: PropTypes.shape(),
215+
enableEdit: PropTypes.bool
206216
}
207217

208218
export default withRouter(ChallengeView)

src/config/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export const UPLOAD_ATTACHMENT_PENDING = 'UPLOAD_ATTACHMENT_PENDING'
6666
export const UPLOAD_ATTACHMENT_FAILURE = 'UPLOAD_ATTACHMENT_FAILURE'
6767
export const UPLOAD_ATTACHMENT_SUCCESS = 'UPLOAD_ATTACHMENT_SUCCESS'
6868

69+
export const LOAD_CHALLENGE_RESOURCES = 'LOAD_CHALLENGE_RESOURCES'
6970
export const LOAD_CHALLENGE_RESOURCES_SUCCESS = 'LOAD_CHALLENGE_RESOURCES_SUCCESS'
7071
export const LOAD_CHALLENGE_RESOURCES_PENDING = 'LOAD_CHALLENGE_RESOURCES_PENDING'
7172
export const LOAD_CHALLENGE_RESOURCES_FAILURE = 'LOAD_CHALLENGE_RESOURCES_FAILURE'
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@import "../../styles/includes";
2+
3+
4+
.errorContainer {
5+
color: $red;
6+
padding: 10px;
7+
}

src/containers/ChallengeEditor/index.js

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { withRouter, Route } from 'react-router-dom'
55
import ChallengeEditorComponent from '../../components/ChallengeEditor'
66
import ChallengeViewComponent from '../../components/ChallengeEditor/ChallengeView'
77
import Loader from '../../components/Loader'
8+
import styles from './ChallengeEditor.module.scss'
89

910
import {
1011
loadTimelineTemplates,
@@ -113,10 +114,21 @@ class ChallengeEditor extends Component {
113114
loadChallengeDetails(projectId, challengeId)
114115
}
115116

117+
isEditable () {
118+
const { hasProjectAccess, metadata: { resourceRoles }, challengeResources, loggedInUser } = this.props
119+
if (!hasProjectAccess) {
120+
return false
121+
}
122+
const userRoles = _.filter(challengeResources, cr => cr.memberId === `${loggedInUser.userId}`)
123+
const userResourceRoles = _.filter(resourceRoles, rr => _.some(userRoles, ur => ur.roleId === rr.id))
124+
return _.some(userResourceRoles, urr => urr.fullAccess && urr.isActive)
125+
}
126+
116127
render () {
117128
const {
118129
match,
119130
isLoading,
131+
isProjectLoading,
120132
challengeDetails,
121133
challengeResources,
122134
metadata,
@@ -132,6 +144,7 @@ class ChallengeEditor extends Component {
132144
replaceResourceInRole
133145
// members
134146
} = this.props
147+
if (isProjectLoading || isLoading) return <Loader />
135148
const challengeId = _.get(match.params, 'challengeId', null)
136149
if (challengeId && (!challengeDetails || !challengeDetails.id)) {
137150
return (<Loader />)
@@ -144,6 +157,7 @@ class ChallengeEditor extends Component {
144157
handle: submitters[0].memberHandle
145158
}
146159
}
160+
const enableEdit = this.isEditable()
147161
return <div>
148162
<Route
149163
exact
@@ -171,7 +185,8 @@ class ChallengeEditor extends Component {
171185
/>
172186
))
173187
} />
174-
<Route
188+
{ !enableEdit && <div className={styles.errorContainer}>You don't have access to edit the challenge</div>}
189+
{ enableEdit && <Route
175190
exact
176191
path={`${this.props.match.path}/edit`}
177192
render={({ match }) => ((
@@ -196,6 +211,7 @@ class ChallengeEditor extends Component {
196211
/>
197212
))
198213
} />
214+
}
199215
<Route
200216
exact
201217
path={`${this.props.match.path}/view`}
@@ -209,6 +225,7 @@ class ChallengeEditor extends Component {
209225
token={token}
210226
challengeId={challengeId}
211227
assignedMemberDetails={assignedMemberDetails}
228+
enableEdit={enableEdit}
212229
/>
213230
))
214231
} />
@@ -237,6 +254,8 @@ ChallengeEditor.propTypes = {
237254
loadResourceRoles: PropTypes.func,
238255
challengeResources: PropTypes.arrayOf(PropTypes.object),
239256
challengeDetails: PropTypes.object,
257+
isProjectLoading: PropTypes.bool,
258+
hasProjectAccess: PropTypes.bool,
240259
projectDetail: PropTypes.object,
241260
// history: PropTypes.object,
242261
metadata: PropTypes.shape({
@@ -246,6 +265,7 @@ ChallengeEditor.propTypes = {
246265
createAttachment: PropTypes.func,
247266
attachments: PropTypes.arrayOf(PropTypes.shape()),
248267
token: PropTypes.string,
268+
loggedInUser: PropTypes.object,
249269
removeAttachment: PropTypes.func,
250270
failedToLoad: PropTypes.bool,
251271
loadMemberDetails: PropTypes.func,
@@ -256,14 +276,17 @@ ChallengeEditor.propTypes = {
256276
// members: PropTypes.arrayOf(PropTypes.shape())
257277
}
258278

259-
const mapStateToProps = ({ projects: { projectDetail }, challenges: { challengeDetails, challengeResources, metadata, isLoading, attachments, failedToLoad }, auth: { token }, members: { members } }) => ({
279+
const mapStateToProps = ({ projects, challenges: { challengeDetails, challengeResources, metadata, isLoading, attachments, failedToLoad }, auth: { token, user }, members: { members } }) => ({
260280
challengeDetails,
261-
projectDetail,
281+
hasProjectAccess: projects.hasProjectAccess,
282+
projectDetail: projects.projectDetail,
262283
challengeResources,
263284
metadata,
264285
isLoading,
286+
isProjectLoading: projects.isLoading,
265287
attachments,
266288
token,
289+
loggedInUser: user,
267290
failedToLoad
268291
// members
269292
})

src/reducers/challenges.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ export default function (state = initialState, action) {
166166
case LOAD_CHALLENGE_RESOURCES_SUCCESS:
167167
return {
168168
...state,
169-
challengeResources: action.challengeResources,
169+
challengeResources: action.payload,
170170
isLoading: false,
171171
failedToLoad: false
172172
}

src/reducers/projects.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
22
* Reducer to process actions related to project
33
*/
4+
import _ from 'lodash'
45
import {
56
LOAD_PROJECT_DETAILS_FAILURE,
67
LOAD_PROJECT_DETAILS_PENDING,
@@ -16,12 +17,15 @@ export default function (state = initialState, action) {
1617
switch (action.type) {
1718
case LOAD_PROJECT_DETAILS_PENDING:
1819
return { ...state, isLoading: true }
19-
case LOAD_PROJECT_DETAILS_FAILURE:
20-
return { ...state, isLoading: false }
20+
case LOAD_PROJECT_DETAILS_FAILURE: {
21+
const status = _.get(action, 'payload.response.status', 500)
22+
return { ...state, isLoading: false, hasProjectAccess: status !== 403 }
23+
}
2124
case LOAD_PROJECT_DETAILS_SUCCESS:
2225
return {
2326
...state,
2427
projectDetail: action.payload,
28+
hasProjectAccess: true,
2529
isLoading: false
2630
}
2731
default:

0 commit comments

Comments
 (0)