示例#1
0
def create():
    entity_id, entity_type = None, None
    for entity_type in ENTITY_TYPES:
        entity_id = request.args.get(entity_type)
        if entity_id:
            entity_type = entity_type
            break

    if not (entity_id or entity_type):
        logging.warning("Unsupported entity type")
        raise BadRequest("Unsupported entity type")

    if not entity_id:
        flash.info(gettext("Please choose an entity to review."))
        return redirect(url_for('search.selector', next=url_for('.create')))

    if current_user.is_blocked:
        flash.error(gettext("You are not allowed to write new reviews because your "
                            "account has been blocked by a moderator."))
        return redirect(url_for('user.reviews', user_id=current_user.id))

    # Checking if the user already wrote a review for this entity
    reviews, count = db_review.list_reviews(user_id=current_user.id, entity_id=entity_id)
    review = reviews[0] if count is not 0 else None

    if review:
        flash.error(gettext("You have already published a review for this entity!"))
        return redirect(url_for('review.entity', id=review["id"]))

    form = ReviewCreateForm(default_language=get_locale())

    if form.validate_on_submit():
        if current_user.is_review_limit_exceeded:
            flash.error(gettext("You have exceeded your limit of reviews per day."))
            return redirect(url_for('user.reviews', user_id=current_user.id))

        is_draft = form.state.data == 'draft'
        if form.text.data == '':
            form.text.data = None
        review = db_review.create(user_id=current_user.id, entity_id=entity_id, entity_type=entity_type,
                                  text=form.text.data, rating=form.rating.data, license_id=form.license_choice.data,
                                  language=form.language.data, is_draft=is_draft)
        if is_draft:
            flash.success(gettext("Review has been saved!"))
        else:
            flash.success(gettext("Review has been published!"))
        return redirect(url_for('.entity', id=review['id']))

    entity = get_entity_by_id(entity_id, entity_type)
    if not entity:
        flash.error(gettext("You can only write a review for an entity that exists on MusicBrainz!"))
        return redirect(url_for('search.selector', next=url_for('.create')))

    if entity_type == 'release_group':
        spotify_mappings = mbspotify.mappings(entity_id)
        soundcloud_url = soundcloud.get_url(entity_id)
        if not form.errors:
            flash.info(gettext("Please provide some text or a rating for this review."))
        return render_template('review/modify/write.html', form=form, entity_type=entity_type, entity=entity,
                               spotify_mappings=spotify_mappings, soundcloud_url=soundcloud_url)
    if not form.errors:
        flash.info(gettext("Please provide some text or a rating for this review."))
    return render_template('review/modify/write.html', form=form, entity_type=entity_type, entity=entity)
示例#2
0
 def bad_query_parameter(self, attrname):
     return BadRequest(
         'Unknown or unsupported query parameter {0}'.format(attrname))
def ca_validate_input(input_json: Dict,
                      ecosystem: str) -> Tuple[List[Dict], List[Package]]:
    """Validate CA Input."""
    logger.debug('Validating ca input data.')
    if not input_json:
        error_msg = "Expected JSON request"
        raise BadRequest(error_msg)

    if not isinstance(input_json, dict):
        error_msg = "Expected list of dependencies in JSON request"
        raise BadRequest(error_msg)

    if not check_for_accepted_ecosystem(ecosystem):
        error_msg: str = f"Ecosystem {ecosystem} is not supported for this request"
        raise BadRequest(error_msg)

    if not input_json.get('package_versions'):
        error_msg: str = "package_versions is missing"
        raise BadRequest(error_msg)

    gh = GithubUtils()
    packages_list = []
    normalised_input_pkgs = []
    for pkg in input_json.get('package_versions'):
        pseudo_version = False
        package = given_package = pkg.get("package")
        clean_version = given_version = pkg.get("version")
        if not all([package, given_version]):
            error_msg = "Invalid Input: Package, Version are required."
            raise BadRequest(error_msg)

        if (not isinstance(given_version, str)) or (not isinstance(
                package, str)):
            error_msg = "Package version should be string format only."
            raise BadRequest(error_msg)

        if not validate_version(given_version):
            error_msg = "Package version should not have special characters."
            raise BadRequest(error_msg)

        if ecosystem == 'maven':
            package = MavenCoordinates.normalize_str(package)

        if ecosystem == 'pypi':
            package = package.lower()

        if ecosystem == 'golang':
            _, clean_version = GolangDependencyTreeGenerator.clean_version(
                given_version)
            pseudo_version = gh.is_pseudo_version(clean_version)
            # Strip module appended to the package name
            package = package.split('@')[0]

        packages_list.append({
            "name": package,
            "given_name": given_package,
            "version": clean_version,
            "given_version": given_version,
            "is_pseudo_version": pseudo_version
        })
        normalised_input_pkgs.append(
            normlize_packages(package, given_package, clean_version,
                              given_version, pseudo_version))
    return packages_list, normalised_input_pkgs
