Пример #1
0
    def _post(self, changed_by, transaction):
        if dbo.releases.getReleaseInfo(names=[connexion.request.get_json().get("name")], transaction=transaction, nameOnly=True, limit=1):
            return problem(
                400,
                "Bad Request",
                "Release: %s already exists" % connexion.request.get_json().get("name"),
                ext={"data": "Database already contains the release"},
            )
        try:
            blob = createBlob(connexion.request.get_json().get("blob"))
            name = dbo.releases.insert(
                changed_by=changed_by,
                transaction=transaction,
                name=connexion.request.get_json().get("name"),
                product=connexion.request.get_json().get("product"),
                data=blob,
            )
        except BlobValidationError as e:
            msg = "Couldn't create release: %s" % e
            self.log.warning("Bad input: %s", msg)
            return problem(400, "Bad Request", "Couldn't create release", ext={"data": e.errors})
        except ValueError as e:
            msg = "Couldn't create release: %s" % e
            self.log.warning("Bad input: %s", msg)
            return problem(400, "Bad Request", "Couldn't create release", ext={"data": e.args})

        release = dbo.releases.getReleases(name=name, transaction=transaction, limit=1)[0]
        return Response(status=201, response=json.dumps(dict(new_data_version=release["data_version"])))
Пример #2
0
    def get(self, change_id, field):
        try:
            value = self.get_value(change_id, field)
            data_version = self.get_value(change_id, "data_version")

            prev_id = self.get_prev_id(value, change_id)
            if prev_id:
                previous = self.get_value(prev_id, field)
                prev_data_version = self.get_value(prev_id, "data_version")
            else:
                previous = ""
                prev_data_version = ""

        except (KeyError, TypeError, IndexError) as msg:
            return problem(400, "Bad Request", str(msg))
        except ValueError as msg:
            return problem(404, "Not Found", str(msg))

        value = self.format_value(value)
        previous = self.format_value(previous)

        result = difflib.unified_diff(
            previous.splitlines(),
            value.splitlines(),
            fromfile="Data Version {}".format(prev_data_version),
            tofile="Data Version {}".format(data_version),
            lineterm="")

        return Response('\n'.join(result), content_type='text/plain')
Пример #3
0
    def _put(self, username, permission, changed_by, transaction):
        try:
            if dbo.permissions.getUserPermissions(username, transaction).get(permission):
                # Existing Permission
                if not connexion.request.get_json().get("data_version"):
                    return problem(400, "Bad Request", "'data_version' is missing from request body")

                options_dict = None
                if connexion.request.get_json().get("options"):
                    options_dict = json.loads(connexion.request.get_json().get("options"))
                    if len(options_dict) == 0:
                        options_dict = None

                dbo.permissions.update(where={"username": username, "permission": permission},
                                       what={"options": options_dict}, changed_by=changed_by,
                                       old_data_version=connexion.request.get_json().get("data_version"),
                                       transaction=transaction)
                new_data_version = dbo.permissions.getPermission(username=username, permission=permission,
                                                                 transaction=transaction)['data_version']
                return jsonify(new_data_version=new_data_version)
            else:
                # New Permission
                options_dict = None
                if connexion.request.get_json().get("options"):
                    options_dict = json.loads(connexion.request.get_json().get("options"))
                    if len(options_dict) == 0:
                        options_dict = None
                dbo.permissions.insert(changed_by, transaction=transaction, username=username, permission=permission,
                                       options=options_dict)
                return Response(status=201, response=json.dumps(dict(new_data_version=1)))
        except ValueError as e:
            self.log.warning("Bad input: %s", e.args)
            return problem(400, "Bad Request", str(e.args))
Пример #4
0
    def _post(self, sc_id, transaction, changed_by):
        # TODO: modify UI and clients to stop sending 'change_type' in request body
        sc_rs_permission = self.sc_table.select(where={"sc_id": sc_id},
                                                transaction=transaction, columns=["change_type"])
        if sc_rs_permission:
            change_type = sc_rs_permission[0]["change_type"]
        else:
            return problem(404, "Not Found", "Unknown sc_id",
                           ext={"exception": "No scheduled change for permission required "
                                             "signoff found for given sc_id"})
        what = {}
        for field in connexion.request.get_json():
            if ((change_type == "insert" and field not in ["when", "product", "role", "signoffs_required"]) or
                    (change_type == "update" and field not in ["when", "signoffs_required", "data_version"]) or
                    (change_type == "delete" and field not in ["when", "data_version"])):
                continue
            what[field] = connexion.request.get_json()[field]

        if change_type in ["update", "delete"] and not what.get("data_version", None):
            return problem(400, "Bad Request", "Missing field", ext={"exception": "data_version is missing"})

        if what.get("signoffs_required", None):
            what["signoffs_required"] = int(what["signoffs_required"])

        return super(PermissionsRequiredSignoffScheduledChangeView, self)._post(sc_id, what, transaction, changed_by,
                                                                                connexion.request.get_json().
                                                                                get("sc_data_version", None))
Пример #5
0
    def _post(self, changed_by, transaction):
        if dbo.releases.getReleaseInfo(names=[connexion.request.get_json().get("name")], transaction=transaction, nameOnly=True, limit=1):
            return problem(
                400,
                "Bad Request",
                "Release: %s already exists" % connexion.request.get_json().get("name"),
                ext={"data": "Database already contains the release"},
            )
        try:
            blob = createBlob(connexion.request.get_json().get("blob"))
            name = dbo.releases.insert(
                changed_by=changed_by,
                transaction=transaction,
                name=connexion.request.get_json().get("name"),
                product=connexion.request.get_json().get("product"),
                data=blob,
            )
        except BlobValidationError as e:
            msg = "Couldn't create release: %s" % e
            self.log.warning("Bad input: %s", msg)
            return problem(400, "Bad Request", "Couldn't create release", ext={"data": e.errors})
        except ValueError as e:
            msg = "Couldn't create release: %s" % e
            self.log.warning("Bad input: %s", msg)
            return problem(400, "Bad Request", "Couldn't create release", ext={"data": e.args})

        release = dbo.releases.getReleases(name=name, transaction=transaction, limit=1)[0]
        return Response(status=201, response=json.dumps(dict(new_data_version=release["data_version"])))
