Skip to content

Commit

Permalink
adding system for managing permissions to changing simulators specs
Browse files Browse the repository at this point in the history
  • Loading branch information
jonrkarr committed Mar 27, 2021
1 parent 82ca38c commit 2eab5a4
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 8 deletions.
2 changes: 1 addition & 1 deletion biosimulators_test_suite/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.1.26'
__version__ = '0.1.27'
80 changes: 78 additions & 2 deletions biosimulators_test_suite/exec_gh_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def run(self):
self.reset_issue_labels(self.issue_number, [IssueLabel.validated.value, IssueLabel.invalid.value, IssueLabel.action_error.value])

# get specifications of simulator and validate simulator
specifications, test_results = self.exec_core(submission)
specifications, test_results = self.exec_core(submission, submitter)

# label issue as validated
self.add_labels_to_issue(self.issue_number, [IssueLabel.validated.value])
Expand Down Expand Up @@ -172,14 +172,15 @@ def get_initial_message(self, submission, submitter):
'A complete log of your simulator {} job will be available [here]({}).{}\n\n'
).format(submitter, actions, not_actions, job_type, self.get_gh_action_run_url(), test_results_msg)

def exec_core(self, submission):
def exec_core(self, submission, submitter):
""" Validate simulator
* Validate specifications
* Validate image
Args:
submission (:obj:`SimulatorSubmission`): simulator submission
submitter (:obj:`str`): GitHub id of the submitter
Returns:
:obj:`tuple`:
Expand All @@ -190,6 +191,11 @@ def exec_core(self, submission):
# validate specifications
specifications = biosimulators_utils.simulator.io.read_simulator_specs(
submission.specifications_url, submission.specifications_patch)

# check permissions
self.validate_permissions(specifications['id'], specifications['name'], submitter)

# indicate that specifications are valid
self.add_comment_to_issue(self.issue_number, 'The specifications of your simulator is valid!')

# validate image
Expand All @@ -202,6 +208,76 @@ def exec_core(self, submission):
# return specifications
return specifications, test_results

def validate_permissions(self, simulator_id, simulator_name, submitter):
""" Validate that the submitter has permissions to submit or update the simulator
Args:
simulator_id (:obj:`str`): simulator id
simulator_name (:obj:`str`): simulator name
submitter (:obj:`str`): GitHub id of the submitter
"""
# check if team exists
response = requests.get(
'https://api.github.com/orgs/biosimulators/teams/' + simulator_id,
auth=self.get_gh_auth())
try:
response.raise_for_status()
has_team = True
except requests.exceptions.HTTPError:
if response.status_code == 404:
has_team = False
else:
raise

# create team if none exists and add submitter as a maintainer of the team
if not has_team:
# get parent team
response = requests.get(
'https://api.github.com/orgs/biosimulators/teams/simulator-developers',
auth=self.get_gh_auth())
response.raise_for_status()
base_team_id = response.json()['id']

# create team
response = requests.post(
'https://api.github.com/orgs/biosimulators/teams',
auth=self.get_gh_auth(), json={
'name': simulator_id,
'description': 'Developers of ' + simulator_name,
'parent_team_id': base_team_id,
'maintainers': [submitter],
})
response.raise_for_status()

# tell user a group was created
msg = (
'We created the GitHub group @biosimulators/{} to manage permissions to change the specifications of {} '
'and added you (@{}) to this group. You can manage permissions to change the specifications of {} at '
'https://github.com/orgs/biosimulators/teams/{}/members.'
).format(simulator_id, simulator_name, submitter, simulator_name, simulator_id)
self.add_comment_to_issue(self.issue_number, msg)

# check submitter has permissions to team
else:
response = requests.get(
'https://api.github.com/orgs/biosimulators/teams/{}/memberships/{}'.format(simulator_id, submitter),
auth=self.get_gh_auth())
try:
response.raise_for_status()
has_permissions = True
except requests.exceptions.HTTPError:
if response.status_code == 404:
has_permissions = False
else:
raise

if not has_permissions:
msg = (
'You (@{}) do not have permissions to update the specifications of {}. Only the members of @biosimulators/{} '
'can update the specifications of {}. Please contact the members of this group to request permissions to update {}.'
).format(submitter, simulator_name, simulator_id, simulator_name, simulator_name)
self.add_error_comment_to_issue(self.issue_number, [Comment(text=msg, error=True)])

def validate_image(self, specifications):
""" Validate a Docker image for simulation tool
Expand Down
104 changes: 99 additions & 5 deletions tests/test_exec_gh_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ def test_validate_image(self):