def upload_batch(region=None, body=None):
    """Create a new upload batch.  The response object will contain a
    ``put_url`` for each file which needs to be uploaded -- which may not be
    all!  The caller is then responsible for uploading to those URLs.  The
    resulting signed URLs are valid for one hour, so uploads should begin
    within that timeframe.  Consider using Amazon's MD5-verification
    capabilities to ensure that the uploaded files are transferred correctly,
    although the tooltool server will verify the integrity anyway.  The
    upload must have the header ``Content-Type: application/octet-stream```.

    The query argument ``region=us-west-1`` indicates a preference for URLs
    in that region, although if the region is not available then URLs in
    other regions may be returned.

    The returned URLs are only valid for 60 seconds, so all upload requests
    must begin within that timeframe.  Clients should therefore perform all
    uploads in parallel, rather than sequentially.  This limitation is in
    place to prevent malicious modification of files after they have been
    verified."""
    region, bucket = get_region_and_bucket(region)

    if not body.message:
        raise BadRequest("message must be non-empty")

    if not body.files:
        raise BadRequest("a batch must include at least one file")

    if body.author:
        raise BadRequest("Author must not be specified for upload")
    try:
        body.author = current_user.authenticated_email
    except AttributeError:
        # no authenticated_email -> use the stringified user (probably a token
        # ID)
        body.author = str(current_user)

    # verify permissions based on visibilities
    visibilities = set(f.visibility for f in body.files.itervalues())
    for v in visibilities:
        prm = p.get('tooltool.upload.{}'.format(v))
        if not prm or not prm.can():
            raise Forbidden("no permission to upload {} files".format(v))

    session = g.db.session(tables.DB_DECLARATIVE_BASE)
    batch = tables.Batch(uploaded=time.now(),
                         author=body.author,
                         message=body.message)

    s3 = current_app.aws.connect_to('s3', region)
    for filename, info in body.files.iteritems():
        log = logger.bind(tooltool_sha512=info.digest,
                          tooltool_operation='upload',
                          tooltool_batch_id=batch.id,
                          mozdef=True)
        if info.algorithm != 'sha512':
            raise BadRequest("'sha512' is the only allowed digest algorithm")
        if not is_valid_sha512(info.digest):
            raise BadRequest("Invalid sha512 digest")
        digest = info.digest
        file = tables.File.query.filter(tables.File.sha512 == digest).first()
        if file and file.visibility != info.visibility:
            raise BadRequest("Cannot change a file's visibility level")
        if file and file.instances != []:
            if file.size != info.size:
                raise BadRequest("Size mismatch for {}".format(filename))
        else:
            if not file:
                file = tables.File(sha512=digest,
                                   visibility=info.visibility,
                                   size=info.size)
                session.add(file)
            log.info(
                "generating signed S3 PUT URL to {} for {}; expiring in {}s".
                format(info.digest[:10], current_user, UPLOAD_EXPIRES_IN))
            info.put_url = s3.generate_url(
                method='PUT',
                expires_in=UPLOAD_EXPIRES_IN,
                bucket=bucket,
                key=util.keyname(info.digest),
                headers={'Content-Type': 'application/octet-stream'})
            # The PendingUpload row needs to reflect the updated expiration
            # time, even if there's an existing pending upload that expires
            # earlier.  The `merge` method does a SELECT and then either UPDATEs
            # or INSERTs the row.  However, merge needs the file_id, rather than
            # just a reference to the file object; and for that, we need to flush
            # the inserted file.
            session.flush()
            pu = tables.PendingUpload(
                file_id=file.id,
                region=region,
                expires=time.now() +
                datetime.timedelta(seconds=UPLOAD_EXPIRES_IN))
            session.merge(pu)
        session.add(tables.BatchFile(filename=filename, file=file,
                                     batch=batch))
    session.add(batch)
    session.commit()

    body.id = batch.id
    return body
示例#5
0
def wireviz_render(input_yaml: str, output_mimetype: str,
                   output_filename: str) -> Response:
    """
    Render WireViz output and create a Flask Response.

    :param input_yaml:      Input data in WireViz YAML format.
    :param output_mimetype: The designated output format mimetype. Currently available:
                            - image/svg+xml
                            - image/png
                            - text/html
                            - text/plain
                            - application/json
    :param output_filename: The designated output filename.
    :return:                A Flask Response object.
    """

    # Sanity checks.
    if not input_yaml.strip():
        raise BadRequest(description="No input data")

    # Compute output type from MIME type.
    return_type = mimetype_to_type(output_mimetype)

    # Parse WireViz YAML.
    try:
        harness: Harness = wireviz.parse(yaml_input=input_yaml,
                                         return_types="harness")
    except Exception as ex:
        message = f"Unable to parse WireViz YAML format: {ex}"
        logger.exception(message)
        raise BadRequest(description=message)

    # Dispatch rendering by designated output type.
    if return_type == "png":
        payload = harness.png

    elif return_type == "svg":
        payload = harness.svg

    elif return_type == "html":
        try:
            tmpfile = NamedTemporaryFile(delete=False)

            # Build list of implicitly created temporary files.
            tempfiles = []
            for suffix in [".gv", ".png", ".svg", ".bom.tsv", ".html"]:
                tempfiles.append(f"{tmpfile.name}{suffix}")

            # Render HTML output.
            harness.output(filename=tmpfile.name, fmt=("png", "svg"))
            with open(f"{tmpfile.name}.html", "rb") as f:
                payload = f.read()

        except Exception as ex:  # pragma: no cover
            message = f"Unable to produce WireViz output: {ex}"
            logger.exception(message)
            raise BadRequest(description=message)

        finally:
            # Clean up temporary files.
            for tempfile in tempfiles:
                try:
                    Path(tempfile).unlink(missing_ok=True)
                except (FileNotFoundError, TypeError):  # pragma: no cover
                    pass

    elif return_type == "bom.txt":
        harness.create_graph()
        bom_list = harness.bom_list()
        payload = tuplelist2tsv(bom_list).encode("utf-8")

    elif return_type == "bom.json":
        harness.create_graph()
        bom_list = harness.bom_list()
        payload = json.dumps(bom_list, indent=2).encode("utf-8")

    # Respond with rendered image.
    return send_file(
        io.BytesIO(payload),
        mimetype=output_mimetype,
        as_attachment=True,
        attachment_filename=output_filename,
    )