Пример #6
0
    def get(self, input_dict):
        if not self.table.select(
            {f: input_dict.get(f)
             for f in self.decisionFields}):
            return problem(404, "Not Found",
                           "Requested Required Signoff does not exist")

        try:
            page = int(connexion.request.args.get('page', 1))
            limit = int(connexion.request.args.get('limit', 100))
        except ValueError as msg:
            self.log.warning("Bad input: %s", msg)
            return problem(400, "Bad Request", str(msg))
        offset = limit * (page - 1)

        query = self.table.history.t.count().where(
            self.table.history.data_version != null())
        for field in self.decisionFields:
            query = query.where(
                getattr(self.table.history, field) == input_dict.get(field))
        total_count = query.execute().fetchone()[0]

        where = [
            getattr(self.table.history, f) == input_dict.get(f)
            for f in self.decisionFields
        ]
        where.append(self.table.history.data_version != null())
        revisions = self.table.history.select(
            where=where,
            limit=limit,
            offset=offset,
            order_by=[self.table.history.timestamp.desc()])

        return jsonify(count=total_count, required_signoffs=revisions)
Пример #7
0
    def _post(self, transaction, changed_by):
        if connexion.request.get_json().get("when", None) is None:
            return problem(400, "Bad Request", "'when' cannot be set to null when scheduling a new change "
                                               "for a Release")
        change_type = connexion.request.get_json().get("change_type")

        what = {}
        for field in connexion.request.get_json():
            if field == "csrf_token":
                continue
            what[field] = connexion.request.get_json()[field]

        if change_type == "update":
            if not what.get("data_version", None):
                return problem(400, "Bad Request", "Missing field", ext={"exception": "data_version is missing"})

            if what.get("data", None):
                what["data"] = createBlob(what.get("data"))

        elif change_type == "insert":
            if not what.get("product", None):
                return problem(400, "Bad Request", "Missing field", ext={"exception": "product is missing"})

            if what.get("data", None):
                what["data"] = createBlob(what.get("data"))
            else:
                return problem(400, "Bad Request", "Missing field", ext={"exception": "Missing blob 'data' value"})

        elif change_type == "delete":
            if not what.get("data_version", None):
                return problem(400, "Bad Request", "Missing field", ext={"exception": "data_version is missing"})

        return super(ReleaseScheduledChangesView, self)._post(what, transaction, changed_by, change_type)
Пример #8
0
    def _post(self, transaction, changed_by):
        # a Post here creates a new rule
        what, mapping_values, fallback_mapping_values = process_rule_form(
            connexion.request.get_json())

        if what.get("mapping", None) is None:
            return problem(400, "Bad Request",
                           "mapping value cannot be set to null/empty")

        if what.get("mapping", None) is not None and len(mapping_values) != 1:
            return problem(
                400, "Bad Request",
                "Invalid mapping value. No release name found in DB")

        if what.get("fallbackMapping"
                    ) is not None and len(fallback_mapping_values) != 1:
            return problem(
                400, "Bad Request",
                "Invalid fallbackMapping value. No release name found in DB")

        # Solves Bug https://bugzilla.mozilla.org/show_bug.cgi?id=1361158
        what.pop("csrf_token", None)

        alias = what.get("alias", None)
        if alias is not None and dbo.rules.getRule(alias):
            return problem(400, "Bad Request", "Rule with alias exists.")

        rule_id = dbo.rules.insert(changed_by=changed_by,
                                   transaction=transaction,
                                   **what)
        return Response(status=200, response=str(rule_id))
Пример #9
0
    def _post(self, username, permission, changed_by, transaction):
        if not dbo.permissions.getUserPermissions(
                username, transaction=transaction).get(permission):
            return problem(status=404,
                           title="Not Found",
                           detail="Requested user permission"
                           " %s not found for %s" % (permission, username))
        try:
            # Existing Permission
            if not connexion.request.get_json().get("data_version"):
                return problem(400, "Bad Request",
                               "'data_version' is missing from request body")
            options_dict = None
            if connexion.request.get_json().get("options"):
                options_dict = json.loads(
                    connexion.request.get_json().get("options"))

            dbo.permissions.update(
                where={
                    "username": username,
                    "permission": permission
                },
                what={"options": options_dict},
                changed_by=changed_by,
                old_data_version=connexion.request.get_json().get(
                    "data_version"),
                transaction=transaction)
            new_data_version = dbo.permissions.getPermission(
                username=username,
                permission=permission,
                transaction=transaction)['data_version']
            return jsonify(new_data_version=new_data_version)
        except ValueError as e:
            self.log.warning("Bad input: %s", e.args)
            return problem(status=400, title="Bad Request", detail=str(e.args))
Пример #10
0
    def _post(self, transaction, changed_by):
        # a Post here creates a new rule
        what, mapping_values, fallback_mapping_values = process_rule_form(
            connexion.request.json)

        if what.get('mapping', None) is not None and len(mapping_values) != 1:
            return problem(
                400, 'Bad Request',
                'Invalid mapping value. No release name found in DB')

        if what.get('fallbackMapping',
                    None) is not None and len(fallback_mapping_values) != 1:
            return problem(
                400, 'Bad Request',
                'Invalid fallbackMapping value. No release name found in DB')

        # Solves Bug https://bugzilla.mozilla.org/show_bug.cgi?id=1361158
        what.pop("csrf_token", None)

        alias = what.get('alias', None)
        if alias is not None and dbo.rules.getRule(alias):
            return problem(400, 'Bad Request', 'Rule with alias exists.')

        rule_id = dbo.rules.insert(changed_by=changed_by,
                                   transaction=transaction,
                                   **what)
        return Response(status=200, response=str(rule_id))
