Skip to content

Commit

Permalink
Added more permission checks (#2047)
Browse files Browse the repository at this point in the history
  • Loading branch information
saravanpa-aot authored Aug 22, 2023
1 parent 4db352b commit 634ecc5
Show file tree
Hide file tree
Showing 14 changed files with 107 additions and 31 deletions.
9 changes: 4 additions & 5 deletions met-api/src/met_api/resources/engagement_members.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@

from met_api.auth import jwt as _jwt
from met_api.exceptions.business_exception import BusinessException
from met_api.schemas.memberships import MembershipSchema
from met_api.schemas.membership_engagement import MembershipEngagementSchema
from met_api.schemas.memberships import MembershipSchema
from met_api.services.membership_service import MembershipService
from met_api.utils.roles import Role
from met_api.utils.util import allowedorigins, cors_preflight

API = Namespace('engagements', description='Endpoints for Engagements Management')
Expand All @@ -39,11 +38,11 @@ class EngagementMembership(Resource):

@staticmethod
@cross_origin(origins=allowedorigins())
@_jwt.has_one_of_roles([Role.VIEW_MEMBERS.value])
@_jwt.requires_auth
def get(engagement_id):
"""Get memberships."""
# TODO validate against a schema.
try:

members = MembershipService.get_memberships(engagement_id)
return jsonify(MembershipSchema().dump(members, many=True)), HTTPStatus.OK
except BusinessException as err:
Expand Down Expand Up @@ -109,7 +108,7 @@ class RevokeMembership(Resource):

@staticmethod
@cross_origin(origins=allowedorigins())
@_jwt.has_one_of_roles([Role.EDIT_MEMBERS.value])
@_jwt.requires_auth
def patch(engagement_id, user_id):
"""Update membership status."""
try:
Expand Down
3 changes: 1 addition & 2 deletions met-api/src/met_api/resources/engagement_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
from met_api.auth import jwt as _jwt
from met_api.schemas.engagement_metadata import EngagementMetadataSchema
from met_api.services.engagement_metadata_service import EngagementMetadataService
from met_api.utils.roles import Role
from met_api.utils.token_info import TokenInfo
from met_api.utils.util import allowedorigins, cors_preflight

Expand Down Expand Up @@ -60,7 +59,7 @@ class EngagementsMetadata(Resource):

@staticmethod
@cross_origin(origins=allowedorigins())
@_jwt.has_one_of_roles([Role.CREATE_ENGAGEMENT.value])
@_jwt.requires_auth
def post():
"""Create a new engagement metadata."""
try:
Expand Down
3 changes: 1 addition & 2 deletions met-api/src/met_api/resources/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,13 @@ def get(engagement_id):
def post(engagement_id):
"""Add new widget for an engagement."""
try:
user_id = TokenInfo.get_id()
request_json = request.get_json()
valid_format, errors = schema_utils.validate(request_json, 'widget')
if not valid_format:
return {'message': schema_utils.serialize(errors)}, HTTPStatus.BAD_REQUEST

widget = WidgetSchema().load(request_json)
created_widget = WidgetService().create_widget(widget, engagement_id, user_id)
created_widget = WidgetService().create_widget(widget, engagement_id)
return created_widget, HTTPStatus.OK
except (KeyError, ValueError) as err:
return str(err), HTTPStatus.INTERNAL_SERVER_ERROR
Expand Down
2 changes: 1 addition & 1 deletion met-api/src/met_api/resources/widget_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def get(widget_id):

@staticmethod
@cross_origin(origins=allowedorigins())
@_jwt.has_one_of_roles([Role.EDIT_ENGAGEMENT.value])
@_jwt.requires_auth
def post(widget_id):
"""Create map widget."""
try:
Expand Down
5 changes: 2 additions & 3 deletions met-api/src/met_api/resources/widget_video.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
from met_api.schemas import utils as schema_utils
from met_api.schemas.widget_video import WidgetVideoSchema
from met_api.services.widget_video_service import WidgetVideoService
from met_api.utils.roles import Role
from met_api.utils.util import allowedorigins, cors_preflight


Expand All @@ -48,7 +47,7 @@ def get(widget_id):

@staticmethod
@cross_origin(origins=allowedorigins())
@_jwt.has_one_of_roles([Role.EDIT_ENGAGEMENT.value])
@_jwt.requires_auth
def post(widget_id):
"""Create video widget."""
try:
Expand All @@ -66,7 +65,7 @@ class Video(Resource):

@staticmethod
@cross_origin(origins=allowedorigins())
@_jwt.has_one_of_roles([Role.EDIT_ENGAGEMENT.value])
@_jwt.requires_auth
def patch(widget_id, video_widget_id):
"""Update video widget."""
request_json = request.get_json()
Expand Down
13 changes: 13 additions & 0 deletions met-api/src/met_api/services/engagement_metadata_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,26 @@ class EngagementMetadataService:
@staticmethod
def get_metadata(engagement_id) -> EngagementMetadataSchema:
"""Get Engagement metadata by the id."""
one_of_roles = (
MembershipType.TEAM_MEMBER.name,
Role.VIEW_ALL_ENGAGEMENTS.value
)
authorization.check_auth(one_of_roles=one_of_roles, engagement_id=engagement_id)

metadata_model: EngagementMetadataModel = EngagementMetadataModel.find_by_id(engagement_id)
metadata = EngagementMetadataSchema().dump(metadata_model)
return metadata

@staticmethod
def create_metadata(request_json: dict):
"""Create engagement metadata."""
if engagement_id := request_json.get('engagement_id', None):
one_of_roles = (
MembershipType.TEAM_MEMBER.name,
Role.CREATE_ENGAGEMENT.value
)
authorization.check_auth(one_of_roles=one_of_roles, engagement_id=engagement_id)

metadata_model = EngagementMetadataService._create_metadata_model(request_json)
metadata_model.commit()
return metadata_model.find_by_id(metadata_model.engagement_id)
Expand Down
1 change: 1 addition & 0 deletions met-api/src/met_api/services/engagement_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ def _get_scope_options(user_roles, has_team_access):
if has_edit_role:
return EngagementScopeOptions(restricted=False)

# check if user
return EngagementScopeOptions(
include_assigned=True
)
Expand Down
10 changes: 9 additions & 1 deletion met-api/src/met_api/services/engagement_settings_service.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Service for engagement settings management."""
from met_api.constants.engagement_status import SubmissionStatus
from met_api.constants.engagement_status import Status, SubmissionStatus
from met_api.constants.membership_type import MembershipType
from met_api.models.engagement import Engagement as EngagementModel
from met_api.models.engagement_settings import EngagementSettingsModel
Expand All @@ -14,6 +14,14 @@ class EngagementSettingsService:
@staticmethod
def get(engagement_id) -> EngagementSettingsSchema:
"""Get Engagement settings by the id."""
engagement_model: EngagementModel = EngagementModel.find_by_id(engagement_id)
if engagement_model.status_id in (Status.Draft.value, Status.Scheduled.value):
one_of_roles = (
MembershipType.TEAM_MEMBER.name,
Role.VIEW_ALL_ENGAGEMENTS.value
)
authorization.check_auth(one_of_roles=one_of_roles, engagement_id=engagement_id)

settings_model: EngagementSettingsModel = EngagementSettingsModel.find_by_id(engagement_id)
settings = EngagementSettingsSchema().dump(settings_model)
return settings
Expand Down
9 changes: 7 additions & 2 deletions met-api/src/met_api/services/membership_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,19 @@ def _create_membership_model(engagement_id, user_details, membership_type=Member
def get_memberships(engagement_id):
"""Get memberships by engagement id."""
# get user to be added from request json
one_of_roles = (
MembershipType.TEAM_MEMBER.name,
Role.VIEW_MEMBERS.value
)
authorization.check_auth(one_of_roles=one_of_roles, engagement_id=engagement_id)

memberships = MembershipModel.find_by_engagement(engagement_id)
return memberships

@staticmethod
def get_assigned_engagements(
user_id,
include_revoked=False,
user_id,
include_revoked=False,
):
"""Get memberships by user id."""
status = MembershipStatus.ACTIVE.value if not include_revoked else None
Expand Down
10 changes: 10 additions & 0 deletions met-api/src/met_api/services/widget_map_service.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"""Service for Widget Map management."""
from http import HTTPStatus

from met_api.constants.membership_type import MembershipType
from met_api.exceptions.business_exception import BusinessException
from met_api.models.widget_map import WidgetMap as WidgetMapModel
from met_api.services import authorization
from met_api.services.shapefile_service import ShapefileService
from met_api.utils.roles import Role


class WidgetMapService:
Expand All @@ -20,6 +23,9 @@ def create_map(widget_id, map_details: dict, shape_file):
"""Create map for the widget."""
geojson = None
map_data = dict(map_details)
eng_id = map_data.get('engagement_id')
authorization.check_auth(one_of_roles=(MembershipType.TEAM_MEMBER.name,
Role.EDIT_ENGAGEMENT.value), engagement_id=eng_id)
if shape_file:
geojson = ShapefileService().convert_to_geojson(shape_file)
map_data['geojson'] = geojson
Expand All @@ -36,6 +42,10 @@ def update_map(widget_id, request_json):
raise BusinessException(
error='Invalid widgets and map',
status_code=HTTPStatus.BAD_REQUEST)
eng_id = widget_map.engagement_id
authorization.check_auth(one_of_roles=(MembershipType.TEAM_MEMBER.name,
Role.EDIT_ENGAGEMENT.value), engagement_id=eng_id)

return WidgetMapModel.update_map(widget_id, request_json)

@staticmethod
Expand Down
41 changes: 37 additions & 4 deletions met-api/src/met_api/services/widget_service.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"""Service for widget management."""
from http import HTTPStatus

from met_api.constants.membership_type import MembershipType
from met_api.exceptions.business_exception import BusinessException
from met_api.models.widget import Widget as WidgetModel
from met_api.models.widget_item import WidgetItem
from met_api.schemas.widget import WidgetSchema
from met_api.schemas.widget_item import WidgetItemSchema
from met_api.services import authorization
from met_api.utils.roles import Role


class WidgetService:
Expand All @@ -28,10 +31,14 @@ def get_widget_items_by_widget_id(widget_id):
return widget_items

@staticmethod
def create_widget(widget_data, engagement_id, user_id):
def create_widget(widget_data, engagement_id):
"""Create widget item."""
widget_data['created_by'] = user_id
widget_data['updated_by'] = user_id
one_of_roles = (
MembershipType.TEAM_MEMBER.name,
Role.EDIT_ENGAGEMENT.value
)
authorization.check_auth(one_of_roles=one_of_roles, engagement_id=engagement_id)

if widget_data.get('engagement_id', None) != int(engagement_id):
raise ValueError('widget data has engagement id for a different engagement')

Expand All @@ -56,6 +63,12 @@ def sort_widget(engagement_id, widgets: list, user_id=None):
"""Sort widgets."""
WidgetService._validate_widget_ids(engagement_id, widgets)

one_of_roles = (
MembershipType.TEAM_MEMBER.name,
Role.EDIT_ENGAGEMENT.value
)
authorization.check_auth(one_of_roles=one_of_roles, engagement_id=engagement_id)

widget_sort_mappings = [{
'id': widget.get('id'),
'sort_index': index + 1,
Expand All @@ -72,6 +85,12 @@ def update_widget(engagement_id, widget_id: list, widget_data: dict, user_id=Non

widget_data['updated_by'] = user_id

one_of_roles = (
MembershipType.TEAM_MEMBER.name,
Role.EDIT_ENGAGEMENT.value
)
authorization.check_auth(one_of_roles=one_of_roles, engagement_id=engagement_id)

updated_widget = WidgetModel.update_widget(engagement_id, widget_id, widget_data)
return WidgetSchema().dump(updated_widget)

Expand Down Expand Up @@ -151,7 +170,14 @@ def get_widget_by_id(widget_id):

def save_widget_items_bulk(self, widget_items: list, widget_id, user_id):
"""Save widget items."""
self.get_widget_by_id(widget_id)
widget: WidgetModel = self.get_widget_by_id(widget_id)

one_of_roles = (
MembershipType.TEAM_MEMBER.name,
Role.EDIT_ENGAGEMENT.value
)

authorization.check_auth(one_of_roles=one_of_roles, engagement_id=widget.engagement_id)

widget_items_db = WidgetItem.get_widget_items_by_widget_id(widget_id)

Expand All @@ -163,6 +189,13 @@ def save_widget_items_bulk(self, widget_items: list, widget_id, user_id):
@staticmethod
def delete_widget(engagement_id, widget_id):
"""Remove widget from engagement."""
one_of_roles = (
MembershipType.TEAM_MEMBER.name,
Role.EDIT_ENGAGEMENT.value
)

authorization.check_auth(one_of_roles=one_of_roles, engagement_id=engagement_id)

widgets = WidgetModel.remove_widget(engagement_id, widget_id)
if not widgets:
raise ValueError('Widget to remove was not found')
Expand Down
10 changes: 10 additions & 0 deletions met-api/src/met_api/services/widget_video_service.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
"""Service for Widget Video management."""
from met_api.constants.membership_type import MembershipType
from met_api.models.widget_video import WidgetVideo as WidgetVideoModel
from met_api.services import authorization
from met_api.utils.roles import Role


class WidgetVideoService:
Expand All @@ -15,6 +18,10 @@ def get_video(widget_id):
def create_video(widget_id, video_details: dict):
"""Create video for the widget."""
video_data = dict(video_details)
eng_id = video_data.get('engagement_id')
authorization.check_auth(one_of_roles=(MembershipType.TEAM_MEMBER.name,
Role.EDIT_ENGAGEMENT.value), engagement_id=eng_id)

widget_video = WidgetVideoService._create_video_model(widget_id, video_data)
widget_video.commit()
return widget_video
Expand All @@ -23,6 +30,9 @@ def create_video(widget_id, video_details: dict):
def update_video(widget_id, video_widget_id, video_data):
"""Update video widget."""
widget_video: WidgetVideoModel = WidgetVideoModel.find_by_id(video_widget_id)
authorization.check_auth(one_of_roles=(MembershipType.TEAM_MEMBER.name,
Role.EDIT_ENGAGEMENT.value), engagement_id=widget_video.engagement_id)

if not widget_video:
raise KeyError('Video widget not found')

Expand Down
10 changes: 5 additions & 5 deletions met-api/tests/unit/api/test_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def test_create_widget(client, jwt, session, widget_info): # pylint:disable=unu
"""Assert that a widget can be POSTed."""
engagement = factory_engagement_model()
widget_info['engagement_id'] = engagement.id
headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.no_role)
headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.staff_admin_role)
rv = client.post('/api/widgets/engagement/' + str(engagement.id), data=json.dumps(widget_info),
headers=headers, content_type=ContentType.JSON.value)
assert rv.status_code == 200
Expand All @@ -48,14 +48,14 @@ def test_create_widget_sort(client, jwt, session): # pylint:disable=unused-argu
engagement = factory_engagement_model()
widget_info_1 = TestWidgetInfo.widget1
widget_info_1['engagement_id'] = engagement.id
headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.no_role)
headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.staff_admin_role)
rv = client.post(f'/api/widgets/engagement/{engagement.id}', data=json.dumps(widget_info_1),
headers=headers, content_type=ContentType.JSON.value)
assert rv.status_code == 200

widget_info_2 = TestWidgetInfo.widget2
widget_info_2['engagement_id'] = engagement.id
headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.no_role)
headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.staff_admin_role)
rv = client.post(f'/api/widgets/engagement/{engagement.id}', data=json.dumps(widget_info_2),
headers=headers, content_type=ContentType.JSON.value)
assert rv.status_code == 200
Expand Down Expand Up @@ -101,7 +101,7 @@ def test_create_widget_sort_invalid(client, jwt, session): # pylint:disable=unu
engagement = factory_engagement_model()
widget_info_1 = TestWidgetInfo.widget1
widget_info_1['engagement_id'] = engagement.id
headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.no_role)
headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.staff_admin_role)
rv = client.post(f'/api/widgets/engagement/{engagement.id}', data=json.dumps(widget_info_1),
headers=headers, content_type=ContentType.JSON.value)
assert rv.status_code == 200
Expand Down Expand Up @@ -132,7 +132,7 @@ def test_create_widget_items(client, jwt, session, widget_item_info): # pylint:
TestWidgetInfo.widget1['engagement_id'] = engagement.id
widget = factory_widget_model(TestWidgetInfo.widget1)

headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.no_role)
headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.staff_admin_role)

data = {
'widget_data_id': widget_item_info.get('widget_data_id'),
Expand Down
Loading

0 comments on commit 634ecc5

Please sign in to comment.