示例#6
0
    def get(self):
        """
        ---
        summary: Search or list objects
        description: |
            Returns list of objects matching provided query,
            ordered from the latest one.

            Limited to 10 objects, use `older_than` parameter to fetch more.

            Don't rely on maximum count of returned objects
            because it can be changed/parametrized in future.
        security:
            - bearerAuth: []
        tags:
            - object
        parameters:
            - in: query
              name: older_than
              schema:
                type: string
              description: |
                Fetch objects which are older than the object
                specified by identifier.

                Used for pagination
              required: false
            - in: query
              name: query
              schema:
                type: string
              description: Filter results using Lucene query
              required: false
        responses:
            200:
                description: List of objects
                content:
                  application/json:
                    schema: ObjectListResponseSchema
            400:
                description: |
                    When wrong parameters were provided
                    or syntax error occurred in Lucene query
            404:
                description: When user doesn't have access to the `older_than` object
        """
        if "page" in request.args:
            logger.warning("'%s' used legacy 'page' parameter", g.auth_user.login)

        obj = load_schema(request.args, ObjectListRequestSchema())

        pivot_obj = None
        if obj["older_than"]:
            pivot_obj = Object.access(obj["older_than"])
            if pivot_obj is None:
                raise NotFound("Object specified in 'older_than' parameter not found")

        query = obj["query"]
        if query:
            try:
                db_query = SQLQueryBuilder().build_query(
                    query, queried_type=self.ObjectType
                )
            except SQLQueryBuilderBaseException as e:
                raise BadRequest(str(e))
            except ParseError as e:
                raise BadRequest(str(e))
        else:
            db_query = db.session.query(self.ObjectType)

        db_query = db_query.filter(
            g.auth_user.has_access_to_object(Object.id)
        ).order_by(Object.id.desc())
        if pivot_obj:
            db_query = db_query.filter(Object.id < pivot_obj.id)
        # Legacy parameter - to be removed in the future
        elif obj["page"] is not None and obj["page"] > 1:
            db_query = db_query.offset((obj["page"] - 1) * 10)

        objects = db_query.limit(10).all()

        schema = self.ListResponseSchema()
        return schema.dump(objects, many=True)
示例#7
0
文件: user.py 项目: zanachka/quickpin
    def post(self):
        '''
        Create a new application user.

        **Example Request**

        .. sourcecode:: json

            {
                "email": "*****@*****.**",
                "password": "******"
            }

        **Example Response**

        .. sourcecode:: json

            {
                "agency": null,
                "created": "2015-05-05T14:30:09.676268",
                "email": "*****@*****.**",
                "id": 2029,
                "is_admin": false,
                "location": null,
                "modified": "2015-05-05T14:30:09.676294",
                "name": null,
                "phone": null,
                "thumb": null,
                "url": "https://quickpin/api/user/2029"
            }

        :<header Content-Type: application/json
        :<header X-Auth: the client's auth token
        :>json str email: e-mail address
        :>json str password: new password, must be >=8 characters, mixed case,

        :>header Content-Type: application/json
        :>json str agency: the name of the organization/agency that this person
            is affiliated with (default: null)
        :>json str created: record creation timestamp in ISO-8601 format
        :>json str email: e-mail address
        :>json bool is_admin: true if this user has admin privileges, false
            otherwise
        :>json str location: location name, e.g. city or state (default: null)
        :>json str modified: record modification timestamp in ISO-8601 format
        :>json str name: user's full name, optionally including title or other
            salutation information (default: null)
        :>json str phone: phone number
        :>json str phone_e164: phone number in E.164 format
        :>json str thumb: PNG thumbnail for this user, base64 encoded
        :>json str url: url to view data about this user

        :status 200: ok
        :status 400: invalid request body
        :status 401: authentication required
        :status 403: not authorized to create accounts
        :status 409: e-mail address already in use
        '''

        request_json = request.get_json()

        if 'email' not in request_json or '@' not in request_json['email']:
            raise BadRequest('Invalid or missing email.')

        user = User(request_json['email'].strip())

        if 'password' not in request_json:
            raise BadRequest('Password is required')

        password = request_json['password'].strip()

        if not valid_password(password):
            raise BadRequest('Password does not meet complexity requirements.')

        user.password_hash = hash_password(
            password, g.config.get('password_hash', 'algorithm'),
            int(g.config.get('password_hash', 'rounds')))

        try:
            g.db.add(user)
            g.db.commit()
        except IntegrityError:
            raise Conflict('This e-mail address is already in use.')

        g.db.expire(user)

        return jsonify(**self._user_dict(user))