Пример #11
0
    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 problem(status=404, title="Not Found", detail="Requested rule wasn't found",
                           ext={"exception": "Requested rule does not exist"})

        what, mapping_values, fallback_mapping_values = process_rule_form(connexion.request.get_json())

        # If 'mapping' key is present in request body but is either blank/null
        if 'mapping' in what and what.get('mapping', None) is None:
            return problem(400, 'Bad Request', 'mapping value cannot be set to null/empty')

        if what.get('mapping', None) is not None and len(mapping_values) != 1:
            return problem(400, 'Bad Request', 'Invalid mapping value. No release name found in DB')

        if what.get('fallbackMapping') is not None and len(fallback_mapping_values) != 1:
            return problem(400, 'Bad Request', 'Invalid fallbackMapping value. No release name found in DB')

        # Solves https://bugzilla.mozilla.org/show_bug.cgi?id=1361158
        what.pop("csrf_token", None)
        data_version = what.pop("data_version", None)

        dbo.rules.update(changed_by=changed_by, where={"rule_id": id_or_alias}, what=what,
                         old_data_version=data_version, transaction=transaction)

        # find out what the next data version is
        rule = dbo.rules.getRule(id_or_alias, transaction=transaction)
        return jsonify(new_data_version=rule["data_version"])
Пример #12
0
    def _post(self, transaction, changed_by):
        if connexion.request.get_json().get("when", None) is None:
            return problem(400, "Bad Request", "'when' cannot be set to null when scheduling a new change "
                                               "for a Permission")
        change_type = connexion.request.get_json().get("change_type")

        what = {}
        for field in connexion.request.get_json():
            if field == "csrf_token":
                continue

            what[field] = connexion.request.get_json()[field]

        if what.get("options", None):
            what["options"] = json.loads(what.get("options"))
            if len(what["options"]) == 0:
                what["options"] = None

        if change_type in ["update", "delete"]:
            if not what.get("data_version", None):
                return problem(400, "Bad Request", "Missing field", ext={"exception": "data_version is missing"})
            else:
                what["data_version"] = int(what["data_version"])

        return super(PermissionScheduledChangesView, self)._post(what, transaction, changed_by, change_type)
Пример #13
0
    def _post(self, sc_id, transaction, changed_by):
        # TODO: modify UI and clients to stop sending 'change_type' in request body
        sc_permission = self.sc_table.select(where={"sc_id": sc_id}, transaction=transaction, columns=["change_type"])
        if sc_permission:
            change_type = sc_permission[0]["change_type"]
        else:
            return problem(404, "Not Found", "Unknown sc_id",
                           ext={"exception": "No scheduled change for permission found for given sc_id"})

        # TODO: UI passes too many extra non-required fields apart from 'change_type' in request body
        # Only required fields must be passed to DB layer
        what = {}
        for field in connexion.request.get_json():

            # When editing an existing Scheduled Change for an for an existing Permission only options may be
            # provided. Because edits are identified by sc_id (in the URL), permission and username
            # are not required (nor allowed, because they are PK fields).
            # When editing an existing Scheduled Change for a new Permission, any field
            # may be changed.
            if ((change_type == "delete" and field not in ["when", "data_version"]) or
                    (change_type == "update" and field not in ["when", "options", "data_version"]) or
                    (change_type == "insert" and field not in ["when", "options", "permission", "username"])):
                continue

            what[field] = connexion.request.get_json()[field]

        if change_type in ["update", "delete"] and not what.get("data_version", None):
            return problem(400, "Bad Request", "Missing field", ext={"exception": "data_version is missing"})

        if what.get("options", None):
            what["options"] = json.loads(what["options"])

        return super(PermissionScheduledChangeView, self)._post(sc_id, what, transaction, changed_by,
                                                                connexion.request.get_json().get("sc_data_version"))
Пример #14
0
    def _delete(self, release, changed_by, transaction):
        releases = dbo.releases.getReleaseInfo(names=[release],
                                               nameOnly=True,
                                               limit=1)
        if not releases:
            return problem(404, "Not Found", "Release: %s not found" % release)
        release = releases[0]

        # query argument i.e. data_version  is also required.
        # All input value validations already defined in swagger specification and carried out by connexion.
        try:
            old_data_version = int(connexion.request.args.get("data_version"))
            dbo.releases.delete(where={"name": release["name"]},
                                changed_by=changed_by,
                                old_data_version=old_data_version,
                                transaction=transaction)
        except ReadOnlyError as e:
            msg = "Couldn't delete release: %s" % e
            self.log.warning("Bad input: %s", msg)
            return problem(403,
                           "Forbidden",
                           "Couldn't delete %s. Release is marked read only" %
                           release["name"],
                           ext={"data": e.args})

        return Response(status=200)
Пример #15
0
    def _put(self, username, permission, changed_by, transaction):
        try:
            if dbo.permissions.getUserPermissions(username, transaction).get(permission):
                # Existing Permission
                if not connexion.request.get_json().get("data_version"):
                    return problem(400, "Bad Request", "'data_version' is missing from request body")

                options_dict = None
                if connexion.request.get_json().get("options"):
                    options_dict = json.loads(connexion.request.get_json().get("options"))
                    if len(options_dict) == 0:
                        options_dict = None

                dbo.permissions.update(where={"username": username, "permission": permission},
                                       what={"options": options_dict}, changed_by=changed_by,
                                       old_data_version=connexion.request.get_json().get("data_version"),
                                       transaction=transaction)
                new_data_version = dbo.permissions.getPermission(username=username, permission=permission,
                                                                 transaction=transaction)['data_version']
                return jsonify(new_data_version=new_data_version)
            else:
                # New Permission
                options_dict = None
                if connexion.request.get_json().get("options"):
                    options_dict = json.loads(connexion.request.get_json().get("options"))
                    if len(options_dict) == 0:
                        options_dict = None
                dbo.permissions.insert(changed_by, transaction=transaction, username=username, permission=permission,
                                       options=options_dict)
                return Response(status=201, response=json.dumps(dict(new_data_version=1)))
        except ValueError as e:
            self.log.warning("Bad input: %s", e.args)
            return problem(400, "Bad Request", str(e.args))
