Skip to content

Commit

Permalink
implement restrictions on edit template surveys (#1496)
Browse files Browse the repository at this point in the history
* implement restrictions on edit template surveys

* updating error status
  • Loading branch information
VineetBala-AOT authored Apr 12, 2023
1 parent 20c00e0 commit 96e2587
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 9 deletions.
3 changes: 3 additions & 0 deletions met-api/src/met_api/resources/survey.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from met_api.auth import auth
from met_api.auth import jwt as _jwt
from met_api.exceptions.business_exception import BusinessException
from met_api.models.pagination_options import PaginationOptions
from met_api.models.survey_exclusion_option import SurveyExclusionOptions
from met_api.schemas.survey import SurveySchema
Expand Down Expand Up @@ -133,6 +134,8 @@ def put():
return str(err), HTTPStatus.INTERNAL_SERVER_ERROR
except ValueError as err:
return str(err), HTTPStatus.INTERNAL_SERVER_ERROR
except BusinessException as err:
return {'message': err.error}, err.status_code


@cors_preflight('PUT,OPTIONS')
Expand Down
17 changes: 17 additions & 0 deletions met-api/src/met_api/services/survey_service.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Service for survey management."""
from http import HTTPStatus

from met_api.constants.engagement_status import Status
from met_api.models import Engagement as EngagementModel
Expand All @@ -11,6 +12,8 @@
from met_api.utils.roles import Role
from met_api.utils.token_info import TokenInfo

from ..exceptions.business_exception import BusinessException


class SurveyService:
"""Survey management service."""
Expand Down Expand Up @@ -78,6 +81,12 @@ def update(cls, data: SurveySchema):
cls.validate_update_fields(data)
survey = cls.get(data.get('id', None))
engagement = survey.get('engagement', None)

# check if user has edit all surveys access to edit template surveys as well
user_roles = TokenInfo.get_user_roles()
is_template = survey.get('is_template', None)
cls.validate_template_surveys_edit_access(is_template, user_roles)

if engagement and engagement.get('status_id', None) != Status.Draft.value:
raise ValueError('Engagament already published')
return SurveyModel.update_survey(data)
Expand All @@ -90,6 +99,14 @@ def validate_update_fields(data):
if any(empty_fields):
raise ValueError('Some required fields are empty')

@staticmethod
def validate_template_surveys_edit_access(is_template, user_roles):
"""Validatef user has edit access on a template survey."""
if is_template and Role.EDIT_ALL_SURVEYS.value not in user_roles:
raise BusinessException(
error='Changes could not be saved due to restricted access on a template survey',
status_code=HTTPStatus.FORBIDDEN)

@staticmethod
def validate_create_fields(data):
"""Validate all fields."""
Expand Down
1 change: 1 addition & 0 deletions met-api/src/met_api/utils/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ class Role(Enum):
VIEW_MEMBERS = 'view_members'
EDIT_MEMBERS = 'edit_members'
VIEW_ALL_SURVEYS = 'view_all_surveys'
EDIT_ALL_SURVEYS = 'edit_all_surveys'
31 changes: 29 additions & 2 deletions met-api/tests/unit/api/test_survey.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
from met_api.utils.enums import ContentType
from tests.utilities.factory_scenarios import TestJwtClaims, TestSurveyInfo
from tests.utilities.factory_utils import (
factory_auth_header, factory_engagement_model, factory_hidden_survey_model, factory_survey_model)
factory_auth_header, factory_engagement_model, factory_hidden_survey_model, factory_survey_model,
factory_template_survey_model)


@pytest.mark.parametrize('survey_info', [TestSurveyInfo.survey2])
Expand Down Expand Up @@ -134,7 +135,7 @@ def test_get_hidden_survey_for_team_member(client, jwt, session, survey_info):
def test_get_template_survey(client, jwt, session, survey_info): # pylint:disable=unused-argument
"""Assert that a hidden survey cannot be fetched by team members."""
headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.staff_admin_role)
survey = factory_hidden_survey_model()
survey = factory_template_survey_model()
survey_id = str(survey.id)
new_survey_name = 'new_survey_name'
rv = client.put('/api/surveys/', data=json.dumps({'id': survey_id, 'name': new_survey_name}),
Expand All @@ -153,3 +154,29 @@ def test_get_template_survey(client, jwt, session, survey_info): # pylint:disab
headers=headers, content_type=ContentType.JSON.value)
assert rv.status_code == 200
assert rv.json.get('total') == 1


@pytest.mark.parametrize('survey_info', [TestSurveyInfo.survey4])
def test_edit_template_survey_for_admins(client, jwt, session, survey_info): # pylint:disable=unused-argument
"""Assert that a hidden survey cannot be fetched by team members."""
headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.staff_admin_role)
survey = factory_template_survey_model()
survey_id = str(survey.id)
new_survey_name = 'new_survey_name'
rv = client.put('/api/surveys/', data=json.dumps({'id': survey_id, 'name': new_survey_name}),
headers=headers, content_type=ContentType.JSON.value)

assert rv.status_code == 200, 'Admins are able to edit template surveys'


@pytest.mark.parametrize('survey_info', [TestSurveyInfo.survey4])
def test_edit_template_survey_for_team_member(client, jwt, session, survey_info): # pylint:disable=unused-argument
"""Assert that a hidden survey cannot be fetched by team members."""
headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.team_member_role)
survey = factory_template_survey_model()
survey_id = str(survey.id)
new_survey_name = 'new_survey_name'
rv = client.put('/api/surveys/', data=json.dumps({'id': survey_id, 'name': new_survey_name}),
headers=headers, content_type=ContentType.JSON.value)

assert rv.status_code == 403, 'Team members are not able to edit template surveys, so throws exception.'
3 changes: 2 additions & 1 deletion met-api/tests/utilities/factory_scenarios.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ class TestJwtClaims(dict, Enum):
'view_users',
'view_private_engagements',
'create_admin_user',
'view_all_surveys'
'view_all_surveys',
'edit_all_surveys',
]
}
}
Expand Down
17 changes: 17 additions & 0 deletions met-api/tests/utilities/factory_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,23 @@ def factory_hidden_survey_model(survey_info: dict = TestSurveyInfo.survey3):
return survey


def factory_template_survey_model(survey_info: dict = TestSurveyInfo.survey4):
"""Produce a survey model."""
survey = SurveyModel(
name=fake.name(),
form_json=survey_info.get('form_json'),
created_by=survey_info.get('created_by'),
updated_by=survey_info.get('updated_by'),
created_date=survey_info.get('created_date'),
updated_date=survey_info.get('updated_date'),
is_hidden=survey_info.get('is_hidden'),
is_template=survey_info.get('is_template'),
)
db.session.add(survey)
db.session.commit()
return survey


def factory_email_verification(survey_id):
"""Produce a EmailVerification model."""
email_verification = EmailVerificationModel(
Expand Down
22 changes: 16 additions & 6 deletions met-web/src/components/survey/building/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { openNotificationModal } from 'services/notificationModalService/notific
import { Palette } from 'styles/Theme';
import { PermissionsGate } from 'components/permissionsGate';
import { SCOPES } from 'components/permissionsGate/PermissionMaps';
import axios from 'axios';

const SurveyFormBuilder = () => {
const navigate = useNavigate();
Expand Down Expand Up @@ -167,12 +168,21 @@ const SurveyFormBuilder = () => {
navigate('/surveys');
} catch (error) {
setIsSaving(false);
dispatch(
openNotification({
severity: 'error',
text: 'Error occurred while saving survey',
}),
);
if (axios.isAxiosError(error)) {
dispatch(
openNotification({
severity: 'error',
text: error.response?.data.message,
}),
);
} else {
dispatch(
openNotification({
severity: 'error',
text: 'Error occurred while saving survey',
}),
);
}
}
};

Expand Down

0 comments on commit 96e2587

Please sign in to comment.