diff --git a/src/containers/Challenges/index.js b/src/containers/Challenges/index.js index e413cd79..7de33ffb 100644 --- a/src/containers/Challenges/index.js +++ b/src/containers/Challenges/index.js @@ -20,7 +20,7 @@ import { setActiveProject, resetSidebarActiveParams } from '../../actions/sidebar' -import { checkAdmin, checkIsUserInvitedToProject } from '../../util/tc' +import { checkAdmin, checkCopilot, checkIsProjectMember, checkIsUserInvitedToProject } from '../../util/tc' import { withRouter } from 'react-router-dom' import { getActiveProject } from './helper' @@ -126,8 +126,10 @@ class Challenges extends Component { filterProjectOption, projects, activeProjectId, + projectId, status, projectDetail: reduxProjectInfo, + hasProjectAccess, loadChallengesByPage, page, perPage, @@ -158,13 +160,26 @@ class Challenges extends Component { activeProjectId ) + // Check if user has access to this specific project + const isProjectRoute = !!projectId && !dashboard && !selfService + const isProjectDetailLoaded = reduxProjectInfo && `${reduxProjectInfo.id}` === `${projectId}` + const isAdmin = checkAdmin(auth.token) + const isCopilot = checkCopilot(auth.token) + const isProjectMember = isProjectDetailLoaded && checkIsProjectMember(auth.token, reduxProjectInfo) + const canAccessProject = isAdmin || isCopilot || isProjectMember || hasProjectAccess + + // Show access denied message if user cannot access this project + const accessDeniedMessage = isProjectRoute && isProjectDetailLoaded && !canAccessProject + ? "You don't have access to this project. Please contact support@topcoder.com." + : warnMessage + return ( {(dashboard || activeProjectId !== -1 || selfService) && ( ({ activeProjectId: sidebar.activeProjectId, projects: sidebar.projects, projectDetail: projects.projectDetail, + hasProjectAccess: projects.hasProjectAccess, isBillingAccountExpired: projects.isBillingAccountExpired, billingStartDate: projects.billingStartDate, billingEndDate: projects.billingEndDate, diff --git a/src/containers/ProjectEntry/index.js b/src/containers/ProjectEntry/index.js index 1215b497..24d1ac3b 100644 --- a/src/containers/ProjectEntry/index.js +++ b/src/containers/ProjectEntry/index.js @@ -15,6 +15,7 @@ import { checkIsUserInvitedToProject } from '../../util/tc' * the invitation modal before challenge-specific requests run. */ const ProjectEntry = ({ + hasProjectAccess, history, isProjectLoading, loadOnlyProjectInfo, @@ -24,6 +25,7 @@ const ProjectEntry = ({ }) => { const projectId = _.get(match, 'params.projectId') const [resolvedProjectId, setResolvedProjectId] = useState(null) + const [accessDenied, setAccessDenied] = useState(false) useEffect(() => { let isActive = true @@ -34,15 +36,22 @@ const ProjectEntry = ({ } setResolvedProjectId(null) + setAccessDenied(false) loadOnlyProjectInfo(projectId) .then(() => { if (isActive) { setResolvedProjectId(projectId) } }) - .catch(() => { + .catch((error) => { if (isActive) { - history.replace('/projects') + const status = _.get(error, 'payload.response.status', _.get(error, 'response.status')) + if (status === 403) { + setAccessDenied(true) + setResolvedProjectId(projectId) + } else { + history.replace('/projects') + } } }) @@ -52,11 +61,17 @@ const ProjectEntry = ({ }, [history, loadOnlyProjectInfo, projectId]) useEffect(() => { - if ( - !resolvedProjectId || - isProjectLoading || - `${_.get(projectDetail, 'id', '')}` !== `${resolvedProjectId}` - ) { + if (!resolvedProjectId || isProjectLoading) { + return + } + + // Handle 403 access denied - redirect to challenges page which will show the error + if (accessDenied || !hasProjectAccess) { + history.replace(`/projects/${resolvedProjectId}/challenges`) + return + } + + if (`${_.get(projectDetail, 'id', '')}` !== `${resolvedProjectId}`) { return } @@ -65,12 +80,13 @@ const ProjectEntry = ({ : `/projects/${resolvedProjectId}/challenges` history.replace(destination) - }, [history, isProjectLoading, projectDetail, resolvedProjectId, token]) + }, [accessDenied, hasProjectAccess, history, isProjectLoading, projectDetail, resolvedProjectId, token]) return } ProjectEntry.propTypes = { + hasProjectAccess: PropTypes.bool, history: PropTypes.shape({ replace: PropTypes.func.isRequired }).isRequired, @@ -86,6 +102,7 @@ ProjectEntry.propTypes = { } const mapStateToProps = ({ auth, projects }) => ({ + hasProjectAccess: projects.hasProjectAccess, isProjectLoading: projects.isLoading, projectDetail: projects.projectDetail, token: auth.token