Пример #16
0
    def _post(self, transaction, changed_by):
        if connexion.request.get_json().get("when", None) is None:
            return problem(400, "Bad Request", "'when' cannot be set to null when scheduling a new change "
                                               "for a Permissions Required Signoff")
        change_type = connexion.request.get_json().get("change_type")

        what = {}
        for field in connexion.request.get_json():
            if field == "csrf_token" or change_type == "insert" and field == "data_version":
                continue
            what[field] = connexion.request.get_json()[field]

        if change_type == "update":
            for field in ["signoffs_required", "data_version"]:
                if not what.get(field, None):
                    return problem(400, "Bad Request", "Missing field", ext={"exception": "%s is missing" % field})
                else:
                    what[field] = int(what[field])

        elif change_type == "insert":
            if not what.get("signoffs_required", None):
                return problem(400, "Bad Request", "Missing field", ext={"exception": "signoffs_required is missing"})
            else:
                what["signoffs_required"] = int(what["signoffs_required"])

        elif change_type == "delete":
            if not what.get("data_version", None):
                return problem(400, "Bad Request", "Missing field", ext={"exception": "data_version is missing"})
            else:
                what["data_version"] = int(what["data_version"])

        return super(PermissionsRequiredSignoffsScheduledChangesView, self)._post(what, transaction, changed_by,
                                                                                  change_type)
Пример #17
0
    def get(self, input_dict):
        if not self.table.select({f: input_dict.get(f) for f in self.decisionFields}):
            return problem(404, "Not Found", "Requested Required Signoff does not exist")

        try:
            page = int(connexion.request.args.get('page', 1))
            limit = int(connexion.request.args.get('limit', 100))
        except ValueError as msg:
            self.log.warning("Bad input: %s", msg)
            return problem(400, "Bad Request", str(msg))
        offset = limit * (page - 1)

        where_count = [self.table.history.data_version != null()]
        for field in self.decisionFields:
            where_count.append(getattr(self.table.history, field) == input_dict.get(field))
        total_count = self.table.history.count(where=where_count)

        where = [getattr(self.table.history, f) == input_dict.get(f) for f in self.decisionFields]
        where.append(self.table.history.data_version != null())
        revisions = self.table.history.select(
            where=where, limit=limit, offset=offset,
            order_by=[self.table.history.timestamp.desc()]
        )

        return jsonify(count=total_count, required_signoffs=revisions)
Пример #18
0
    def _post(self, sc_id, transaction, changed_by):
        # TODO: modify UI and clients to stop sending 'change_type' in request body
        sc_release = self.sc_table.select(where={"sc_id": sc_id}, transaction=transaction, columns=["change_type"])
        if sc_release:
            change_type = sc_release[0]["change_type"]
        else:
            return problem(404, "Not Found", "Unknown sc_id",
                           ext={"exception": "No scheduled change for release found for given sc_id"})

        what = {}
        for field in connexion.request.get_json():
            # Only data may be changed when editing an existing Scheduled Change for
            # an existing Release. Name cannot be changed because it is a PK field, and product
            # cannot be changed because it almost never makes sense to (and can be done
            # by deleting/recreating instead).
            # Any Release field may be changed when editing an Scheduled Change for a new Release
            if ((change_type == "delete" and field not in ["when", "data_version"]) or
                    (change_type == "update" and field not in ["when", "data", "data_version"]) or
                    (change_type == "insert" and field not in ["when", "name", "product", "data"])):
                continue

            what[field] = connexion.request.get_json()[field]

        if change_type in ["update", "delete"] and not what.get("data_version", None):
            return problem(400, "Bad Request", "Missing field", ext={"exception": "data_version is missing"})

        elif change_type == "insert" and 'data' in what and not what.get("data", None):
            # edit scheduled change for new release
            return problem(400, "Bad Request", "Null/Empty Value",
                           ext={"exception": "data cannot be set to null when scheduling insertion of a new release"})
        if what.get("data", None):
            what["data"] = createBlob(what.get("data"))

        return super(ReleaseScheduledChangeView, self)._post(sc_id, what, transaction, changed_by,
                                                             connexion.request.get_json().get("sc_data_version", None))
Пример #19
0
    def _post(self, transaction, changed_by):
        if connexion.request.get_json().get("when", None) is None:
            return problem(400, "Bad Request", "'when' cannot be set to null when scheduling a new change "
                                               "for a Permission")
        change_type = connexion.request.get_json().get("change_type")

        what = {}
        for field in connexion.request.get_json():
            if field == "csrf_token":
                continue

            what[field] = connexion.request.get_json()[field]

        if what.get("options", None):
            what["options"] = json.loads(what.get("options"))
            if len(what["options"]) == 0:
                what["options"] = None

        if change_type in ["update", "delete"]:
            if not what.get("data_version", None):
                return problem(400, "Bad Request", "Missing field", ext={"exception": "data_version is missing"})
            else:
                what["data_version"] = int(what["data_version"])

        return super(PermissionScheduledChangesView, self)._post(what, transaction, changed_by, change_type)
Пример #20
0
    def get(self, change_id, field):
        try:
            value = self.get_value(change_id, field)
            data_version = self.get_value(change_id, "data_version")

            prev_id = self.get_prev_id(value, change_id)
            if prev_id:
                previous = self.get_value(prev_id, field)
                prev_data_version = self.get_value(prev_id, "data_version")
            else:
                previous = ""
                prev_data_version = ""

        except (KeyError, TypeError, IndexError) as msg:
            return problem(400, "Bad Request", str(msg))
        except ValueError as msg:
            return problem(404, "Not Found", str(msg))

        value = self.format_value(value)
        previous = self.format_value(previous)

        result = difflib.unified_diff(
            previous.splitlines(),
            value.splitlines(),
            fromfile="Data Version {}".format(prev_data_version),
            tofile="Data Version {}".format(data_version),
            lineterm=""
        )

        return Response('\n'.join(result), content_type='text/plain')
Пример #21
0
    def _post(self, what, transaction, changed_by, change_type):
        if change_type not in ["insert", "update", "delete"]:
            return problem(400, "Bad Request", "Invalid or missing change_type")

        if is_when_present_and_in_past_validator(what):
            return problem(400, "Bad Request", "Changes may not be scheduled in the past")

        sc_id = self.sc_table.insert(changed_by, transaction, **what)
        return jsonify(sc_id=sc_id)