示例#8
0
def admin_delete_user(userid):
    """
    Delete a user. The current user cannot be deleted.

    This action return a confirmation page. It is necessary to submit the confirmation form to complete the action.

    POST command parameters:

    * `confirm_token`: Confirmation token. Only applies to superuser/delete requests. Optional (returns the confirmation
        page if not present).
    * `next`: URL, the URL to return to after the update.

    :param userid: The user ID to modify.
    :return:
    """

    from quasselflask import forms

    # validate
    if userid == current_user.qfuserid:
        flash("Oops! You can't delete yourself.", "error")
        return safe_redirect(get_next_url('POST'))

    # Valid - let's process it
    user = query_qfuser(db.session, userid)

    # check if confirmed
    is_confirmed = False
    data = {}
    if 'confirm_token' in request.form:
        try:
            data = forms.check_confirm_key(request.form.get('confirm_token'),
                                           'admin_user_delete')
            is_confirmed = True
        except BadSignature:
            logger.error(
                log_action_error('update user', 'invalid confirmation key',
                                 ('user', _get_qfuser_log(user))))
            logger.debug('request.form=' + repr_user_input(request.form))
            is_confirmed = False

    if not is_confirmed:
        confirm_key = forms.generate_confirm_key(
            {
                'current_user': current_user.qfuserid,
                'target_user': user.qfuserid
            }, 'admin_user_delete')
        return render_template('admin/update_user_confirm.html',
                               target=url_for('admin_delete_user',
                                              userid=userid),
                               user=user,
                               action='delete',
                               next_url=get_next_url('POST'),
                               confirm_key=confirm_key)
    else:
        valid_request = data.get('target_user', -1) == user.qfuserid and \
                        data.get('current_user', -1) == current_user.qfuserid
        if not valid_request:
            logger.error(
                log_action_error('delete user', 'invalid confirmation key',
                                 ('user', _get_qfuser_log(user))))
            logger.debug('confirmation data=' + repr_user_input(data))
            logger.debug('request.form=' + repr_user_input(request.form))
            raise BadRequest('Invalid confirmation key.')

        db.session.delete(user)
        flash('Deleted user {}'.format(user.username))
        db.session.commit()
        logger.info(log_action('delete user', ('user', _get_qfuser_log(user))))
        return redirect(url_for('admin_users'))
示例#9
0
def admin_user_reset_password(userid):
    """
    Reset the user's password and send them an email inviting them to change their password.

    This action return a confirmation page. It is necessary to submit the confirmation form to complete the action.

    POST command parameters:

    * `confirm_token`: Confirmation token. Optional (returns the confirmation page if not present).
    * `next`: URL, the URL to return to after the update.

    :param userid: The user ID to modify.
    :return:
    """

    from quasselflask import forms

    if userid == current_user.get_id():
        flash(
            "Can't force reset your own password: go change it from the user profile like a normal person, "
            "you dummy!", 'error')
        return safe_redirect(get_next_url('POST'))

    user = query_qfuser(db.session, userid)

    # check if confirmed
    is_confirmed = False
    data = {}
    if 'confirm_token' in request.form:
        try:
            data = forms.check_confirm_key(request.form.get('confirm_token'),
                                           'admin_user_reset_password')
            is_confirmed = True
        except BadSignature:
            logger.error(
                log_action_error('update user', 'invalid confirmation key',
                                 ('user', _get_qfuser_log(user))))
            logger.debug('request.form=' + repr_user_input(request.form))
            is_confirmed = False

    if not is_confirmed:
        confirm_key = forms.generate_confirm_key(
            {
                'current_user': current_user.qfuserid,
                'target_user': user.qfuserid
            }, 'admin_user_reset_password')
        return render_template('admin/update_user_confirm.html',
                               target=url_for('admin_user_reset_password',
                                              userid=userid),
                               user=user,
                               action='reset password',
                               next_url=get_next_url('POST'),
                               confirm_key=confirm_key)
    else:
        valid_request = data.get('target_user', -1) == user.qfuserid and \
                        data.get('current_user', -1) == current_user.qfuserid
        if not valid_request:
            logger.error(
                log_action_error('reset user password',
                                 'invalid confirmation key',
                                 ('user', _get_qfuser_log(user))))
            logger.debug('confirmation data=' + repr_user_input(data))
            logger.debug('request.form=' + repr_user_input(request.form))
            raise BadRequest('Invalid confirmation key.')

        try:
            user.password = userman.hash_password(make_reset_password())
            send_new_user_set_password_email(user)
            db.session.commit()
            flash(
                'User {user} login disabled; this user cannot login until they reset their password. A password '
                'reset email has been sent to {email}.'.format(
                    user=user.username, email=user.email), 'success')
        except Exception as e:
            logger.error(log_action_error('error while creating user',
                                          repr(e.args), ('id', user.qfuserid),
                                          ('name', user.username),
                                          ('email', user.email),
                                          ('superuser', repr(user.superuser))),
                         exc_info=True)
            db.session.rollback()
            if app.debug:
                raise  # let us debug this
            else:
                flash(
                    'Error occurred resetting password. Please try again later, or contact the server administrator '
                    'with the date/time of this error and your IP address in order to trace error information in the'
                    'logs.', 'error')
                return redirect(
                    url_for('admin_manage_user', userid=user.qfuserid))

        logger.info(
            log_action('reset user password', ('user', _get_qfuser_log(user))))
        return safe_redirect(get_next_url('POST'))
