def _delete(self, rule_id, transaction, changed_by): # Verify that the rule_id exists. rule = dbo.rules.getRuleById(rule_id, transaction=transaction) if not rule: return Response(status=404) # Bodies are ignored for DELETE requests, so we need to force WTForms # to look at the arguments instead. # Even though we aren't going to use most of the form fields (just # rule_id and data_version), we still want to create and validate the # form to make sure that the CSRF token is checked. form = DbEditableForm(request.args) if not dbo.permissions.hasUrlPermission( changed_by, '/rules/:id', 'DELETE', urlOptions={'product': rule['product']}): msg = "%s is not allowed to alter rules that affect %s" % ( changed_by, rule['product']) cef_event('Unauthorized access attempt', CEF_ALERT, msg=msg) return Response(status=401, response=msg) dbo.rules.deleteRule(changed_by=changed_by, rule_id=rule_id, old_data_version=form.data_version.data, transaction=transaction) return Response(status=200)
def _delete(self, release, changed_by, transaction): releases = dbo.releases.getReleases(name=release) if not releases: return Response(status=404, response='bad release') release = releases[0] if not dbo.permissions.hasUrlPermission( changed_by, '/releases/:name', 'DELETE', urlOptions={'product': release['product']}): msg = "%s is not allowed to delete %s releases" % ( changed_by, release['product']) cef_event('Unauthorized access attempt', CEF_ALERT, msg=msg) return Response(status=401, response=msg) # Bodies are ignored for DELETE requests, so we need to force WTForms # to look at the arguments instead. # We only need the release name (which comes through the URL) and the # data version to process this request. Because of that, we can just # use this form to validate, because we're only validating CSRF # and data version. form = DbEditableForm(request.args) if not form.validate(): cef_event("Bad input", CEF_WARN, errors=form.errors) return Response(status=400, response=form.errors) dbo.releases.deleteRelease(changed_by=changed_by, name=release['name'], old_data_version=form.data_version.data, transaction=transaction) return Response(status=200)
def _post(self, changed_by, transaction): form = CompleteReleaseForm() if not form.validate(): cef_event("Bad input", CEF_WARN, errors=form.errors) return Response(status=400, response=json.dumps(form.errors)) try: blob = createBlob(form.blob.data) name = dbo.releases.addRelease( name=form.name.data, product=form.product.data, version=form.version.data, blob=blob, changed_by=changed_by, transaction=transaction ) except ValueError as e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=msg) release = dbo.releases.getReleases( name=name, transaction=transaction, limit=1 )[0] new_data_version = release['data_version'] response = make_response( json.dumps(dict(new_data_version=new_data_version)), 201 ) response.headers['Content-Type'] = 'application/json' return response
def decorated(*args, **kwargs): try: return f(*args, **kwargs) except OutdatedDataError as e: msg = "Couldn't perform the request %s. Outdated Data Version. old_data_version doesn't match current data_version: %s" % (messages, e) cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=json.dumps({"data": e.args}))
def _delete(self, rule_id, transaction, changed_by): # Verify that the rule_id exists. rule = dbo.rules.getRuleById(rule_id, transaction=transaction) if not rule: return Response(status=404) # Bodies are ignored for DELETE requests, so we need to force WTForms # to look at the arguments instead. # Even though we aren't going to use most of the form fields (just # rule_id and data_version), we still want to create and validate the # form to make sure that the CSRF token is checked. form = DbEditableForm(request.args) if not dbo.permissions.hasUrlPermission( changed_by, "/rules/:id", "DELETE", urlOptions={"product": rule["product"]} ): msg = "%s is not allowed to alter rules that affect %s" % (changed_by, rule["product"]) cef_event("Unauthorized access attempt", CEF_ALERT, msg=msg) return Response(status=401, response=msg) dbo.rules.deleteRule( changed_by=changed_by, rule_id=rule_id, old_data_version=form.data_version.data, transaction=transaction ) return Response(status=200)
def _post(self, transaction, changed_by): # a Post here creates a new rule form = RuleForm() releaseNames = dbo.releases.getReleaseNames() form.mapping.choices = [(item['name'], item['name']) for item in releaseNames] form.mapping.choices.insert(0, ('', 'NULL')) if not form.validate(): cef_event("Bad input", CEF_WARN, errors=form.errors) return Response(status=400, response=json.dumps(form.errors)) what = dict(backgroundRate=form.backgroundRate.data, mapping=form.mapping.data, priority=form.priority.data, alias=form.alias.data, product=form.product.data, version=form.version.data, buildID=form.buildID.data, channel=form.channel.data, locale=form.locale.data, distribution=form.distribution.data, buildTarget=form.buildTarget.data, osVersion=form.osVersion.data, distVersion=form.distVersion.data, whitelist=form.whitelist.data, comment=form.comment.data, update_type=form.update_type.data, headerArchitecture=form.headerArchitecture.data) rule_id = dbo.rules.addRule(changed_by=changed_by, what=what, transaction=transaction) return Response(status=200, response=str(rule_id))
def _delete(self, release, changed_by, transaction): releases = dbo.releases.getReleases(name=release) if not releases: return Response(status=404, response='bad release') release = releases[0] if not dbo.permissions.hasUrlPermission(changed_by, '/releases/:name', 'DELETE', urlOptions={'product': release['product']}): msg = "%s is not allowed to delete %s releases" % (changed_by, release['product']) cef_event('Unauthorized access attempt', CEF_ALERT, msg=msg) return Response(status=401, response=msg) # Bodies are ignored for DELETE requests, so we need to force WTForms # to look at the arguments instead. # We only need the release name (which comes through the URL) and the # data version to process this request. Because of that, we can just # use this form to validate, because we're only validating CSRF # and data version. form = DbEditableForm(request.args) if not form.validate(): cef_event("Bad input", CEF_WARN, errors=form.errors) return Response(status=400, response=form.errors) dbo.releases.deleteRelease(changed_by=changed_by, name=release['name'], old_data_version=form.data_version.data, transaction=transaction) return Response(status=200)
def _post(self, transaction, changed_by): # a Post here creates a new rule form = RuleForm() releaseNames = dbo.releases.getReleaseNames() form.mapping.choices = [(item['name'], item['name']) for item in releaseNames] form.mapping.choices.insert(0, ('', 'NULL')) if not form.validate(): cef_event("Bad input", CEF_WARN, errors=form.errors) return Response(status=400, response=json.dumps(form.errors)) what = dict(backgroundRate=form.backgroundRate.data, mapping=form.mapping.data, priority=form.priority.data, product=form.product.data, version=form.version.data, buildID=form.buildID.data, channel=form.channel.data, locale=form.locale.data, distribution=form.distribution.data, buildTarget=form.buildTarget.data, osVersion=form.osVersion.data, distVersion=form.distVersion.data, comment=form.comment.data, update_type=form.update_type.data, headerArchitecture=form.headerArchitecture.data) rule_id = dbo.rules.addRule(changed_by=changed_by, what=what, transaction=transaction) return Response(status=200, response=str(rule_id))
def _post(self, id_or_alias, transaction, changed_by): # Verify that the rule_id or alias exists. rule = dbo.rules.getRule(id_or_alias, transaction=transaction) if not rule: return Response(status=404) form = EditRuleForm() # Verify that the user has permission for the existing rule _and_ what the rule would become. toCheck = [rule['product']] # Rules can be partially updated - if product is null/None, we won't update that field, so # we shouldn't check its permission. if form.product.data: toCheck.append(form.product.data) for product in toCheck: if not dbo.permissions.hasUrlPermission(changed_by, '/rules/:id', 'POST', urlOptions={'product': product}): msg = "%s is not allowed to alter rules that affect %s" % (changed_by, product) cef_event('Unauthorized access attempt', CEF_ALERT, msg=msg) return Response(status=401, response=msg) releaseNames = dbo.releases.getReleaseNames() form.mapping.choices = [(item['name'], item['name']) for item in releaseNames] form.mapping.choices.insert(0, ('', 'NULL')) if not form.validate(): cef_event("Bad input", CEF_WARN, errors=form.errors) return Response(status=400, response=json.dumps(form.errors)) what = dict() # We need to be able to support changing AND removing parts of a rule, # and because of how Flask's request object and WTForm's defaults work # this gets a little hary. for k, v in form.data.iteritems(): # data_version is a "special" column, in that it's not part of the # primary data, and shouldn't be updatable by the user. if k == "data_version": continue # We need to check for each column in both the JSON style post # and the regular multipart form data. If the key is not present in # either of these data structures. We treat this cases as no-op # and shouldn't modify the data for that key. # If the key is present we should modify the data as requested. # If a value is an empty string, we should remove that restriction # from the rule (aka, set as NULL in the db). The underlying Form # will have already converted it to None, so we can treat it the # same as a modification here. if (request.json and k in request.json) or k in request.form: what[k] = v dbo.rules.updateRule(changed_by=changed_by, id_or_alias=id_or_alias, what=what, old_data_version=form.data_version.data, transaction=transaction) # find out what the next data version is rule = dbo.rules.getRule(id_or_alias, transaction=transaction) new_data_version = rule['data_version'] response = make_response(json.dumps(dict(new_data_version=new_data_version))) response.headers['Content-Type'] = 'application/json' return response
def decorated(*args, **kwargs): try: return f(*args, **kwargs) except OutdatedDataError as e: msg = "Couldn't perform the request %s. Outdated Data Version. old_data_version doesn't match current data_version: %s" % ( messages, e) cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=json.dumps({"data": e.args}))
def _post(self, rule_id, transaction, changed_by): change_id = None if request.json: change_id = request.json.get('change_id') if not change_id: cef_event("Bad input", CEF_WARN, errors="no change_id") return Response(status=400, response='no change_id') change = dbo.rules.history.getChange(change_id=change_id) if change is None: return Response(status=404, response='bad change_id') if change['rule_id'] != rule_id: return Response(status=404, response='bad rule_id') rule = dbo.rules.getRule(rule_id) if rule is None: return Response(status=404, response='bad rule_id') # Verify that the user has permission for the existing rule _and_ what the rule would become. for product in (rule['product'], change['product']): if not dbo.permissions.hasUrlPermission( changed_by, '/rules/:id', 'POST', urlOptions={'product': product}): msg = "%s is not allowed to alter rules that affect %s" % ( changed_by, product) cef_event('Unauthorized access attempt', CEF_ALERT, msg=msg) return Response(status=401, response=msg) old_data_version = rule['data_version'] # now we're going to make a new insert based on this what = dict( backgroundRate=change['backgroundRate'], mapping=change['mapping'], priority=change['priority'], alias=change['alias'], product=change['product'], version=change['version'], buildID=change['buildID'], channel=change['channel'], locale=change['locale'], distribution=change['distribution'], buildTarget=change['buildTarget'], osVersion=change['osVersion'], distVersion=change['distVersion'], whitelist=change['whitelist'], comment=change['comment'], update_type=change['update_type'], headerArchitecture=change['headerArchitecture'], ) dbo.rules.updateRule(changed_by=changed_by, id_or_alias=rule_id, what=what, old_data_version=old_data_version, transaction=transaction) return Response("Excellent!")
def get(self, type_, change_id, field): try: value = self.get_value(type_, change_id, field) except KeyError as msg: cef_event("Bad input", CEF_WARN, errors=str(msg), field=field) return Response(status=400, response=str(msg)) except ValueError as msg: return Response(status=404, response=str(msg)) value = self.format_value(value) return Response(value, content_type='text/plain')
def _post(self, username, permission, changed_by, transaction): if not dbo.permissions.getUserPermissions(username, transaction=transaction).get(permission): return Response(status=404) try: form = ExistingPermissionForm() dbo.permissions.updatePermission(changed_by, username, permission, form.data_version.data, form.options.data, transaction=transaction) new_data_version = dbo.permissions.getPermission(username=username, permission=permission, transaction=transaction)['data_version'] return make_response(json.dumps(dict(new_data_version=new_data_version)), 200) except ValueError, e: cef_event("Bad input", CEF_WARN, errors=e.args) return Response(status=400, response=e.args)
def _delete(self, username, permission, changed_by, transaction): if not dbo.permissions.getUserPermissions(username, transaction=transaction).get(permission): return Response(status=404) try: # For practical purposes, DELETE can't have a request body, which means the Form # won't find data where it's expecting it. Instead, we have to tell it to look at # the query string, which Flask puts in request.args. form = ExistingPermissionForm(request.args) dbo.permissions.revokePermission(changed_by, username, permission, form.data_version.data, transaction=transaction) return Response(status=200) except ValueError, e: cef_event("Bad input", CEF_WARN, errors=e.args) return Response(status=400, response=e.args)
def _post(self, rule_id, transaction, changed_by): rule_id = int(rule_id) change_id = request.form.get("change_id") if not change_id: cef_event("Bad input", CEF_WARN, errors="no change_id") return Response(status=400, response="no change_id") change = dbo.rules.history.getChange(change_id=change_id) if change is None: return Response(status=404, response="bad change_id") if change["rule_id"] != rule_id: return Response(status=404, response="bad rule_id") rule = dbo.rules.getRuleById(rule_id=rule_id) if rule is None: return Response(status=404, response="bad rule_id") # Verify that the user has permission for the existing rule _and_ what the rule would become. for product in (rule["product"], change["product"]): if not dbo.permissions.hasUrlPermission(changed_by, "/rules/:id", "POST", urlOptions={"product": product}): msg = "%s is not allowed to alter rules that affect %s" % (changed_by, product) cef_event("Unauthorized access attempt", CEF_ALERT, msg=msg) return Response(status=401, response=msg) old_data_version = rule["data_version"] # now we're going to make a new insert based on this what = dict( backgroundRate=change["backgroundRate"], mapping=change["mapping"], priority=change["priority"], product=change["product"], version=change["version"], buildID=change["buildID"], channel=change["channel"], locale=change["locale"], distribution=change["distribution"], buildTarget=change["buildTarget"], osVersion=change["osVersion"], distVersion=change["distVersion"], comment=change["comment"], update_type=change["update_type"], headerArchitecture=change["headerArchitecture"], ) dbo.rules.updateRule( changed_by=changed_by, rule_id=rule_id, what=what, old_data_version=old_data_version, transaction=transaction, ) return Response("Excellent!")
def get(self, rule_id): rule = dbo.rules.getRuleById(rule_id=rule_id) if not rule: return Response(status=404, response="Requested rule does not exist") table = dbo.rules.history try: page = int(request.args.get("page", 1)) limit = int(request.args.get("limit", 100)) assert page >= 1 except (ValueError, AssertionError), msg: cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=str(msg))
def get(self, rule_id): rule = dbo.rules.getRuleById(rule_id=rule_id) if not rule: return Response(status=404, response='Requested rule does not exist') table = dbo.rules.history try: page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 100)) assert page >= 1 except (ValueError, AssertionError), msg: cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=str(msg))
def get(self, release): releases = dbo.releases.getReleases(name=release, limit=1) if not releases: return Response(status=404, response='Requested release does not exist') release = releases[0] table = dbo.releases.history try: page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 10)) assert page >= 1 except (ValueError, AssertionError), msg: cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=str(msg))
def _put(self, release, changed_by, transaction): form = NewReleaseForm() if not form.validate(): cef_event("Bad input", CEF_WARN, errors=form.errors) return Response(status=400, response=form.errors) try: blob = createBlob(form.blob.data) dbo.releases.addRelease(name=release, product=form.product.data, version=form.version.data, blob=blob, changed_by=changed_by, transaction=transaction) except ValueError, e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=msg)
def _post(self, rule_id, transaction, changed_by): change_id = None if request.json: change_id = request.json.get('change_id') if not change_id: cef_event("Bad input", CEF_WARN, errors="no change_id") return Response(status=400, response='no change_id') change = dbo.rules.history.getChange(change_id=change_id) if change is None: return Response(status=404, response='bad change_id') if change['rule_id'] != rule_id: return Response(status=404, response='bad rule_id') rule = dbo.rules.getRule(rule_id) if rule is None: return Response(status=404, response='bad rule_id') # Verify that the user has permission for the existing rule _and_ what the rule would become. for product in (rule['product'], change['product']): if not dbo.permissions.hasUrlPermission(changed_by, '/rules/:id', 'POST', urlOptions={'product': product}): msg = "%s is not allowed to alter rules that affect %s" % (changed_by, product) cef_event('Unauthorized access attempt', CEF_ALERT, msg=msg) return Response(status=401, response=msg) old_data_version = rule['data_version'] # now we're going to make a new insert based on this what = dict( backgroundRate=change['backgroundRate'], mapping=change['mapping'], priority=change['priority'], alias=change['alias'], product=change['product'], version=change['version'], buildID=change['buildID'], channel=change['channel'], locale=change['locale'], distribution=change['distribution'], buildTarget=change['buildTarget'], osVersion=change['osVersion'], distVersion=change['distVersion'], whitelist=change['whitelist'], comment=change['comment'], update_type=change['update_type'], headerArchitecture=change['headerArchitecture'], ) dbo.rules.updateRule(changed_by=changed_by, id_or_alias=rule_id, what=what, old_data_version=old_data_version, transaction=transaction) return Response("Excellent!")
def _put(self, username, permission, changed_by, transaction): try: if dbo.permissions.getUserPermissions(username, transaction).get(permission): form = ExistingPermissionForm() if not form.data_version.data: raise ValueError("Must provide the data version when updating an existing permission.") dbo.permissions.updatePermission(changed_by, username, permission, form.data_version.data, form.options.data, transaction=transaction) new_data_version = dbo.permissions.getPermission(username=username, permission=permission, transaction=transaction)['data_version'] return make_response(json.dumps(dict(new_data_version=new_data_version)), 200) else: form = NewPermissionForm() dbo.permissions.grantPermission(changed_by, username, permission, form.options.data, transaction=transaction) return make_response(json.dumps(dict(new_data_version=1)), 201) except ValueError, e: cef_event("Bad input", CEF_WARN, errors=e.args) return Response(status=400, response=e.args)
def _put(self, release, changed_by, transaction): form = CompleteReleaseForm() if not form.validate(): cef_event("Bad input", CEF_WARN, errors=form.errors) return Response(status=400, response=json.dumps(form.errors)) blob = createBlob(form.blob.data) if dbo.releases.getReleases(name=release, limit=1): data_version = form.data_version.data try: dbo.releases.updateRelease(name=release, blob=blob, product=form.product.data, changed_by=changed_by, old_data_version=data_version, transaction=transaction) except BlobValidationError as e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=json.dumps({"data": e.errors})) except ValueError as e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=json.dumps({"data": e.args})) data_version += 1 return Response(json.dumps(dict(new_data_version=data_version)), status=200) else: try: dbo.releases.addRelease(name=release, product=form.product.data, blob=blob, changed_by=changed_by, transaction=transaction) except BlobValidationError as e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=json.dumps({"data": e.errors})) except ValueError as e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=json.dumps({"data": e.args})) return Response(status=201)
def _post(self, changed_by, transaction): form = CompleteReleaseForm() if not form.validate(): cef_event("Bad input", CEF_WARN, errors=form.errors) return Response(status=400, response=json.dumps(form.errors)) try: blob = createBlob(form.blob.data) name = dbo.releases.addRelease( name=form.name.data, product=form.product.data, version=form.version.data, blob=blob, changed_by=changed_by, transaction=transaction ) except ValueError, e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=msg)
def decorated(*args, **kwargs): username = request.environ.get('REMOTE_USER') method = request.method extra = dict() for opt in options: if opt in request.form: extra[opt] = request.form[opt] elif request.get_json() and opt in request.json: extra[opt] = request.json[opt] else: msg = "Couldn't find required option %s in form" % opt cef_event("Bad input", CEF_WARN, msg=msg) return Response(status=400, response=msg) if not dbo.permissions.hasUrlPermission(username, url, method, urlOptions=extra): msg = "%s is not allowed to access %s by %s" % (username, url, method) cef_event('Unauthorized access attempt', CEF_ALERT, msg=msg) return Response(status=401, response=msg) return f(*args, **kwargs)
def _put(self, release, changed_by, transaction): form = CompleteReleaseForm() if not form.validate(): cef_event("Bad input", CEF_WARN, errors=form.errors) return Response(status=400, response=form.errors) blob = createBlob(form.blob.data) if dbo.releases.getReleases(name=release, limit=1): data_version = form.data_version.data try: dbo.releases.updateRelease(name=release, blob=blob, version=form.version.data, product=form.product.data, changed_by=changed_by, old_data_version=data_version, transaction=transaction) except ValueError, e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=msg) data_version += 1 return Response(json.dumps(dict(new_data_version=data_version)), status=200)
def decorated(*args, **kwargs): username = request.environ.get('REMOTE_USER') method = request.method extra = dict() for opt in options: if opt in request.form: extra[opt] = request.form[opt] elif request.get_json() and opt in request.json: extra[opt] = request.json[opt] else: msg = "Couldn't find required option %s in form" % opt cef_event("Bad input", CEF_WARN, msg=msg) return Response(status=400, response=msg) if not dbo.permissions.hasUrlPermission( username, url, method, urlOptions=extra): msg = "%s is not allowed to access %s by %s" % (username, url, method) cef_event('Unauthorized access attempt', CEF_ALERT, msg=msg) return Response(status=401, response=msg) return f(*args, **kwargs)
def _post(self, release, transaction, changed_by): change_id = request.form.get('change_id') if not change_id: cef_event("Bad input", CEF_WARN, errors="no change id", release=release) return Response(status=400, response='no change_id') change = dbo.releases.history.getChange(change_id=change_id) if change is None: return Response(status=404, response='bad change_id') if change['name'] != release: return Response(status=404, response='bad release') releases = dbo.releases.getReleases(name=release) if not releases: return Response(status=404, response='bad release') release = releases[0] if not dbo.permissions.hasUrlPermission(changed_by, '/releases/:name', 'POST', urlOptions={'product': release['product']}): msg = "%s is not allowed to alter %s releases" % (changed_by, release['product']) cef_event('Unauthorized access attempt', CEF_ALERT, msg=msg) return Response(status=401, response=msg) old_data_version = release['data_version'] # now we're going to make a new update based on this change blob = createBlob(change['data']) try: dbo.releases.updateRelease(changed_by=changed_by, name=change['name'], version=change['version'], blob=blob, old_data_version=old_data_version, transaction=transaction) except ValueError, e: cef_event("Bad input", CEF_WARN, errors=e.args) return Response(status=400, response=e.args)
def _post(self, changed_by, transaction): form = CompleteReleaseForm() if not form.validate(): cef_event("Bad input", CEF_WARN, errors=form.errors) return Response(status=400, response=json.dumps(form.errors)) try: blob = createBlob(form.blob.data) name = dbo.releases.addRelease(name=form.name.data, product=form.product.data, blob=blob, changed_by=changed_by, transaction=transaction) except BlobValidationError as e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=json.dumps({"data": e.errors})) except ValueError as e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=json.dumps({"data": e.args})) release = dbo.releases.getReleases(name=name, transaction=transaction, limit=1)[0] new_data_version = release['data_version'] response = make_response( json.dumps(dict(new_data_version=new_data_version)), 201) response.headers['Content-Type'] = 'application/json' return response
def get(self, release): releases = dbo.releases.getReleases(name=release, limit=1) if not releases: return Response(status=404, response='Requested release does not exist') release = releases[0] table = dbo.releases.history try: page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 10)) assert page >= 1 except (ValueError, AssertionError) as e: cef_event("Bad input", CEF_WARN, errors=json.dumps(e.args)) return Response(status=400, response=json.dumps({"data": e.args})) offset = limit * (page - 1) total_count = table.t.count()\ .where(table.name == release['name'])\ .where(table.data_version != null())\ .execute()\ .fetchone()[0] revisions = table.select( where=[ table.name == release['name'], table.data_version != null() ], limit=limit, offset=offset, order_by=[table.timestamp.asc()], ) self.annotateRevisionDifferences(revisions) return jsonify({ 'revisions': revisions, 'count': total_count, })
def decorated(*args, **kwargs): username = request.environ.get('REMOTE_USER') if not username: cef_event('Login Required', CEF_WARN) return Response(status=401) return f(*args, changed_by=username, **kwargs)
def changeRelease(release, changed_by, transaction, existsCallback, commitCallback, log): """Generic function to change an aspect of a release. It relies on a ReleaseForm existing and does some upfront work and checks before doing anything. It will, for the named release and any found in the 'copyTo' field of the ReleaseForm: - Create the release if it doesn't already exist. - return a 400 Response if the release exists and old_data_version doesn't. - return a 400 Response if the product name in the form doesn't match the existing one. - update the version column of the release table if the one in the form doesn't match it. - if the release already exists, 'existsCallback' will be called. If that function returns True, a 201 Response will be returned upon successful completion. If that function returns False, a 200 Response will be returned instead. @type release: string @param release: The primary release to update. Additional releases found in the 'copyTo' field of the ReleaseForm will also be updated. @type changed_by: string @param changed_by: The username making the change. @type transaction: AUSTransaction object @param transaction: The transaction object to be used for all database operations. @type existsCallback: callable @param existsCallback: The callable to call to determine whether to consider this a "new" change or not. It must receive 3 positional arguments: - the name of the release - the product name from the ReleaseForm - the version from the ReleaseForm @type commitCallback: callable @param commitCallback: The callable to call after all prerequisite checks and updates are done. It must receive 6 positional arguments: - the name of the release - the product name from the ReleaseForm - the version from the ReleaseForm - the data from the ReleaseForm - the most recent version of the data for the release from the database - the old_data_version from the ReleaseForm """ new = True form = ReleaseForm() if not form.validate(): cef_event("Bad input", CEF_WARN, errors=form.errors) return Response(status=400, response=json.dumps(form.errors)) product = form.product.data version = form.version.data incomingData = form.data.data copyTo = form.copyTo.data alias = form.alias.data old_data_version = form.data_version.data # schema_version is an attribute at the root level of a blob. # Endpoints that receive an entire blob can find it there. # Those that don't have to pass it as a form element instead. if getattr(form.schema_version, "data", None): schema_version = form.schema_version.data elif incomingData.get("schema_version"): schema_version = incomingData.get("schema_version") else: return Response(status=400, response="schema_version is required") if getattr(form.hashFunction, "data", None): hashFunction = form.hashFunction.data elif incomingData.get("hashFunction"): hashFunction = incomingData.get("hashFunction") else: hashFunction = None allReleases = [release] if copyTo: allReleases += copyTo for rel in allReleases: try: releaseInfo = dbo.releases.getReleases(name=rel, transaction=transaction)[0] if existsCallback(rel, product, version): new = False # "release" is the one named in the URL (as opposed to the # ones that can be provided in copyTo), and we treat it as # the "primary" one if rel == release: # Make sure that old_data_version is provided, because we need to verify it when updating. if not old_data_version: msg = "Release exists, data_version must be provided" cef_event("Bad input", CEF_WARN, errors=msg, release=rel) return Response(status=400, response=msg) # If the product we're given doesn't match the one in the DB, panic. if product != releaseInfo['product']: msg = "Product name '%s' doesn't match the one on the release object ('%s') for release '%s'" % ( product, releaseInfo['product'], rel) cef_event("Bad input", CEF_WARN, errors=msg, release=rel) return Response(status=400, response=msg) if 'hashFunction' in releaseInfo[ 'data'] and hashFunction and hashFunction != releaseInfo[ 'data']['hashFunction']: msg = "hashFunction '%s' doesn't match the one on the release object ('%s') for release '%s'" % ( hashFunction, releaseInfo['data']['hashFunction'], rel) cef_event("Bad input", CEF_WARN, errors=msg, release=rel) return Response(status=400, response=msg) # If this isn't the release in the URL... else: # Use the data_version we just grabbed from the dbo. old_data_version = releaseInfo['data_version'] except IndexError: # If the release doesn't already exist, create it, and set old_data_version appropriately. newReleaseData = dict(name=rel, schema_version=schema_version) if hashFunction: newReleaseData['hashFunction'] = hashFunction try: releaseInfo = createRelease(rel, product, version, changed_by, transaction, newReleaseData) except ValueError, e: msg = "Couldn't create release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg, release=rel) return Response(status=400, response=msg) old_data_version = 1 # If the version doesn't match, just update it. This will be the case for nightlies # every time there's a version bump. if version != releaseInfo['version']: log.debug("database version for %s is %s, updating it to %s", rel, releaseInfo['version'], version) try: dbo.releases.updateRelease(name=rel, version=version, changed_by=changed_by, old_data_version=old_data_version, transaction=transaction) except ValueError, e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg, release=rel) return Response(status=400, response=msg) old_data_version += 1
def get(self, rule_id): rule = dbo.rules.getRule(rule_id) if not rule: return Response(status=404, response='Requested rule does not exist') table = dbo.rules.history try: page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 100)) assert page >= 1 except (ValueError, AssertionError) as msg: cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=str(msg)) offset = limit * (page - 1) total_count = table.t.count()\ .where(table.rule_id == rule_id)\ .where(table.data_version != null())\ .execute()\ .fetchone()[0] revisions = table.select( where=[table.rule_id == rule_id, table.data_version != null()], limit=limit, offset=offset, order_by=[table.timestamp.asc()], ) _rules = [] _mapping = { # return : db name 'id': 'rule_id', 'mapping': 'mapping', 'priority': 'priority', 'alias': 'alias', 'product': 'product', 'version': 'version', 'background_rate': 'backgroundRate', 'buildID': 'buildID', 'channel': 'channel', 'locale': 'locale', 'distribution': 'distribution', 'buildTarget': 'buildTarget', 'osVersion': 'osVersion', 'distVersion': 'distVersion', 'whitelist': 'whitelist', 'comment': 'comment', 'update_type': 'update_type', 'headerArchitecture': 'headerArchitecture', 'data_version': 'data_version', # specific to revisions 'change_id': 'change_id', 'timestamp': 'timestamp', 'changed_by': 'changed_by', } for rule in revisions: _rules.append(dict( (key, rule[db_key]) for key, db_key in _mapping.items() )) ret = { 'count': total_count, 'rules': _rules, } return Response(response=json.dumps(ret), mimetype="application/json")
def isForbiddenUrl(url, whitelistedDomains): domain = urlparse(url)[1] if domain not in whitelistedDomains: cef_event("Forbidden domain", CEF_ALERT, domain=domain) return True return False
changed_by=changed_by, old_data_version=old_data_version, transaction=transaction) except ValueError, e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg, release=rel) return Response(status=400, response=msg) old_data_version += 1 extraArgs = {} if alias: extraArgs['alias'] = alias try: commitCallback(rel, product, version, incomingData, releaseInfo['data'], old_data_version, extraArgs) except (OutdatedDataError, ValueError), e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg, release=rel) return Response(status=400, response=msg) new_data_version = dbo.releases.getReleases(name=release, transaction=transaction)[0]['data_version'] if new: status = 201 else: status = 200 return make_response(json.dumps(dict(new_data_version=new_data_version)), status) class SingleLocaleView(AdminView): """/releases/[release]/builds/[platform]/[locale]""" def get(self, release, platform, locale): try: locale = dbo.releases.getLocale(release, platform, locale)
def _post(self, rule_id, transaction, changed_by): # Verify that the rule_id exists. rule = dbo.rules.getRuleById(rule_id, transaction=transaction) if not rule: return Response(status=404) form = EditRuleForm() # Verify that the user has permission for the existing rule _and_ what the rule would become. toCheck = [rule['product']] # Rules can be partially updated - if product is null/None, we won't update that field, so # we shouldn't check its permission. if form.product.data: toCheck.append(form.product.data) for product in toCheck: if not dbo.permissions.hasUrlPermission(changed_by, '/rules/:id', 'POST', urlOptions={'product': product}): msg = "%s is not allowed to alter rules that affect %s" % (changed_by, product) cef_event('Unauthorized access attempt', CEF_ALERT, msg=msg) return Response(status=401, response=msg) releaseNames = dbo.releases.getReleaseNames() form.mapping.choices = [(item['name'],item['name']) for item in releaseNames] form.mapping.choices.insert(0, ('', 'NULL' )) if not form.validate(): cef_event("Bad input", CEF_WARN, errors=form.errors) return Response(status=400, response=json.dumps(form.errors)) what = dict() if form.backgroundRate.data: what['backgroundRate'] = form.backgroundRate.data if form.mapping.data: what['mapping'] = form.mapping.data if form.priority.data: what['priority'] = form.priority.data if form.product.data: what['product'] = form.product.data if form.version.data: what['version'] = form.version.data if form.build_id.data: what['buildID'] = form.build_id.data if form.channel.data: what['channel'] = form.channel.data if form.locale.data: what['locale'] = form.locale.data if form.distribution.data: what['distribution'] = form.distribution.data if form.build_target.data: what['buildTarget'] = form.build_target.data if form.os_version.data: what['osVersion'] = form.os_version.data if form.dist_version.data: what['distVersion'] = form.dist_version.data if form.comment.data: what['comment'] = form.comment.data if form.update_type.data: what['update_type'] = form.update_type.data if form.header_arch.data: what['headerArchitecture'] = form.header_arch.data dbo.rules.updateRule(changed_by=changed_by, rule_id=rule_id, what=what, old_data_version=form.data_version.data, transaction=transaction) # find out what the next data version is rule = dbo.rules.getRuleById(rule_id, transaction=transaction) new_data_version = rule['data_version'] response = make_response(json.dumps(dict(new_data_version=new_data_version))) response.headers['Content-Type'] = 'application/json' return response
def changeRelease(release, changed_by, transaction, existsCallback, commitCallback, log): """Generic function to change an aspect of a release. It relies on a ReleaseForm existing and does some upfront work and checks before doing anything. It will, for the named release and any found in the 'copyTo' field of the ReleaseForm: - Create the release if it doesn't already exist. - return a 400 Response if the release exists and old_data_version doesn't. - return a 400 Response if the product name in the form doesn't match the existing one. - update the version column of the release table if the one in the form doesn't match it. - if the release already exists, 'existsCallback' will be called. If that function returns True, a 201 Response will be returned upon successful completion. If that function returns False, a 200 Response will be returned instead. @type release: string @param release: The primary release to update. Additional releases found in the 'copyTo' field of the ReleaseForm will also be updated. @type changed_by: string @param changed_by: The username making the change. @type transaction: AUSTransaction object @param transaction: The transaction object to be used for all database operations. @type existsCallback: callable @param existsCallback: The callable to call to determine whether to consider this a "new" change or not. It must receive 3 positional arguments: - the name of the release - the product name from the ReleaseForm - the version from the ReleaseForm @type commitCallback: callable @param commitCallback: The callable to call after all prerequisite checks and updates are done. It must receive 6 positional arguments: - the name of the release - the product name from the ReleaseForm - the version from the ReleaseForm - the data from the ReleaseForm - the most recent version of the data for the release from the database - the old_data_version from the ReleaseForm """ new = True form = ReleaseForm() if not form.validate(): cef_event("Bad input", CEF_WARN, errors=form.errors) return Response(status=400, response=json.dumps(form.errors)) product = form.product.data version = form.version.data incomingData = form.data.data copyTo = form.copyTo.data alias = form.alias.data old_data_version = form.data_version.data # schema_version is an attribute at the root level of a blob. # Endpoints that receive an entire blob can find it there. # Those that don't have to pass it as a form element instead. if getattr(form.schema_version, "data", None): schema_version = form.schema_version.data elif incomingData.get("schema_version"): schema_version = incomingData.get("schema_version") else: return Response(status=400, response="schema_version is required") if getattr(form.hashFunction, "data", None): hashFunction = form.hashFunction.data elif incomingData.get("hashFunction"): hashFunction = incomingData.get("hashFunction") else: hashFunction = None allReleases = [release] if copyTo: allReleases += copyTo for rel in allReleases: try: releaseInfo = dbo.releases.getReleases(name=rel, transaction=transaction)[0] if existsCallback(rel, product, version): new = False # "release" is the one named in the URL (as opposed to the # ones that can be provided in copyTo), and we treat it as # the "primary" one if rel == release: # Make sure that old_data_version is provided, because we need to verify it when updating. if not old_data_version: msg = "Release exists, data_version must be provided" cef_event("Bad input", CEF_WARN, errors=msg, release=rel) return Response(status=400, response=msg) # If the product we're given doesn't match the one in the DB, panic. if product != releaseInfo['product']: msg = "Product name '%s' doesn't match the one on the release object ('%s') for release '%s'" % (product, releaseInfo['product'], rel) cef_event("Bad input", CEF_WARN, errors=msg, release=rel) return Response(status=400, response=msg) if 'hashFunction' in releaseInfo['data'] and hashFunction and hashFunction != releaseInfo['data']['hashFunction']: msg = "hashFunction '%s' doesn't match the one on the release object ('%s') for release '%s'" % (hashFunction, releaseInfo['data']['hashFunction'], rel) cef_event("Bad input", CEF_WARN, errors=msg, release=rel) return Response(status=400, response=msg) # If this isn't the release in the URL... else: # Use the data_version we just grabbed from the dbo. old_data_version = releaseInfo['data_version'] except IndexError: # If the release doesn't already exist, create it, and set old_data_version appropriately. newReleaseData = dict(name=rel, schema_version=schema_version) if hashFunction: newReleaseData['hashFunction'] = hashFunction try: releaseInfo = createRelease(rel, product, version, changed_by, transaction, newReleaseData) except ValueError, e: msg = "Couldn't create release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg, release=rel) return Response(status=400, response=msg) old_data_version = 1 # If the version doesn't match, just update it. This will be the case for nightlies # every time there's a version bump. if version != releaseInfo['version']: log.debug("database version for %s is %s, updating it to %s", rel, releaseInfo['version'], version) try: dbo.releases.updateRelease(name=rel, version=version, changed_by=changed_by, old_data_version=old_data_version, transaction=transaction) except ValueError, e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg, release=rel) return Response(status=400, response=msg) old_data_version += 1
def get(self, type_, change_id, field): try: value = self.get_value(type_, change_id, field) except KeyError, msg: cef_event("Bad input", CEF_WARN, errors=str(msg), field=field) return Response(status=400, response=str(msg))
transaction=transaction) except ValueError, e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg, release=rel) return Response(status=400, response=msg) old_data_version += 1 extraArgs = {} if alias: extraArgs['alias'] = alias try: commitCallback(rel, product, version, incomingData, releaseInfo['data'], old_data_version, extraArgs) except (OutdatedDataError, ValueError), e: msg = "Couldn't update release: %s" % e cef_event("Bad input", CEF_WARN, errors=msg, release=rel) return Response(status=400, response=msg) new_data_version = dbo.releases.getReleases( name=release, transaction=transaction)[0]['data_version'] if new: status = 201 else: status = 200 return make_response(json.dumps(dict(new_data_version=new_data_version)), status) class SingleLocaleView(AdminView): """/releases/[release]/builds/[platform]/[locale]""" def get(self, release, platform, locale):
def get(self, rule_id): rule = dbo.rules.getRule(rule_id) if not rule: return Response(status=404, response='Requested rule does not exist') table = dbo.rules.history try: page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 100)) assert page >= 1 except (ValueError, AssertionError) as msg: cef_event("Bad input", CEF_WARN, errors=msg) return Response(status=400, response=str(msg)) offset = limit * (page - 1) total_count = table.t.count()\ .where(table.rule_id == rule_id)\ .where(table.data_version != null())\ .execute()\ .fetchone()[0] revisions = table.select( where=[table.rule_id == rule_id, table.data_version != null()], limit=limit, offset=offset, order_by=[table.timestamp.asc()], ) _rules = [] _mapping = { # return : db name 'id': 'rule_id', 'mapping': 'mapping', 'priority': 'priority', 'alias': 'alias', 'product': 'product', 'version': 'version', 'background_rate': 'backgroundRate', 'buildID': 'buildID', 'channel': 'channel', 'locale': 'locale', 'distribution': 'distribution', 'buildTarget': 'buildTarget', 'osVersion': 'osVersion', 'distVersion': 'distVersion', 'whitelist': 'whitelist', 'comment': 'comment', 'update_type': 'update_type', 'headerArchitecture': 'headerArchitecture', 'data_version': 'data_version', # specific to revisions 'change_id': 'change_id', 'timestamp': 'timestamp', 'changed_by': 'changed_by', } for rule in revisions: _rules.append( dict((key, rule[db_key]) for key, db_key in _mapping.items())) ret = { 'count': total_count, 'rules': _rules, } return Response(response=json.dumps(ret), mimetype="application/json")
def _post(self, rule_id, transaction, changed_by): # Verify that the rule_id exists. rule = dbo.rules.getRuleById(rule_id, transaction=transaction) if not rule: return Response(status=404) form = EditRuleForm() # Verify that the user has permission for the existing rule _and_ what the rule would become. toCheck = [rule['product']] # Rules can be partially updated - if product is null/None, we won't update that field, so # we shouldn't check its permission. if form.product.data: toCheck.append(form.product.data) for product in toCheck: if not dbo.permissions.hasUrlPermission( changed_by, '/rules/:id', 'POST', urlOptions={'product': product}): msg = "%s is not allowed to alter rules that affect %s" % ( changed_by, product) cef_event('Unauthorized access attempt', CEF_ALERT, msg=msg) return Response(status=401, response=msg) releaseNames = dbo.releases.getReleaseNames() form.mapping.choices = [(item['name'], item['name']) for item in releaseNames] form.mapping.choices.insert(0, ('', 'NULL')) if not form.validate(): cef_event("Bad input", CEF_WARN, errors=form.errors) return Response(status=400, response=json.dumps(form.errors)) what = dict() # We need to be able to support changing AND removing parts of a rule, # and because of how Flask's request object and WTForm's defaults work # this gets a little hary. for k, v in form.data.iteritems(): # data_version is a "special" column, in that it's not part of the # primary data, and shouldn't be updatable by the user. if k == "data_version": continue # We need to check for each column in both the JSON style post # and the regular multipart form data. If the key is not present in # either of these data structures. We treat this cases as no-op # and shouldn't modify the data for that key. # If the key is present we should modify the data as requested. # If a value is an empty string, we should remove that restriction # from the rule (aka, set as NULL in the db). The underlying Form # will have already converted it to None, so we can treat it the # same as a modification here. if (request.json and k in request.json) or k in request.form: what[k] = v dbo.rules.updateRule(changed_by=changed_by, rule_id=rule_id, what=what, old_data_version=form.data_version.data, transaction=transaction) # find out what the next data version is rule = dbo.rules.getRuleById(rule_id, transaction=transaction) new_data_version = rule['data_version'] response = make_response( json.dumps(dict(new_data_version=new_data_version))) response.headers['Content-Type'] = 'application/json' return response