Пример #22
0
    def _post(self, what, transaction, changed_by, change_type):
        if change_type not in ["insert", "update", "delete"]:
            return problem(400, "Bad Request", "Invalid or missing change_type")

        if is_when_present_and_in_past_validator(what):
            return problem(400, "Bad Request", "Changes may not be scheduled in the past")

        sc_id = self.sc_table.insert(changed_by, transaction, **what)
        return jsonify(sc_id=sc_id)
Пример #23
0
 def get(self, change_id, field):
     try:
         value = self.get_value(change_id, field)
     except KeyError as msg:
         self.log.warning("Bad input: %s", field)
         return problem(400, "Bad Request", str(msg))
     except ValueError as msg:
         return problem(404, "Not Found", str(msg))
     value = self.format_value(value)
     return Response(value, content_type='text/plain')
Пример #24
0
 def get(self, change_id, field):
     try:
         value = self.get_value(change_id, field)
     except KeyError as msg:
         self.log.warning("Bad input: %s", field)
         return problem(400, "Bad Request", str(msg))
     except ValueError as msg:
         return problem(404, "Not Found", str(msg))
     value = self.format_value(value)
     return Response(value, content_type='text/plain')
Пример #25
0
    def _delete(self, sc_id, transaction, changed_by):
        where = {"sc_id": sc_id}
        sc = self.sc_table.select(where, transaction, columns=["sc_id"])
        if not sc:
            return problem(404, "Bad Request", "Scheduled change does not exist")

        if not connexion.request.args.get("data_version", None):
            return problem(400, "Bad Request", "data_version is missing")

        self.sc_table.delete(where, changed_by, int(connexion.request.args.get("data_version")), transaction)
        return Response(status=200)
Пример #26
0
    def _delete(self, sc_id, transaction, changed_by):
        where = {"sc_id": sc_id}
        sc = self.sc_table.select(where, transaction, columns=["sc_id"])
        if not sc:
            return problem(404, "Bad Request", "Scheduled change does not exist")

        if not connexion.request.args.get("data_version", None):
            return problem(400, "Bad Request", "data_version is missing")

        self.sc_table.delete(where, changed_by, int(connexion.request.args.get("data_version")), transaction)
        return Response(status=200)
Пример #27
0
    def _post(self, what, transaction, changed_by, change_type):
        if change_type not in ["insert", "update", "delete"]:
            return problem(400, "Bad Request", "Invalid or missing change_type")

        if is_when_present_and_in_past_validator(what):
            return problem(400, "Bad Request", "Changes may not be scheduled in the past")

        sc_id = self.sc_table.insert(changed_by, transaction, **what)
        signoffs = {}
        for signoff in self.sc_table.signoffs.select(where={"sc_id": sc_id}, transaction=transaction):
            signoffs[signoff["username"]] = signoff["role"]
        return jsonify(sc_id=sc_id, signoffs=signoffs)
Пример #28
0
    def _post(self, what, transaction, changed_by, change_type):
        if change_type not in ["insert", "update", "delete"]:
            return problem(400, "Bad Request", "Invalid or missing change_type")

        if is_when_present_and_in_past_validator(what):
            return problem(400, "Bad Request", "Changes may not be scheduled in the past")

        sc_id = self.sc_table.insert(changed_by, transaction, **what)
        signoffs = {}
        for signoff in self.sc_table.signoffs.select(where={"sc_id": sc_id}, transaction=transaction):
            signoffs[signoff["username"]] = signoff["role"]
        return jsonify(sc_id=sc_id, signoffs=signoffs)
Пример #29
0
 def decorated(*args, **kwargs):
     username = request.environ.get('REMOTE_USER', request.environ.get("HTTP_REMOTE_USER"))
     if not username:
         log.warning("Login Required")
         return problem(401, 'Unauthenticated', 'Login Required')
     elif not dbo.isKnownUser(username):
         # Was identified some situations where a REMOTE_USER can be changed through
         # the 'Remote-User' header.
         # This check prevents the request reaches database layer when the user is not
         # in permissions table.
         # https://bugzilla.mozilla.org/show_bug.cgi?id=1457905
         log.warning("Authorization Required")
         return problem(403, 'Forbidden', 'Authorization Required')
     return f(*args, changed_by=username, **kwargs)
Пример #30
0
 def decorated(*args, **kwargs):
     username = request.environ.get('REMOTE_USER',
                                    request.environ.get("HTTP_REMOTE_USER"))
     if not username:
         log.warning("Login Required")
         return problem(401, 'Unauthenticated', 'Login Required')
     elif not dbo.isKnownUser(username):
         # Was identified some situations where a REMOTE_USER can be changed through
         # the 'Remote-User' header.
         # This check prevents the request reaches database layer when the user is not
         # in permissions table.
         # https://bugzilla.mozilla.org/show_bug.cgi?id=1457905
         log.warning("Authorization Required")
         return problem(403, 'Forbidden', 'Authorization Required')
     return f(*args, changed_by=username, **kwargs)
Пример #31
0
    def get(self, sc_id):
        sc = self.sc_table.select(where={"sc_id": sc_id})
        if not sc:
            return problem(404, "Bad Request", "Scheduled change does not exist")

        scheduled_change = add_signoff_information(sc[0], self.table, self.sc_table)
        return jsonify({"scheduled_change": scheduled_change})
Пример #32
0
 def get(self, username, permission):
     try:
         perm = dbo.permissions.getUserPermissions(username)[permission]
     except KeyError:
         return problem(404, "Not Found", "Requested user permission"
                                          " %s not found for %s" % (permission, username))
     return jsonify(perm)
Пример #33
0
 def get(self, username, permission):
     try:
         perm = dbo.permissions.getUserPermissions(username)[permission]
     except KeyError:
         return problem(404, "Not Found", "Requested user permission"
                                          " %s not found for %s" % (permission, username))
     return jsonify(perm)