示例#10
0
def admin_update_user(userid):
    """
    Update a user's information. Any fields not included will remain unchanged.

    Some actions return a confirmation page. It is necessary to submit the confirmation form to complete the action.

    If acting on the current user, only the email address may be updated.  This prevents the current user from locking
    themselves out of the application or removing a sole superuser.

    POST command parameter (exactly one required, except email and confirm_email may appear together):

    * `status`: 1 (enabled) or 0 (disabled). Other values treated as 0.
    * `superuser`: 1 (superuser) or 0 (normal user). Other values treated as 0. Returns a confirmation page.
    * `confirm_token`: Confirmation token. Only applies to superuser/delete requests.
    * `email`: new email address. Can only be combined with the `confirm_email` parameter.
    * `confirm_email`: 1 (confirm) or 0 (unconfirm). Force the email confirmation status. Other values are treated as 0.
            Can only be combined with the `email` parameter.

    Other POST parameters, common to all requests:

    * `next`: URL, the URL to return to after the update.

    :param userid: The user ID to modify.
    :return:
    """

    commands = frozenset(
        ('status', 'superuser', 'email', 'confirm_email', 'confirm_token'))
    request_commands = set(request.form.keys()) & commands

    # validate
    request_valid = len(request_commands) == 1 or \
        (len(request_commands) == 2 and 'email' in request_commands and 'confirm_email' in request_commands)
    user_valid = (userid != current_user.qfuserid
                  or ('email' in request_commands
                      and request.form.get('confirm_email', '0') == '1'))

    if not request_valid:
        raise BadRequest(
            "No command or multiple commands to user update API endpoint")

    if not user_valid:
        flash("Oops! You can't make changes that would disable yourself.",
              "error")
        return safe_redirect(get_next_url('POST'))

    # Valid - let's process it
    user = query_qfuser(db.session, userid)

    if 'status' in request.form:
        return admin_update_user_status(user)
    elif 'superuser' in request.form:  # non-confirmed
        return admin_update_user_superuser_confirm(user)
    elif 'confirm_token' in request.form:  # confirm superuser/delete
        return admin_update_user_superuser(user)
    elif 'email' in request.form or 'confirm_email' in request.form:
        return admin_update_user_email(user)

    flash("Something happened. You shouldn't have gotten this far.", "error")
    logger.error(
        log_action_error('update user',
                         'should have returned earlier in code - bug?',
                         ('user', _get_qfuser_log(user))))
    logger.debug('request.form=' + repr_user_input(request.form))
    return safe_redirect(get_next_url('POST'))
示例#11
0
def admin_permissions(userid):
    """
    On POST request, update a user's permissions. Redirects to the user management page.

    POST parameters:

    * `permissions`: new permissions to set. Data in the following structure:

        .. code-block:: json
            {
                "permissions": [
                    {
                        "access": "allow|deny",
                        "type": "user|network|buffer",
                        "id": 0
                    },
                    { ... }
                ],
                "default": "allow|deny"
            }

        ``id`` represents the quasseluser, network or buffer ID, depending on the set `type` value.


    :param userid: The user ID to modify.
    :return:
    """
    user = query_qfuser(db.session, userid)
    try:
        perm_data = json.loads(request.form.get('permissions'))
    except json.JSONDecodeError:
        logger.error(
            log_action_error('update permissions', 'invalid JSON',
                             ('user', _get_qfuser_log(user))))
        logger.debug('request.form=' + repr_user_input(request.form))
        raise BadRequest('Invalid permissions data.')

    try:
        logger.debug(
            log_action('old permissions', ('user', _get_qfuser_log(user)),
                       ('default', user.access),
                       ('permissions', user.permissions)))
        user.access = PermissionAccess.from_name(perm_data['default'])

        # If any permissions for this user, delete them first
        if user.permissions:
            del user.permissions[:]

        # Add the new permissions. Validations:
        # access, type: from_name validates against enum python-side; db columns are enums
        # ID: foreign key constraint, database will complain if non-existent for the given type
        # All: If not present in in_perm (malformed permission object), KeyError raised
        # TODO: figure out what kind of SQLAlchemy exceptions can be thrown here to throw in with Invalid permissions
        for in_perm in perm_data['permissions']:
            user.permissions.append(
                QfPermission(PermissionAccess.from_name(in_perm['access']),
                             PermissionType.from_name(in_perm['type']),
                             in_perm['id']))

        db.session.commit()

        logger.info(
            log_action('update permissions', ('user', _get_qfuser_log(user)),
                       ('default', user.access),
                       ('permissions', user.permissions)))

    except (KeyError, TypeError, ValueError,
            sqlalchemy.exc.NoReferencedColumnError):
        logger.error(
            log_action_error('update permissions', 'invalid perm data',
                             ('user', _get_qfuser_log(user))))
        logger.debug('perm_data=' + repr_user_input(perm_data),
                     exc_info=True,
                     stack_info=True)
        db.session.rollback()
        raise BadRequest('Invalid permissions data.')
    # other SQLAlchemy errors are not caught: legitimate server error (HTTP 500), let Flask handle it

    return safe_redirect(get_next_url('POST'))