specs = {
'id': 'tellurium',
'name': 'tellurium',
'image': {
'url': 'ghcr.io/biosimulators/biosimulators_tellurium/tellurium:2.1.6',
},
Expand Down Expand Up @@ -168,11 +169,12 @@ def post(self, url, json=None, auth=None, headers=None):

requests_mock = RequestMock(parent=self)

specs = {'id': 'tellurium'}
specs = {'id': 'tellurium', 'name': 'tellurium'}
with mock.patch('biosimulators_utils.simulator.io.read_simulator_specs', return_value=specs):
with mock.patch.object(exec_gh_action.ValidateCommitSimulatorGitHubAction, 'validate_image', return_value=None):
with mock.patch('requests.post', side_effect=requests_mock.post):
specs2, results = action.exec_core(self.submission)
with mock.patch.object(exec_gh_action.ValidateCommitSimulatorGitHubAction, 'validate_permissions', return_value=None):
with mock.patch('requests.post', side_effect=requests_mock.post):
specs2, results = action.exec_core(self.submission, 'submitter')
self.assertEqual(specs2, specs)
self.assertEqual(results, None)
self.assertEqual(requests_mock.n_post, 2)
Expand Down Expand Up @@ -672,6 +674,7 @@ def get(self, url, json=None, auth=None, headers=None):
if url == 'https://raw.githubusercontent.com/biosimulators/Biosimulators_tellurium/d08f33/biosimulators.json':
response = {
'id': 'tellurium',
'name': 'tellurium',
'version': self.submitted_version,
'image': {
'url': 'ghcr.io/biosimulators/Biosimulators_tellurium/tellurium:' + self.submitted_version
Expand Down Expand Up @@ -867,8 +870,9 @@ def _exec_run_mock_objs(self, requests_mock, docker_mock, validation_run_results
with mock.patch.object(exec_core.SimulatorValidator, 'find_cases', return_value=cases):
with mock.patch.object(exec_core.SimulatorValidator, 'eval_case',
side_effect=validation_run_results):
action = exec_gh_action.ValidateCommitSimulatorGitHubAction()
action.run()
with mock.patch.object(exec_gh_action.ValidateCommitSimulatorGitHubAction, 'validate_permissions', return_value=None):
action = exec_gh_action.ValidateCommitSimulatorGitHubAction()
action.run()

def test_add_comment_to_issue(self):
def requests_post(url, headers=None, auth=None, json=None):
Expand Down Expand Up @@ -928,3 +932,93 @@ def raise_for_status():
with mock.patch('requests.post', side_effect=RequestsPost().requests_post):
exec_gh_action.ValidateCommitSimulatorGitHubAction.add_comment_to_issue(
None, 'message', alternative_comment='alt', max_len=5)

def test_validate_permissions(self):
action = exec_gh_action.ValidateCommitSimulatorGitHubAction()

class RequestsMock(object):
def __init__(self, check_team_error=False, check_member_error=False):
self.check_team_error = check_team_error
self.check_member_error = check_member_error
self.new_team = False
self.comment = None
self.error = None

def get(self, url, auth=None):
if url == 'https://api.github.com/orgs/biosimulators/teams/simulator-developers':
return mock.Mock(raise_for_status=lambda: None, json=lambda: {'id': 10})
elif url == 'https://api.github.com/orgs/biosimulators/teams/tellurium':
return mock.Mock(raise_for_status=lambda: None, json=lambda: {'id': 20})
elif url == 'https://api.github.com/orgs/biosimulators/teams/tellurium2':
def raise_for_status():
raise requests.exceptions.HTTPError('404')
return mock.Mock(status_code=500 if self.check_team_error else 404, raise_for_status=raise_for_status)

elif url == 'https://api.github.com/orgs/biosimulators/teams/tellurium/memberships/te-owner':
return mock.Mock(raise_for_status=lambda: None, json=lambda: {})
elif url == 'https://api.github.com/orgs/biosimulators/teams/tellurium/memberships/te-owner2':
def raise_for_status():
raise requests.exceptions.HTTPError('404')
return mock.Mock(status_code=500 if self.check_member_error else 404, raise_for_status=raise_for_status)

def post(self, url, auth=None, json=None):
self.new_team = True
return mock.Mock(raise_for_status=lambda: None)

def add_comment(self, isse_number, comment):
self.comment = comment

def add_error(self, isse_number, comment):
self.error = comment

requests_mock = RequestsMock(check_team_error=False, check_member_error=False)
with mock.patch('requests.get', requests_mock.get):
with mock.patch('requests.post', requests_mock.post):
with mock.patch.object(exec_gh_action.ValidateCommitSimulatorGitHubAction, 'add_comment_to_issue', requests_mock.add_comment):
with mock.patch.object(exec_gh_action.ValidateCommitSimulatorGitHubAction, 'add_error_comment_to_issue', requests_mock.add_error):
action.validate_permissions('tellurium', 'tellurium', 'te-owner')
self.assertEqual(requests_mock.new_team, False)
self.assertEqual(requests_mock.comment, None)
self.assertEqual(requests_mock.error, None)

requests_mock = RequestsMock(check_team_error=False, check_member_error=False)
with mock.patch('requests.get', requests_mock.get):
with mock.patch('requests.post', requests_mock.post):
with mock.patch.object(exec_gh_action.ValidateCommitSimulatorGitHubAction, 'add_comment_to_issue', requests_mock.add_comment):
with mock.patch.object(exec_gh_action.ValidateCommitSimulatorGitHubAction, 'add_error_comment_to_issue', requests_mock.add_error):
action.validate_permissions('tellurium2', 'tellurium', 'te-owner')
self.assertEqual(requests_mock.new_team, True)
self.assertNotEqual(requests_mock.comment, None)
self.assertEqual(requests_mock.error, None)

requests_mock = RequestsMock(check_team_error=True, check_member_error=False)
with mock.patch('requests.get', requests_mock.get):
with mock.patch('requests.post', requests_mock.post):
with mock.patch.object(exec_gh_action.ValidateCommitSimulatorGitHubAction, 'add_comment_to_issue', requests_mock.add_comment):
with mock.patch.object(exec_gh_action.ValidateCommitSimulatorGitHubAction, 'add_error_comment_to_issue', requests_mock.add_error):
with self.assertRaisesRegex(requests.exceptions.HTTPError, '404'):
action.validate_permissions('tellurium2', 'tellurium', 'te-owner')
self.assertEqual(requests_mock.new_team, False)
self.assertEqual(requests_mock.comment, None)
self.assertEqual(requests_mock.error, None)

requests_mock = RequestsMock(check_team_error=False, check_member_error=False)
with mock.patch('requests.get', requests_mock.get):
with mock.patch('requests.post', requests_mock.post):
with mock.patch.object(exec_gh_action.ValidateCommitSimulatorGitHubAction, 'add_comment_to_issue', requests_mock.add_comment):
with mock.patch.object(exec_gh_action.ValidateCommitSimulatorGitHubAction, 'add_error_comment_to_issue', requests_mock.add_error):
action.validate_permissions('tellurium', 'tellurium', 'te-owner2')
self.assertEqual(requests_mock.new_team, False)
self.assertEqual(requests_mock.comment, None)
self.assertNotEqual(requests_mock.error, None)

requests_mock = RequestsMock(check_team_error=False, check_member_error=True)
with mock.patch('requests.get', requests_mock.get):
with mock.patch('requests.post', requests_mock.post):
with mock.patch.object(exec_gh_action.ValidateCommitSimulatorGitHubAction, 'add_comment_to_issue', requests_mock.add_comment):
with mock.patch.object(exec_gh_action.ValidateCommitSimulatorGitHubAction, 'add_error_comment_to_issue', requests_mock.add_error):
with self.assertRaisesRegex(requests.exceptions.HTTPError, '404'):
action.validate_permissions('tellurium', 'tellurium', 'te-owner2')
self.assertEqual(requests_mock.new_team, False)
self.assertEqual(requests_mock.comment, None)
self.assertEqual(requests_mock.error, None)

0 comments on commit 2eab5a4

Please sign in to comment.