Пример #34
0
    def _delete(self, username, permission, changed_by, transaction):
        if not dbo.permissions.getUserPermissions(username, transaction=transaction).get(permission):
            return problem(404, "Not Found", "Requested user permission"
                                             " %s not found for %s" % (permission, username))
        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.

            old_data_version = int(connexion.request.args.get("data_version"))
            dbo.permissions.delete(where={"username": username, "permission": permission},
                                   changed_by=changed_by, old_data_version=old_data_version, transaction=transaction)
            return Response(status=200)
        except ValueError as e:
            self.log.warning("Bad input: %s", e.args)
            return problem(400, "Bad Request", str(e.args))
Пример #35
0
    def _delete(self, username, permission, changed_by, transaction):
        if not dbo.permissions.getUserPermissions(username, transaction=transaction).get(permission):
            return problem(404, "Not Found", "Requested user permission"
                                             " %s not found for %s" % (permission, username))
        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.

            old_data_version = int(connexion.request.args.get("data_version"))
            dbo.permissions.delete(where={"username": username, "permission": permission},
                                   changed_by=changed_by, old_data_version=old_data_version, transaction=transaction)
            return Response(status=200)
        except ValueError as e:
            self.log.warning("Bad input: %s", e.args)
            return problem(400, "Bad Request", str(e.args))
Пример #36
0
    def get(self, username, changed_by):
        permissions = dbo.permissions.getUserPermissions(username, changed_by)

        if not permissions:
            return problem(status=404, title="Not Found", detail="No permission found for username %s" % username)
        roles = {r["role"]: {"data_version": r["data_version"]} for r in dbo.permissions.getUserRoles(username)}
        return jsonify({"username": username, "permissions": permissions, "roles": roles})
Пример #37
0
def ise(error):
    capture_exception(error)
    log.error("Caught ISE 500 error.")
    log.debug("Request path is: %s", request.path)
    log.debug("Request environment is: %s", request.environ)
    log.debug("Request headers are: %s", request.headers)
    return problem(500, "Internal Server Error", "Internal Server Error")
Пример #38
0
    def _put(self, release, changed_by, transaction):
        releases = dbo.releases.getReleaseInfo(names=[release],
                                               nameOnly=True,
                                               limit=1)
        if not releases:
            return problem(404, "Not Found", "Release: %s not found" % release)

        data_version = connexion.request.get_json().get("data_version")
        is_release_read_only = dbo.releases.isReadOnly(release)

        if connexion.request.get_json().get("read_only"):
            if not is_release_read_only:
                dbo.releases.update(where={"name": release},
                                    what={"read_only": True},
                                    changed_by=changed_by,
                                    old_data_version=data_version,
                                    transaction=transaction)
                data_version += 1
        else:
            dbo.releases.update(where={"name": release},
                                what={"read_only": False},
                                changed_by=changed_by,
                                old_data_version=data_version,
                                transaction=transaction)
            data_version += 1
        return Response(status=201,
                        response=json.dumps(
                            dict(new_data_version=data_version)))
Пример #39
0
 def _post(self, release, transaction, changed_by):
     try:
         return self.revert_to_revision(
             get_object_callback=lambda: self._get_release(release),
             change_field='name',
             get_what_callback=self._get_what,
             changed_by=changed_by,
             response_message='Excellent!',
             transaction=transaction,
             obj_not_found_msg='bad release')
     except BlobValidationError as e:
         self.log.warning("Bad input: %s", e.args)
         return problem(400, "Bad Request", "Invalid input blob: %s" % e.args, ext={"data": e.errors})
     except ValueError as e:
         self.log.warning("Bad input: %s", e.args)
         return problem(400, "Bad Request", "Invalid input", ext={"data": e.args})
Пример #40
0
    def get(self, release):
        try:
            is_release_read_only = dbo.releases.isReadOnly(name=release, limit=1)
        except KeyError as e:
            return problem(404, "Not Found", json.dumps(e.args))

        return jsonify(read_only=is_release_read_only)
Пример #41
0
 def decorated(*args, **kwargs):
     username = verified_userinfo(request, app.config["AUTH_DOMAIN"], app.config["AUTH_AUDIENCE"])["email"]
     if not username:
         log.warning("Login Required")
         return problem(401, "Unauthenticated", "Login Required")
     # Machine to machine accounts are identified by uninformative clientIds
     # In order to keep Balrog permissions more readable, we map them to
     # more useful usernames, which are stored in the app config.
     if "@" not in username:
         username = app.config["M2M_ACCOUNT_MAPPING"].get(username, username)
     # Even if the user has provided a valid access token, we don't want to assume
     # that person should be able to access Balrog (in case auth0 is not configured
     # to be restrictive enough.
     elif not dbo.isKnownUser(username):
         log.warning("Authorization Required")
         return problem(403, "Forbidden", "Authorization Required")
     return f(*args, changed_by=username, **kwargs)
Пример #42
0
    def _delete(self, release, changed_by, transaction):
        releases = dbo.releases.getReleaseInfo(names=[release], nameOnly=True, limit=1)
        if not releases:
            return problem(404, "Not Found", "Release: %s not found" % release)
        release = releases[0]

        # query argument i.e. data_version  is also required.
        # All input value validations already defined in swagger specification and carried out by connexion.
        try:
            old_data_version = int(connexion.request.args.get("data_version"))
            dbo.releases.delete(where={"name": release["name"]}, changed_by=changed_by, old_data_version=old_data_version, transaction=transaction)
        except ReadOnlyError as e:
            msg = "Couldn't delete release: %s" % e
            self.log.warning("Bad input: %s", msg)
            return problem(403, "Forbidden", "Couldn't delete %s. Release is marked read only" % release["name"], ext={"data": e.args})

        return Response(status=200)