示例#12
0
    def save_session(self, app, session, response):
        """Save current session."""
        domain = self.get_cookie_domain(app)
        if not session:
            current_app.logger.debug("Empty session: " + str(request.url))
            return
        #    response.delete_cookie(app.session_cookie_name,
        #                            domain=domain)
        #    response.delete_cookie(app.session_cookie_name + 'stub',
        #                            domain=domain)
        #    return
        timeout = self.get_session_expiration_time(app, session)
        session_expiry = datetime.utcnow() + timeout
        max_age = cookie_expiry = None
        uid = session.uid
        if uid > -1 and session.permanent:
            max_age = app.permanent_session_lifetime
            cookie_expiry = session_expiry
        sid = session.sid
        if session.logging_in:
            # # FIXME Do we really need to delete the session after login?
            # # The user just logged in, better change the session ID
            # sid = self.generate_sid()
            # flashes = get_flashed_messages(with_categories=True)
            # # And remove the cookie that has been set
            # self.backend.delete(session.sid)
            # session.clear()
            # response.delete_cookie(app.session_cookie_name, domain=domain)
            # response.delete_cookie(app.session_cookie_name + 'stub',
            #                        domain=domain)
            # session.sid = sid
            # session.uid = uid
            # # Fixes problem with lost flashes after login.
            # map(lambda (cat, msg): flash(msg, cat), flashes)
            pass
        # Set all user id keys for compatibility.

        if len(session.keys()) == 1 and '_id' in session:
            session.delete()
            return
        elif not session.modified:
            return

        session.uid = uid
        session.save_ip(request)
        self.backend.set(sid,
                         self.serializer.dumps(dict(session)),
                         timeout=timeout)

        if not self.has_secure_url:
            response.set_cookie(app.session_cookie_name,
                                sid,
                                expires=cookie_expiry,
                                httponly=True,
                                domain=domain,
                                max_age=max_age)
        elif session.uid > 0:
            # User is authenticated, we shall use HTTPS then
            if request.scheme == 'https':
                response.set_cookie(app.session_cookie_name,
                                    sid,
                                    expires=cookie_expiry,
                                    httponly=True,
                                    domain=domain,
                                    secure=True,
                                    max_age=max_age)
                response.set_cookie(app.session_cookie_name + 'stub',
                                    'HTTPS',
                                    expires=cookie_expiry,
                                    httponly=True,
                                    domain=domain,
                                    max_age=max_age)
            else:
                raise BadRequest("The user is being authenticated over HTTP "
                                 "rather than HTTPS?")
        else:
            response.set_cookie(app.session_cookie_name,
                                sid,
                                httponly=True,
                                domain=domain)
            response.set_cookie(app.session_cookie_name + 'stub',
                                'NO',
                                httponly=True,
                                domain=domain)
示例#13
0
 def put(self, obj_id=None):
     """update an object, new attrs should be passed in the payload"""
     args = self.reqparse_args(right="write", default=False)
     if not args:
         raise BadRequest()
     return self.controller.update({"id": obj_id}, args), 200
