Skip to content

Commit

Permalink
Web APIs for Required Signoffs (#218). r=nthomas,jlorenzo
Browse files Browse the repository at this point in the history
  • Loading branch information
bhearsum committed Feb 21, 2017
1 parent a2f13d3 commit fd03557
Show file tree
Hide file tree
Showing 18 changed files with 1,712 additions and 163 deletions.
41 changes: 40 additions & 1 deletion auslib/admin/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,27 @@
SpecificPermissionView, UserRolesView, UserRoleView, AllRolesView, \
PermissionScheduledChangesView, PermissionScheduledChangeView, \
EnactPermissionScheduledChangeView, PermissionScheduledChangeHistoryView, \
PermissionScheduledChangeSignoffsView
PermissionScheduledChangeSignoffsView, SpecificUserView
from auslib.admin.views.releases import SingleLocaleView, \
SingleReleaseView, ReleaseHistoryView, \
ReleasesAPIView, SingleReleaseColumnView, ReleaseReadOnlyView, \
ReleaseScheduledChangesView, ReleaseScheduledChangeView, \
EnactReleaseScheduledChangeView, ReleaseScheduledChangeHistoryView, \
ReleaseScheduledChangeSignoffsView, ReleaseFieldView, ReleaseDiffView
from auslib.admin.views.required_signoffs import ProductRequiredSignoffsView, \
ProductRequiredSignoffsHistoryAPIView, \
ProductRequiredSignoffsScheduledChangesView, \
ProductRequiredSignoffScheduledChangeView, \
EnactProductRequiredSignoffScheduledChangeView, \
ProductRequiredSignoffScheduledChangeSignoffsView, \
ProductRequiredSignoffScheduledChangeHistoryView, \
PermissionsRequiredSignoffsView, \
PermissionsRequiredSignoffsHistoryAPIView, \
PermissionsRequiredSignoffsScheduledChangesView, \
PermissionsRequiredSignoffScheduledChangeView, \
EnactPermissionsRequiredSignoffScheduledChangeView, \
PermissionsRequiredSignoffScheduledChangeSignoffsView, \
PermissionsRequiredSignoffScheduledChangeHistoryView
from auslib.admin.views.rules import RulesAPIView, \
SingleRuleView, RuleHistoryAPIView, SingleRuleColumnView, \
RuleScheduledChangesView, RuleScheduledChangeView, \
Expand Down Expand Up @@ -78,6 +92,7 @@ def add_security_headers(response):
app.add_url_rule("/csrf_token", view_func=CSRFView.as_view("csrf"))
app.add_url_rule("/users", view_func=UsersView.as_view("users"))
app.add_url_rule("/users/roles", view_func=AllRolesView.as_view("all_users_roles"))
app.add_url_rule("/users/<username>", view_func=SpecificUserView.as_view("specific_user"))
app.add_url_rule("/users/<username>/permissions", view_func=PermissionsView.as_view("user_permissions"))
app.add_url_rule("/users/<username>/permissions/<permission>", view_func=SpecificPermissionView.as_view("specific_permission"))
app.add_url_rule("/users/<username>/roles", view_func=UserRolesView.as_view("user_roles"))
Expand All @@ -94,6 +109,11 @@ def add_security_headers(response):
app.add_url_rule("/releases/<release>/builds/<platform>/<locale>", view_func=SingleLocaleView.as_view("single_locale"))
app.add_url_rule("/releases/<release>/revisions", view_func=ReleaseHistoryView.as_view("release_revisions"))
app.add_url_rule("/releases/columns/<column>", view_func=SingleReleaseColumnView.as_view("release_columns"))
app.add_url_rule("/required_signoffs/product", view_func=ProductRequiredSignoffsView.as_view("product_required_signoffs"))
app.add_url_rule("/required_signoffs/product/revisions", view_func=ProductRequiredSignoffsHistoryAPIView.as_view("product_required_signoffs_revisions"))
app.add_url_rule("/required_signoffs/permissions", view_func=PermissionsRequiredSignoffsView.as_view("permissions_required_signoffs"))
app.add_url_rule("/required_signoffs/permissions/revisions",
view_func=PermissionsRequiredSignoffsHistoryAPIView.as_view("permissions_required_signoffs_revisions"))
app.add_url_rule("/history/diff/release/<change_id>/<field>", view_func=ReleaseDiffView.as_view("release_diff"))
app.add_url_rule("/history/view/release/<change_id>/<field>", view_func=ReleaseFieldView.as_view("release_field"))
app.add_url_rule("/scheduled_changes/rules", view_func=RuleScheduledChangesView.as_view("scheduled_changes_rules"))
Expand All @@ -114,3 +134,22 @@ def add_security_headers(response):
app.add_url_rule("/scheduled_changes/releases/<int:sc_id>/signoffs", view_func=ReleaseScheduledChangeSignoffsView.as_view("scheduled_change_release_signoffs"))
app.add_url_rule("/scheduled_changes/releases/<int:sc_id>/revisions",
view_func=ReleaseScheduledChangeHistoryView.as_view("scheduled_change_releases_history"))
app.add_url_rule("/scheduled_changes/required_signoffs/product", view_func=ProductRequiredSignoffsScheduledChangesView.as_view("scheduled_changes_product_rs"))
app.add_url_rule("/scheduled_changes/required_signoffs/product/<int:sc_id>",
view_func=ProductRequiredSignoffScheduledChangeView.as_view("scheduled_change_product_rs"))
app.add_url_rule("/scheduled_changes/required_signoffs/product/<int:sc_id>/enact",
view_func=EnactProductRequiredSignoffScheduledChangeView.as_view("enact_scheduled_change_product_rs"))
app.add_url_rule("/scheduled_changes/required_signoffs/product/<int:sc_id>/signoffs",
view_func=ProductRequiredSignoffScheduledChangeSignoffsView.as_view("scheduled_change_product_rs_signoffs"))
app.add_url_rule("/scheduled_changes/required_signoffs/product/<int:sc_id>/revisions",
view_func=ProductRequiredSignoffScheduledChangeHistoryView.as_view("scheduled_change_product_rs_history"))
app.add_url_rule("/scheduled_changes/required_signoffs/permissions",
view_func=PermissionsRequiredSignoffsScheduledChangesView.as_view("scheduled_changes_permissions_rs"))
app.add_url_rule("/scheduled_changes/required_signoffs/permissions/<int:sc_id>",
view_func=PermissionsRequiredSignoffScheduledChangeView.as_view("scheduled_change_permissions_rs"))
app.add_url_rule("/scheduled_changes/required_signoffs/permissions/<int:sc_id>/enact",
view_func=EnactPermissionsRequiredSignoffScheduledChangeView.as_view("enact_scheduled_change_permissions_rs"))
app.add_url_rule("/scheduled_changes/required_signoffs/permissions/<int:sc_id>/signoffs",
view_func=PermissionsRequiredSignoffScheduledChangeSignoffsView.as_view("scheduled_change_permissions_rs_signoffs"))
app.add_url_rule("/scheduled_changes/required_signoffs/permissions/<int:sc_id>/revisions",
view_func=PermissionsRequiredSignoffScheduledChangeHistoryView.as_view("scheduled_change_permissions_rs_history"))
116 changes: 107 additions & 9 deletions auslib/admin/views/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ class ScheduledChangeNewPermissionForm(ScheduledChangeTimeForm):
permission = StringField('Permission', validators=[Length(0, 50), InputRequired()])
username = StringField('Username', validators=[Length(0, 100), InputRequired()])
options = JSONStringField(None, 'Options')
change_type = SelectField("Change Type", choices=[('insert', 'insert'), ('update', 'update'), ('delete', 'delete')])
change_type = SelectField("Change Type", choices=[('insert', 'insert')])


class ScheduledChangeExistingPermissionForm(ScheduledChangeTimeForm):
Expand All @@ -186,7 +186,7 @@ class ScheduledChangeExistingPermissionForm(ScheduledChangeTimeForm):
username = StringField('Username', validators=[Length(0, 100), InputRequired()])
options = JSONStringField(None, 'Options')
data_version = IntegerField('data_version', validators=[InputRequired()], widget=HiddenInput())
change_type = SelectField("Change Type", choices=[('insert', 'insert'), ('update', 'update'), ('delete', 'delete')])
change_type = SelectField("Change Type", choices=[('update', 'update')])


class ScheduledChangeDeletePermissionForm(ScheduledChangeTimeForm):
Expand All @@ -195,7 +195,7 @@ class ScheduledChangeDeletePermissionForm(ScheduledChangeTimeForm):
permission = StringField('Permission', validators=[Length(0, 50), InputRequired()])
username = StringField('Username', validators=[Length(0, 100), InputRequired()])
data_version = IntegerField('data_version', validators=[InputRequired()], widget=HiddenInput())
change_type = SelectField("Change Type", choices=[('insert', 'insert'), ('update', 'update'), ('delete', 'delete')])
change_type = SelectField("Change Type", choices=[('delete', 'delete')])


class EditScheduledChangeNewPermissionForm(ScheduledChangeTimeForm):
Expand Down Expand Up @@ -274,7 +274,7 @@ class EditRuleForm(DbEditableForm):


class ScheduledChangeNewRuleForm(ScheduledChangeTimeForm, ScheduledChangeUptakeForm, RuleForm):
change_type = SelectField("Change Type", choices=[('insert', 'insert'), ('update', 'update'), ('delete', 'delete')])
change_type = SelectField("Change Type", choices=[('insert', 'insert')])


class ScheduledChangeExistingRuleForm(ScheduledChangeTimeForm, ScheduledChangeUptakeForm, EditRuleForm):
Expand All @@ -283,7 +283,7 @@ class ScheduledChangeExistingRuleForm(ScheduledChangeTimeForm, ScheduledChangeUp
# through URLs that contain scheduled change IDs, so we need to include
# the rule_id in the form when editing scheduled changes for rules.
rule_id = IntegerField('Rule ID', validators=[InputRequired()])
change_type = SelectField("Change Type", choices=[('insert', 'insert'), ('update', 'update'), ('delete', 'delete')])
change_type = SelectField("Change Type", choices=[('update', 'update')])


class ScheduledChangeDeleteRuleForm(ScheduledChangeTimeForm, ScheduledChangeUptakeForm):
Expand All @@ -292,7 +292,7 @@ class ScheduledChangeDeleteRuleForm(ScheduledChangeTimeForm, ScheduledChangeUpta
"""
rule_id = IntegerField('Rule ID', validators=[InputRequired()])
data_version = IntegerField('data_version', validators=[InputRequired()], widget=HiddenInput())
change_type = SelectField("Change Type", choices=[('insert', 'insert'), ('update', 'update'), ('delete', 'delete')])
change_type = SelectField("Change Type", choices=[('delete', 'delete')])


class EditScheduledChangeNewRuleForm(ScheduledChangeTimeForm, ScheduledChangeUptakeForm, RuleForm):
Expand Down Expand Up @@ -335,7 +335,7 @@ class ScheduledChangeNewReleaseForm(ScheduledChangeTimeForm):
name = StringField('Name', validators=[InputRequired()])
product = StringField('Product', validators=[InputRequired()])
data = JSONStringField({}, 'Data', validators=[InputRequired()], widget=FileInput())
change_type = SelectField("Change Type", choices=[('insert', 'insert'), ('update', 'update'), ('delete', 'delete')])
change_type = SelectField("Change Type", choices=[('insert', 'insert')])


class ScheduledChangeExistingReleaseForm(ScheduledChangeTimeForm):
Expand All @@ -346,15 +346,15 @@ class ScheduledChangeExistingReleaseForm(ScheduledChangeTimeForm):
product = StringField('Product', validators=[Optional()])
data = JSONStringField({}, 'Data', validators=[Optional()], widget=FileInput())
data_version = IntegerField('data_version', validators=[InputRequired()], widget=HiddenInput())
change_type = SelectField("Change Type", choices=[('insert', 'insert'), ('update', 'update'), ('delete', 'delete')])
change_type = SelectField("Change Type", choices=[('update', 'update')])


class ScheduledChangeDeleteReleaseForm(ScheduledChangeTimeForm):
"""Name must be provided when Scheduling a Change that deletes an
existing Permission so that we can find it."""
name = StringField('Name', validators=[InputRequired()])
data_version = IntegerField('data_version', validators=[InputRequired()], widget=HiddenInput())
change_type = SelectField("Change Type", choices=[('insert', 'insert'), ('update', 'update'), ('delete', 'delete')])
change_type = SelectField("Change Type", choices=[('delete', 'delete')])


class EditScheduledChangeNewReleaseForm(ScheduledChangeTimeForm):
Expand All @@ -374,3 +374,101 @@ class EditScheduledChangeExistingReleaseForm(ScheduledChangeTimeForm):
data = JSONStringField(None, 'Data', validators=[Optional()], widget=FileInput())
data_version = IntegerField('data_version', widget=HiddenInput())
sc_data_version = IntegerField('sc_data_version', validators=[InputRequired()], widget=HiddenInput())


class ProductRequiredSignoffForm(Form):
product = StringField('Product', validators=[Length(0, 15), InputRequired()])
channel = StringField('Channel', validators=[Length(0, 75), InputRequired()])
role = StringField('Role', validators=[InputRequired()])
signoffs_required = IntegerField("Signoffs Required", validators=[InputRequired()])


class ProductRequiredSignoffHistoryForm(Form):
product = StringField('Product', validators=[Length(0, 15), InputRequired()])
channel = StringField('Channel', validators=[Length(0, 75), InputRequired()])
role = StringField('Role', validators=[InputRequired()])


class ScheduledChangeNewProductRequiredSignoffForm(ScheduledChangeTimeForm):
product = StringField('Product', validators=[Length(0, 15), InputRequired()])
channel = StringField('Channel', validators=[Length(0, 75), InputRequired()])
role = StringField('Role', validators=[InputRequired()])
signoffs_required = IntegerField("Signoffs Required", validators=[InputRequired()])
change_type = SelectField("Change Type", choices=[('insert', 'insert')])


class ScheduledChangeExistingProductRequiredSignoffForm(ScheduledChangeTimeForm):
product = StringField('Product', validators=[Length(0, 15), InputRequired()])
channel = StringField('Channel', validators=[Length(0, 75), InputRequired()])
role = StringField('Role', validators=[InputRequired()])
signoffs_required = IntegerField("Signoffs Required", validators=[InputRequired()])
data_version = IntegerField('data_version', validators=[InputRequired()], widget=HiddenInput())
change_type = SelectField("Change Type", choices=[('update', 'update')])


class ScheduledChangeDeleteProductRequiredSignoffForm(ScheduledChangeTimeForm):
product = StringField('Product', validators=[Length(0, 15), InputRequired()])
channel = StringField('Channel', validators=[Length(0, 75), InputRequired()])
role = StringField('Role', validators=[InputRequired()])
data_version = IntegerField('data_version', validators=[InputRequired()], widget=HiddenInput())
change_type = SelectField("Change Type", choices=[('delete', 'delete')])


class EditScheduledChangeNewProductRequiredSignoffForm(ScheduledChangeTimeForm):
product = StringField('Product', validators=[Length(0, 15), Optional()])
channel = StringField('Channel', validators=[Length(0, 75), Optional()])
role = StringField('Role', validators=[Optional()])
signoffs_required = IntegerField("Signoffs Required", validators=[Optional()])
sc_data_version = IntegerField('sc_data_version', validators=[InputRequired()], widget=HiddenInput())


class EditScheduledChangeExistingProductRequiredSignoffForm(ScheduledChangeTimeForm):
signoffs_required = IntegerField("Signoffs Required", validators=[Optional()])
data_version = IntegerField('data_version', widget=HiddenInput())
sc_data_version = IntegerField('sc_data_version', validators=[InputRequired()], widget=HiddenInput())


class PermissionsRequiredSignoffForm(Form):
product = StringField('Permissions', validators=[Length(0, 15), InputRequired()])
role = StringField('Role', validators=[InputRequired()])
signoffs_required = IntegerField("Signoffs Required", validators=[InputRequired()])


class PermissionsRequiredSignoffHistoryForm(Form):
product = StringField('Product', validators=[Length(0, 15), InputRequired()])
role = StringField('Role', validators=[InputRequired()])


class ScheduledChangeNewPermissionsRequiredSignoffForm(ScheduledChangeTimeForm):
product = StringField('Permissions', validators=[Length(0, 15), InputRequired()])
role = StringField('Role', validators=[InputRequired()])
signoffs_required = IntegerField("Signoffs Required", validators=[InputRequired()])
change_type = SelectField("Change Type", choices=[('insert', 'insert')])


class ScheduledChangeExistingPermissionsRequiredSignoffForm(ScheduledChangeTimeForm):
product = StringField('Permissions', validators=[Length(0, 15), InputRequired()])
role = StringField('Role', validators=[InputRequired()])
signoffs_required = IntegerField("Signoffs Required", validators=[InputRequired()])
data_version = IntegerField('data_version', validators=[InputRequired()], widget=HiddenInput())
change_type = SelectField("Change Type", choices=[('update', 'update')])


class ScheduledChangeDeletePermissionsRequiredSignoffForm(ScheduledChangeTimeForm):
product = StringField('Permissions', validators=[Length(0, 15), InputRequired()])
role = StringField('Role', validators=[InputRequired()])
data_version = IntegerField('data_version', validators=[InputRequired()], widget=HiddenInput())
change_type = SelectField("Change Type", choices=[('delete', 'delete')])


class EditScheduledChangeNewPermissionsRequiredSignoffForm(ScheduledChangeTimeForm):
product = StringField('Permissions', validators=[Length(0, 15), Optional()])
role = StringField('Role', validators=[Optional()])
signoffs_required = IntegerField("Signoffs Required", validators=[Optional()])
sc_data_version = IntegerField('sc_data_version', validators=[InputRequired()], widget=HiddenInput())


class EditScheduledChangeExistingPermissionsRequiredSignoffForm(ScheduledChangeTimeForm):
signoffs_required = IntegerField("Signoffs Required", validators=[Optional()])
data_version = IntegerField('data_version', widget=HiddenInput())
sc_data_version = IntegerField('sc_data_version', validators=[InputRequired()], widget=HiddenInput())
32 changes: 24 additions & 8 deletions auslib/admin/views/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,30 @@ def get(self):
return jsonify(dict(users=users))


class SpecificUserView(AdminView):
"""Returns all of the details about the logged in user. The UI needs this
method to know things about the current user because it does not have
access to REMOTE_USER, so it cannot query directly by name."""

def get(self, username):
current_user = request.environ.get('REMOTE_USER', request.environ.get("HTTP_REMOTE_USER"))
if username == "current":
username = current_user
# If the user is retrieving permissions other than their own, we need
# to make sure they have enough access to do so. If any user is able
# to retrieve permissions of anyone, it may make privilege escalation
# attacks easier.
# TODO: do this at the database layer
else:
if username != current_user and not dbo.hasPermission(current_user, "permission", "view"):
return Response(status=403, response="You are not authorized to view permissions of other users.")
permissions = dbo.permissions.getUserPermissions(username)
if not permissions:
return Response(status=404)
roles = {r["role"]: {"data_version": r["data_version"]} for r in dbo.permissions.getUserRoles(username)}
return jsonify({"username": username, "permissions": permissions, "roles": roles})


class PermissionsView(AdminView):
"""/users/:username/permissions"""

Expand Down Expand Up @@ -154,14 +178,6 @@ class PermissionScheduledChangeSignoffsView(SignoffsView):
def __init__(self):
super(PermissionScheduledChangeSignoffsView, self).__init__("permissions", dbo.permissions)

@requirelogin
def _post(self, sc_id, transaction, changed_by):
return super(PermissionScheduledChangeSignoffsView, self)._post(sc_id, transaction, changed_by)

@requirelogin
def _delete(self, sc_id, transaction, changed_by):
return super(PermissionScheduledChangeSignoffsView, self)._delete(sc_id, transaction, changed_by)


class PermissionScheduledChangeHistoryView(ScheduledChangeHistoryView):
def __init__(self):
Expand Down
8 changes: 0 additions & 8 deletions auslib/admin/views/releases.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,14 +571,6 @@ class ReleaseScheduledChangeSignoffsView(SignoffsView):
def __init__(self):
super(ReleaseScheduledChangeSignoffsView, self).__init__("releases", dbo.releases)

@requirelogin
def _post(self, sc_id, transaction, changed_by):
return super(ReleaseScheduledChangeSignoffsView, self)._post(sc_id, transaction, changed_by)

@requirelogin
def _delete(self, sc_id, transaction, changed_by):
return super(ReleaseScheduledChangeSignoffsView, self)._delete(sc_id, transaction, changed_by)


class ReleaseScheduledChangeHistoryView(ScheduledChangeHistoryView):
def __init__(self):
Expand Down

0 comments on commit fd03557

Please sign in to comment.