Пример #43
0
    def get_revisions(
        self,
        get_object_callback,
        history_filters_callback,
        revisions_order_by,
        process_revisions_callback=None,
        obj_not_found_msg="Requested object does not exist",
        response_key="revisions",
    ):
        """Get revisions for Releases, Rules or ScheduledChanges.
        Uses callable parameters to handle specific AUS object data.

        @param get_object_callback: A callback to get requested AUS object.
        @type get_object_callback: callable

        @param history_filters_callback: A callback that get the filters list
        to query the history.
        @type history_filters_callback: callable

        @param process_revisions_callback: A callback that process revisions
        according to the requested AUS object.
        @type process_revisions_callback: callable

        @param revisions_order_by: Fields list to sort history.
        @type revisions_order_by: list

        @param obj_not_found_msg: Error message for not found AUS object.
        @type obj_not_found_msg: string

        @param response_key: Dictionary key to wrap returned revisions.
        @type response_key: string
        """
        page = int(connexion.request.args.get("page", 1))
        limit = int(connexion.request.args.get("limit", 10))

        obj = get_object_callback()
        if not obj:
            return problem(status=404,
                           title="Not Found",
                           detail=obj_not_found_msg)

        offset = limit * (page - 1)

        filters = history_filters_callback(obj)
        total_count = self.history_table.count(where=filters)

        revisions = self.history_table.select(where=filters,
                                              limit=limit,
                                              offset=offset,
                                              order_by=revisions_order_by)

        if process_revisions_callback:
            revisions = process_revisions_callback(revisions)

        ret = dict()
        ret[response_key] = revisions
        ret["count"] = total_count
        return jsonify(ret)
Пример #44
0
 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" % messages
         log.warning("Bad input: %s", msg)
         log.warning(e)
         # using connexion.problem results in TypeError: 'ConnexionResponse' object is not callable
         # hence using flask.Response but modifying response's json data into connexion.problem format
         # for validation purpose
         return problem(400,
                        "Bad Request",
                        "OutdatedDataError",
                        ext={"exception": msg})
     except UpdateMergeError as e:
         msg = "Couldn't perform the request %s due to merge error. " "Is there a scheduled change that conflicts with yours?" % messages
         log.warning("Bad input: %s", msg)
         log.warning(e)
         return problem(400,
                        "Bad Request",
                        "UpdateMergeError",
                        ext={"exception": msg})
     except ChangeScheduledError as e:
         msg = "Couldn't perform the request %s due a conflict with a scheduled change. " % messages
         msg += str(e)
         log.warning("Bad input: %s", msg)
         log.warning(e)
         return problem(400,
                        "Bad Request",
                        "ChangeScheduledError",
                        ext={"exception": msg})
     except (PermissionDeniedError, AuthError) as e:
         msg = "Permission denied to perform the request. {}".format(e)
         log.warning(msg)
         return problem(403,
                        "Forbidden",
                        "PermissionDeniedError",
                        ext={"exception": msg})
     except ValueError as e:
         msg = "Bad input: {}".format(e)
         log.warning(msg)
         return problem(400,
                        "Bad Request",
                        "ValueError",
                        ext={"exception": msg})
Пример #45
0
    def _post(self, transaction, changed_by):
        if connexion.request.get_json().get("when", None) is None:
            return problem(
                400, "Bad Request",
                "'when' cannot be set to null when scheduling a new change "
                "for a Permissions Required Signoff")
        change_type = connexion.request.get_json().get("change_type")

        what = {}
        for field in connexion.request.get_json():
            if field == "csrf_token" or change_type == "insert" and field == "data_version":
                continue
            what[field] = connexion.request.get_json()[field]

        if change_type == "update":
            for field in ["signoffs_required", "data_version"]:
                if not what.get(field, None):
                    return problem(400,
                                   "Bad Request",
                                   "Missing field",
                                   ext={"exception": "%s is missing" % field})
                else:
                    what[field] = int(what[field])

        elif change_type == "insert":
            if not what.get("signoffs_required", None):
                return problem(
                    400,
                    "Bad Request",
                    "Missing field",
                    ext={"exception": "signoffs_required is missing"})
            else:
                what["signoffs_required"] = int(what["signoffs_required"])

        elif change_type == "delete":
            if not what.get("data_version", None):
                return problem(400,
                               "Bad Request",
                               "Missing field",
                               ext={"exception": "data_version is missing"})
            else:
                what["data_version"] = int(what["data_version"])

        return super(PermissionsRequiredSignoffsScheduledChangesView,
                     self)._post(what, transaction, changed_by, change_type)
Пример #46
0
    def _post(self, transaction, changed_by):
        if connexion.request.get_json().get("when", None) is None:
            return problem(400, "Bad Request", "'when' cannot be set to null when scheduling a new change "
                                               "for a Rule")
        if connexion.request.get_json():
            change_type = connexion.request.get_json().get("change_type")
        else:
            change_type = connexion.request.values.get("change_type")

        what = {}
        delete_change_type_allowed_fields = ["telemetry_product", "telemetry_channel", "telemetry_uptake", "when",
                                             "rule_id", "data_version", "change_type"]
        for field in connexion.request.get_json():
            # TODO: currently UI passes extra rule model fields in change_type == 'delete' request body. Fix it and
            # TODO: change the below operation from filter/pop to throw Error when extra fields are passed.
            if (field == "csrf_token" or (change_type == "insert" and field in ["rule_id", "data_version"]) or
                    (change_type == "delete" and field not in delete_change_type_allowed_fields)):
                continue

            if field in ["rule_id", "data_version"]:
                what[field] = int(connexion.request.get_json()[field])
            else:
                what[field] = connexion.request.get_json()[field]

        # Explicit checks for each change_type
        if change_type in ["update", "delete"]:
            for field in ["rule_id", "data_version"]:
                if not what.get(field, None):
                    return problem(400, "Bad Request", "Missing field", ext={"exception": "%s is missing" % field})

        elif change_type == "insert":
            for field in ["update_type", "backgroundRate", "priority"]:
                if what.get(field, None) is None or \
                        isinstance(what.get(field), str_types) and what.get(field).strip() == '':
                    return problem(400, "Bad Request", "Null/Empty Value",
                                   ext={"exception": "%s cannot be set to null/empty "
                                                     "when scheduling insertion of a new rule" % field})

        if change_type in ["update", "insert"]:
            rule_dict, mapping_values, fallback_mapping_values = process_rule_form(what)
            what = rule_dict

            # if 'mapping' key is present in request body but is null
            if 'mapping' in what:
                if what.get('mapping', None) is None:
                    return problem(400, 'Bad Request', 'mapping value cannot be set to null/empty')

            # if 'mapping' key-value is null/not-present-in-request-body and change_type == "insert"
            if what.get('mapping', None) is None:
                if change_type == "insert":
                    return problem(400, 'Bad Request', 'mapping value cannot be set to null/empty')

            # If mapping is present in request body and is non-empty string which does not match any release name
            if what.get('mapping') is not None and len(mapping_values) != 1:
                return problem(400, 'Bad Request', 'Invalid mapping value. No release name found in DB')

            if what.get('fallbackMapping') is not None and len(fallback_mapping_values) != 1:
                return problem(400, 'Bad Request', 'Invalid fallbackMapping value. No release name found in DB')

        return super(RuleScheduledChangesView, self)._post(what, transaction, changed_by, change_type)