示例#14
0
文件: user.py 项目: zanachka/quickpin
    def put(self, id_):
        '''
        Update data about the application identified by `id`. Omitted fields
        are not changed.

        **Example Request**

        .. sourcecode:: json

            {
                "agency": "Department Of Justice",
                "email": "*****@*****.**",
                "is_admin": true,
                "location": "Washington, DC",
                "name": "Lt. John Doe",
                "password": "******",
                "phone": "+12025551234",
                "thumb": "iVBORw0KGgoAAAANS..."
            }

        **Example Response**

        .. sourcecode:: json

            {
                "agency": "Department Of Justice",
                "created": "2015-05-05T14:30:09.676268",
                "email": "*****@*****.**",
                "id": 2029,
                "is_admin": true,
                "location": "Washington, DC",
                "modified": "2015-05-05T14:30:09.676294",
                "name": "Lt. John Doe",
                "phone": "202-555-1234",
                "thumb": "iVBORw0KGgoAAAANS...",
                "url": "https://quickpin/api/user/2029"
            }

        :<header Content-Type: application/json
        :<header X-Auth: the client's auth token
        :>json str agency: the name of the organization/agency that this person
            is affiliated with
        :>json str email: e-mail address
        :>json bool is_admin: true if this user should have admin privileges,
            false otherwise (this field can only be modified by an admin user)
        :>json str location: location name, e.g. city or state (default: null)
        :>json str name: user's full name, optionally including title or other
            salutation information (default: null)
        :>json str password: new password, must be >=8 characters, mixed case,
            and contain numbers
        :>json str phone: phone number (any reasonable format is okay)
        :>json str thumb: PNG thumbnail for this user, base64 encoded

        :>header Content-Type: application/json
        :>json str agency: the name of the organization/agency that this person
            is affiliated with (default: null)
        :>json str created: record creation timestamp in ISO-8601 format
        :>json str email: e-mail address
        :>json bool is_admin: true if this user has admin privileges, false
            otherwise
        :>json str location: location name, e.g. city or state (default: null)
        :>json str modified: record modification timestamp in ISO-8601 format
        :>json str name: user's full name, optionally including title or other
            salutation information (default: null)
        :>json str phone: phone number
        :>json str phone_e164: phone number in E.164 format
        :>json str thumb: PNG thumbnail for this user, base64 encoded
        :>json str url: url to view data about this user

        :status 200: ok
        :status 400: invalid request body
        :status 401: authentication required
        :status 403: not authorized to make the requested changes
        '''

        request_json = request.get_json()
        user = g.db.query(User).filter(User.id == id_).first()

        if not g.user.is_admin and g.user.id != user.id:
            raise Forbidden('You may only modify your own profile.')

        if 'is_admin' in request_json:
            if not g.user.is_admin:
                raise Forbidden('Only admins can change user roles.')

            if g.user.id == int(id_):
                raise BadRequest('You may not modify your own role.')

            user.is_admin = request_json['is_admin']

        self._update_string_field(request_json, 'agency', user, 'agency')
        self._update_string_field(request_json, 'location', user, 'location')
        self._update_string_field(request_json, 'name', user, 'name')

        if 'email' in request_json:
            email = request_json['email'].strip()

            if email == '':
                raise BadRequest('E-mail may not be blank.')

            if '@' not in email and email != 'admin':
                raise BadRequest('Invalid e-mail address.')

            user.email = email

        if 'phone' in request_json:
            if request_json['phone'].strip() == '':
                user.phone = None
            else:
                try:
                    phone = phonenumbers.parse(request_json['phone'], 'US')

                    if not phonenumbers.is_valid_number(phone):
                        raise ValueError()
                except:
                    raise BadRequest('Invalid phone number.')

                user.phone = phonenumbers.format_number(
                    phone, phonenumbers.PhoneNumberFormat.E164)

        if 'thumb' in request_json:
            try:
                img_data = base64.b64decode(request_json['thumb'])
                img = Image.open(BytesIO(img_data))

                if img.format != 'PNG':  # or img.size != (32,32):
                    raise ValueError()
            except:
                raise BadRequest('Thumbnail image must be 32x32 px,' \
                                 ' PNG format, base64 encoded.')

            user.thumb = img_data

        if 'password' in request_json:
            password = request_json['password'].strip()

            if not valid_password(password):
                raise BadRequest(
                    'Password does not meet complexity requirements.')

            user.password_hash = hash_password(
                password, g.config.get('password_hash', 'algorithm'),
                int(g.config.get('password_hash', 'rounds')))

        g.db.commit()
        g.db.expire(user)

        return jsonify(**self._user_dict(user))
示例#15
0
    def wsgi_app(self, environ, start_response):
        """Execute this instance as a WSGI application.

        See the PEP for the meaning of parameters. The separation of
        __call__ and wsgi_app eases the insertion of middlewares.

        """
        urls = self._url_map.bind_to_environ(environ)
        try:
            endpoint, args = urls.match()
        except HTTPException as exc:
            return exc

        assert endpoint == "rpc"

        response = Response()

        if self._auth is not None and not self._auth(environ):
            response.status_code = 403
            response.mimetype = "plain/text"
            response.data = "Request not allowed."
            return response

        request = Request(environ)
        request.encoding_errors = "strict"

        remote_service = ServiceCoord(args['service'], args['shard'])

        if remote_service not in self._service.remote_services:
            return NotFound()

        # TODO Check content_encoding and content_md5.

        if request.mimetype != "application/json":
            return UnsupportedMediaType()

        if request.accept_mimetypes.quality("application/json") <= 0:
            return NotAcceptable()

        try:
            data = json.load(request.stream, encoding='utf-8')
        except ValueError:
            return BadRequest()

        if not self._service.remote_services[remote_service].connected:
            return ServiceUnavailable()

        result = self._service.remote_services[remote_service].execute_rpc(
            args['method'], data)

        # XXX We could set a timeout on the .wait().
        result.wait()

        response.status_code = 200
        response.mimetype = "application/json"
        response.data = json.dumps({
            "data":
            result.value,
            "error":
            None if result.successful() else "%s" % result.exception
        })

        return response
示例#16
0
 def on_exhausted(self):
     raise BadRequest('input stream exhausted')