Пример #47
0
    def get_revisions(self,
                      get_object_callback,
                      history_filters_callback,
                      revisions_order_by,
                      process_revisions_callback=None,
                      obj_not_found_msg='Requested object does not exist',
                      response_key='revisions'):
        """Get revisions for Releases, Rules or ScheduledChanges.
        Uses callable parameters to handle specific AUS object data.

        @param get_object_callback: A callback to get requested AUS object.
        @type get_object_callback: callable

        @param history_filters_callback: A callback that get the filters list
        to query the history.
        @type history_filters_callback: callable

        @param process_revisions_callback: A callback that process revisions
        according to the requested AUS object.
        @type process_revisions_callback: callable

        @param revisions_order_by: Fields list to sort history.
        @type revisions_order_by: list

        @param obj_not_found_msg: Error message for not found AUS object.
        @type obj_not_found_msg: string

        @param response_key: Dictionary key to wrap returned revisions.
        @type response_key: string
        """
        page = int(connexion.request.args.get('page', 1))
        limit = int(connexion.request.args.get('limit', 10))

        obj = get_object_callback()
        if not obj:
            return problem(status=404, title="Not Found", detail=obj_not_found_msg)

        offset = limit * (page - 1)

        filters = history_filters_callback(obj)
        total_count = self.history_table.t.count()\
                                          .where(and_(*filters))\
                                          .execute().fetchone()[0]

        revisions = self.history_table.select(
            where=filters,
            limit=limit,
            offset=offset,
            order_by=revisions_order_by)

        if process_revisions_callback:
            revisions = process_revisions_callback(revisions)

        ret = dict()
        ret[response_key] = revisions
        ret['count'] = total_count
        return jsonify(ret)
Пример #48
0
 def get(self, release, platform, locale):
     try:
         locale = dbo.releases.getLocale(release, platform, locale)
     except KeyError as e:
         return problem(404, "Not Found", json.dumps(e.args))
     data_version = dbo.releases.getReleases(name=release)[0]['data_version']
     headers = {'X-Data-Version': data_version}
     headers.update(get_csrf_headers())
     return Response(response=json.dumps(locale), mimetype='application/json', headers=headers)
Пример #49
0
    def get(self, username):
        current_user = connexion.request.environ.get('REMOTE_USER', connexion.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 problem(status=403, title="Forbidden",
                               detail="You are not authorized to view permissions of other users.")

        permissions = dbo.permissions.getUserPermissions(username)
        if not permissions:
            return problem(status=404, title="Not Found", detail="No permission found for username %s" % username)
        roles = {r["role"]: {"data_version": r["data_version"]} for r in dbo.permissions.getUserRoles(username)}
        return jsonify({"username": username, "permissions": permissions, "roles": roles})
Пример #50
0
 def _post(self, what, transaction, changed_by):
     where = {f: what.get(f) for f in self.decisionFields}
     if self.table.select(where=where, transaction=transaction):
         raise SignoffRequiredError("Required Signoffs cannot be directly modified")
     else:
         try:
             self.table.insert(changed_by=changed_by, transaction=transaction, **what)
             return Response(status=201, response=json.dumps({"new_data_version": 1}))
         except ValueError as e:
             self.log.warning("Bad input: %s", e.args)
             return problem(400, "Bad Request", str(e.args))
Пример #51
0
 def get_all(self):
     try:
         return self.get_revisions(
             get_object_callback=lambda: ScheduledChangesView.get,
             history_filters_callback=self._get_filters_all,
             process_revisions_callback=self._process_revisions,
             revisions_order_by=[self.history_table.timestamp.desc()],
             obj_not_found_msg='Scheduled change does not exist')
     except (ValueError, AssertionError) as msg:
         self.log.warning("Bad input: %s", msg)
         return problem(400, "Bad Request", "Error in fetching revisions", ext={"exception": msg})
Пример #52
0
 def get(self, release):
     try:
         return self.get_revisions(
             get_object_callback=lambda: self._get_release(release),
             history_filters_callback=self._get_filters,
             process_revisions_callback=self._process_revisions,
             revisions_order_by=[self.history_table.timestamp.desc()],
             obj_not_found_msg='Requested release does not exist')
     except (ValueError, AssertionError) as e:
         self.log.warning("Bad input: %s", json.dumps(e.args))
         return problem(400, "Bad Request", "Invalid input", ext={"data": e.args})
Пример #53
0
 def _delete(self, username, role, changed_by, transaction):
     roles = [r['role'] for r in dbo.permissions.getUserRoles(username)]
     if role not in roles:
         return problem(404, "Not Found", "Role not found", ext={"exception": "No role '%s' found for "
                                                                              "username '%s'" % (role, username)})
     # query argument i.e. data_version  is also required.
     # All input value validations already defined in swagger specification and carried out by connexion.
     old_data_version = int(connexion.request.args.get("data_version"))
     dbo.permissions.revokeRole(username, role, changed_by=changed_by,
                                old_data_version=old_data_version, transaction=transaction)
     return Response(status=200)
Пример #54
0
 def get(self, release):
     release = dbo.releases.getReleases(name=release, limit=1)
     if not release:
         return problem(404, "Not Found", "Release name: %s not found" % release)
     headers = {'X-Data-Version': release[0]['data_version']}
     headers.update(get_csrf_headers())
     if connexion.request.args.get("pretty"):
         indent = 4
     else:
         indent = None
     return Response(response=json.dumps(release[0]['data'], indent=indent, sort_keys=True),
                     mimetype='application/json', headers=headers)