示例#17
0
def run(format, species, genomic_datasets, genomic_samples, rep_datasets,
        rep_samples, params):
    if len(rep_samples) == 0:
        raise BadRequest('No repertoire-derived genotypes were selected.')

    if format != 'html':
        raise BadRequest('Invalid format requested')

    kdiff = float(params['f_kdiff']
                  ) if 'f_kdiff' in params and params['f_kdiff'] != '' else 0
    chain, samples_by_dataset = collate_samples(rep_samples)

    # Format we need to produce is [(gene_name, hetero count, h**o count),...]

    gene_hetrozygous_dis = {}

    for dataset in samples_by_dataset.keys():
        session = vdjbase_dbs[species][dataset].session
        allele_sample_recs = []

        for sample_chunk in chunk_list(samples_by_dataset[dataset],
                                       SAMPLE_CHUNKS):
            sample_list = session.query(
                Sample.sample_name, Sample.genotype, Sample.patient_id).filter(
                    Sample.sample_name.in_(sample_chunk)).all()
            sample_list, wanted_genes = apply_rep_filter_params(
                params, sample_list, session)
            sample_list = [s[0] for s in sample_list]

            query = session.query(Gene.name, Patient.id, Allele.id, Sample.sample_name, Gene.locus_order, AllelesSample.kdiff, Allele.name) \
                .join(Allele, Gene.id == Allele.gene_id) \
                .join(AllelesSample, Allele.id == AllelesSample.allele_id) \
                .join(Sample, Sample.id == AllelesSample.sample_id) \
                .join(Patient, Patient.id == Sample.patient_id) \
                .filter(Gene.name.in_(wanted_genes)) \
                .filter(Allele.name.notlike('%Del%')) \
                .filter(Allele.name.notlike('%OR%')) \
                .filter(Sample.sample_name.in_(sample_list)) \
                .filter(AllelesSample.kdiff >= kdiff)

            if 'sort_order' in params and params['sort_order'] == 'Locus':
                query = query.order_by(Gene.locus_order, Patient.id, Allele.id)
            else:
                query = query.order_by(Gene.alpha_order, Patient.id, Allele.id)

            if params['ambiguous_alleles'] == 'Exclude':
                query = query.filter(Allele.is_single_allele == True)

            allele_sample_recs.extend(query.all())

        # As the result is indexed, run over each gene in turn, count the number of alleles found in each patient, update h_counts accordingly

        i = 0
        target_gene = ''

        while i < len(allele_sample_recs):
            target_gene = allele_sample_recs[i][0]
            h_counts = [0, 0]

            while i < len(allele_sample_recs):
                if allele_sample_recs[i][0] != target_gene:
                    break

                target_patient = allele_sample_recs[i][1]
                patient_allele_ids = []

                while i < len(allele_sample_recs):
                    if allele_sample_recs[i][
                            0] != target_gene or allele_sample_recs[i][
                                1] != target_patient:
                        break

                    patient_allele_ids.append(allele_sample_recs[i][2])
                    i += 1

                patient_allele_ids = set(patient_allele_ids)

                # If we have both an unambiguous allele and an ambiguous allele containing that unambiguous one,
                # drop the unambiguous one because it is already counted

                if params['ambiguous_alleles'] != 'Exclude':
                    patterns = session.query(AllelesPattern.pattern_id)\
                        .filter(AllelesPattern.allele_in_p_id.in_(patient_allele_ids))\
                        .filter(AllelesPattern.pattern_id.in_(patient_allele_ids))\
                        .all()

                    if patterns is not None and len(patterns) > 0:
                        patterns = set([pattern[0] for pattern in patterns])
                        patient_allele_ids = patient_allele_ids - patterns

                if len(patient_allele_ids) > 1:
                    h_counts[1] += 1
                elif len(patient_allele_ids) > 0:
                    h_counts[0] += 1

            if target_gene not in gene_hetrozygous_dis:
                gene_hetrozygous_dis[target_gene] = (target_gene, h_counts[0],
                                                     h_counts[1])
            else:
                gene_hetrozygous_dis[target_gene] = (
                    target_gene,
                    gene_hetrozygous_dis[target_gene][1] + h_counts[0],
                    gene_hetrozygous_dis[target_gene][2] + h_counts[1])

    haplo_path = make_output_file('tab')
    labels = ['GENE', 'HM', 'HT']
    df = pd.DataFrame(gene_hetrozygous_dis.values(), columns=labels)
    df.to_csv(haplo_path, sep='\t', index=False)
    output_path = make_output_file('html')

    cmd_line = [
        "-i",
        haplo_path,
        "-o",
        output_path,
        "-c",
        chain,
    ]

    if run_rscript(HETEROZYGOSITY_SCRIPT, cmd_line) and os.path.isfile(
            output_path) and os.path.getsize(output_path) != 0:
        return send_report(output_path, format)
    else:
        raise BadRequest('No output from report')
示例#18
0
def reject_nuls():
    for key, values in request.values.iterlists():
        if '\0' in key or any('\0' in x for x in values):
            raise BadRequest('NUL byte found in request data')
示例#19
0
 def _forbidden_attributes(self, data):
     for key in data.keys():
         if key in self.reserved_keys:
             msg = "Reserved keys in payload: %s" % key
             raise BadRequest(msg)
示例#20
0
 def validation_fails_handler():
     raise BadRequest()