Example #1
0
def get():
    """
    This function handles the retrieval of report data to generate the chart on the frontend.
    Takes in agency_ein or user_guid from the frontend and filters for the number of requests closed and requests
    opened.

    :return: json object({"labels": ["Opened", "Closed"],
                          "values": [150, 135],
                          "active_users": [('', ''), ('o8pj0k', 'John Doe')]}), 200
    """
    agency_ein = request.args.get('agency_ein')
    user_guid = request.args.get('user_guid', '')
    requests_opened = 0
    requests_closed = 0
    active_users = []
    is_visible = False
    results = False
    if agency_ein and user_guid == '':
        if agency_ein == 'all':
            active_requests = Requests.query.with_entities(Requests.status).join(
                Agencies, Requests.agency_ein == Agencies.ein).filter(
                Agencies.is_active).all()
            requests_closed = len([r for r in active_requests if r[0] == request_status.CLOSED])
            requests_opened = len(active_requests) - requests_closed
        else:
            active_requests = Requests.query.with_entities(Requests.status).join(
                Agencies, Requests.agency_ein == Agencies.ein).filter(
                Agencies.ein == agency_ein, Agencies.is_active).all()
            requests_closed = len([r for r in active_requests if r[0] == request_status.CLOSED])
            requests_opened = len(active_requests) - requests_closed
            if not (current_user.is_anonymous or current_user.is_public):
                if (current_user.is_agency and current_user.is_agency_admin(agency_ein)) or current_user.is_super:
                    is_visible = True
                    if current_user.is_agency_admin(agency_ein) or current_user.is_super:
                        active_users = sorted(
                            [(user.guid, user.name)
                             for user in Agencies.query.filter_by(ein=agency_ein).one().active_users],
                            key=lambda x: x[1])
                    elif current_user.is_agency_active(agency_ein):
                        active_users = [(current_user.guid, current_user.name)]
                    if active_users:
                        active_users.insert(0, ('', ''))
                        results = True

    elif user_guid and (current_user.is_agency_active(agency_ein) or
                        current_user.is_agency_admin(agency_ein) or
                        current_user.is_super):
        is_visible = True
        ureqs = UserRequests.query.filter(UserRequests.user_guid == user_guid
                                          ).all()

        requests_closed = len([u for u in ureqs if u.request.status == request_status.CLOSED])
        requests_opened = len([u for u in ureqs if u.request.status != request_status.CLOSED])

    return jsonify({"labels": ["Open", "Closed"],
                    "values": [requests_opened, requests_closed],
                    "active_users": active_users,
                    "is_visible": is_visible,
                    "results": results
                    }), 200
Example #2
0
def get():
    """
    This function handles the retrieval of report data to generate the chart on the frontend.
    Takes in agency_ein or user_guid from the frontend and filters for the number of requests closed and requests
    opened.

    :return: json object({"labels": ["Opened", "Closed"],
                          "values": [150, 135],
                          "active_users": [('', ''), ('o8pj0k', 'John Doe')]}), 200
    """
    agency_ein = request.args.get('agency_ein')
    user_guid = request.args.get('user_guid', '')
    requests_opened = 0
    requests_closed = 0
    active_users = []
    is_visible = False
    results = False
    if agency_ein and user_guid == '':
        if agency_ein == 'all':
            active_requests = Requests.query.with_entities(Requests.status).join(
                Agencies, Requests.agency_ein == Agencies.ein).filter(
                Agencies.is_active).all()
            requests_closed = len([r for r in active_requests if r[0] == request_status.CLOSED])
            requests_opened = len(active_requests) - requests_closed
        else:
            active_requests = Requests.query.with_entities(Requests.status).join(
                Agencies, Requests.agency_ein == Agencies.ein).filter(
                Agencies.ein == agency_ein, Agencies.is_active).all()
            requests_closed = len([r for r in active_requests if r[0] == request_status.CLOSED])
            requests_opened = len(active_requests) - requests_closed
            if not (current_user.is_anonymous or current_user.is_public):
                if (current_user.is_agency and current_user.is_agency_admin(agency_ein)) or current_user.is_super:
                    is_visible = True
                    if current_user.is_agency_admin(agency_ein) or current_user.is_super:
                        active_users = sorted(
                            [(user.guid, user.name)
                             for user in Agencies.query.filter_by(ein=agency_ein).one().active_users],
                            key=lambda x: x[1])
                    elif current_user.is_agency_active(agency_ein):
                        active_users = [(current_user.guid, current_user.name)]
                    if active_users:
                        active_users.insert(0, ('', ''))
                        results = True

    elif user_guid and (current_user.is_agency_active(agency_ein) or
                        current_user.is_agency_admin(agency_ein) or
                        current_user.is_super):
        is_visible = True
        ureqs = UserRequests.query.filter(UserRequests.user_guid == user_guid
                                          ).all()

        requests_closed = len([u for u in ureqs if u.request.status == request_status.CLOSED])
        requests_opened = len([u for u in ureqs if u.request.status != request_status.CLOSED])

    return jsonify({"labels": ["Open", "Closed"],
                    "values": [requests_opened, requests_closed],
                    "active_users": active_users,
                    "is_visible": is_visible,
                    "results": results
                    }), 200
Example #3
0
    def __init__(self):
        super(SearchRequestsForm, self).__init__()
        self.agency_ein.choices = get_agency_choices()
        self.agency_ein.choices.insert(0, ('', 'All'))
        if current_user.is_agency:
            self.agency_ein.default = current_user.default_agency_ein
            user_agencies = sorted([(agencies.ein, agencies.name) for agencies in current_user.agencies
                                    if agencies.ein != current_user.default_agency_ein],
                                   key=lambda x: x[1])
            default_agency = current_user.default_agency

            # set default value of agency select field to agency user's primary agency
            self.agency_ein.default = default_agency.ein
            self.agency_ein.choices.insert(1, self.agency_ein.choices.pop(self.agency_ein.choices.index(
                (default_agency.ein, default_agency.name))
            ))

            # set secondary agencies to be below the primary
            for agency in user_agencies:
                self.agency_ein.choices.insert(2, self.agency_ein.choices.pop(self.agency_ein.choices.index(agency)))

            # get choices for agency user select field
            if current_user.is_agency_admin():
                self.agency_user.choices = get_active_users_as_choices(current_user.default_agency.ein)

            if current_user.is_agency_active() and not current_user.is_agency_admin():
                self.agency_user.choices = [
                    ('', 'All'),
                    (current_user.get_id(), 'My Requests')
                ]
                self.agency_user.default = current_user.get_id()

            # process form for default values
            self.process()
Example #4
0
def main(agency_ein=None):
    if not current_user.is_anonymous:
        if agency_ein is None:
            agency_ein = current_user.find_admin_agency_ein
        if current_user.is_super:
            agency_form = SelectAgencyForm(agency_ein)
            agency_ein = agency_ein or agency_form.agencies.choices[0][0]
            user_form = ActivateAgencyUserForm(agency_ein)
            active_users = get_agency_active_users(agency_ein)
            agency_is_active = Agencies.query.filter_by(ein=agency_ein).one().is_active
            return render_template("admin/main.html",
                                   agency_ein=agency_ein,
                                   users=active_users,
                                   user_form=user_form,
                                   agency_form=agency_form,
                                   agency_is_active=agency_is_active)
        elif current_user.is_agency_admin(agency_ein) and current_user.is_agency_active(agency_ein):
            form = ActivateAgencyUserForm(agency_ein)
            active_users = get_agency_active_users(agency_ein)
            del active_users[active_users.index(current_user)]
            if len(current_user.agencies.all()) > 1:
                agency_form = SelectAgencyForm(agency_ein)
                return render_template("admin/main.html",
                                       agency_ein=agency_ein,
                                       users=active_users,
                                       agency_form=agency_form,
                                       user_form=form,
                                       multi_agency_admin=True)
            return render_template("admin/main.html",
                                   users=active_users,
                                   agency_ein=agency_ein,
                                   user_form=form)

    return abort(404)
Example #5
0
    def __init__(self):
        super(SearchRequestsForm, self).__init__()
        self.agency_ein.choices = get_agency_choices()
        self.agency_ein.choices.insert(0, ("", "All"))
        if current_user.is_agency:
            self.agency_ein.default = current_user.default_agency_ein
            user_agencies = sorted(
                [(agencies.ein, agencies.name)
                 for agencies in current_user.agencies
                 if agencies.ein != current_user.default_agency_ein],
                key=lambda x: x[1],
            )
            default_agency = current_user.default_agency

            # set default value of agency select field to agency user's primary agency
            self.agency_ein.default = default_agency.ein
            self.agency_ein.choices.insert(
                1,
                self.agency_ein.choices.pop(
                    self.agency_ein.choices.index(
                        (default_agency.ein, default_agency.name))),
            )

            # set secondary agencies to be below the primary
            for agency in user_agencies:
                self.agency_ein.choices.insert(
                    2,
                    self.agency_ein.choices.pop(
                        self.agency_ein.choices.index(agency)),
                )

            # get choices for agency user select field
            if current_user.is_agency_admin():
                self.agency_user.choices = get_active_users_as_choices(
                    current_user.default_agency.ein)

            if current_user.is_agency_active(
            ) and not current_user.is_agency_admin():
                self.agency_user.choices = [
                    ("", "All"),
                    (current_user.get_id(), "My Requests"),
                ]
                self.agency_user.default = current_user.get_id()

            if default_agency.agency_features["custom_request_forms"][
                    "enabled"]:
                self.request_type.choices = [
                    (custom_request_form.form_name,
                     custom_request_form.form_name)
                    for custom_request_form in CustomRequestForms.query.
                    filter_by(agency_ein=default_agency.ein).order_by(
                        asc(CustomRequestForms.category),
                        asc(CustomRequestForms.id)).all()
                ]
                self.request_type.choices.insert(0, ("", "All"))

            # process form for default values
            self.process()
Example #6
0
    def __init__(self):
        super(SearchRequestsForm, self).__init__()
        self.agency_ein.choices = get_agency_choices()
        self.agency_ein.choices.insert(0, ("", "All"))
        if current_user.is_agency:
            self.agency_ein.default = current_user.default_agency_ein
            user_agencies = sorted(
                [(agencies.ein, agencies.name)
                 for agencies in current_user.agencies
                 if agencies.ein != current_user.default_agency_ein],
                key=lambda x: x[1],
            )
            default_agency = current_user.default_agency

            # set default value of agency select field to agency user's primary agency
            self.agency_ein.default = default_agency.ein
            self.agency_ein.choices.insert(
                1,
                self.agency_ein.choices.pop(
                    self.agency_ein.choices.index(
                        (default_agency.ein, default_agency.name))),
            )

            # set secondary agencies to be below the primary
            for agency in user_agencies:
                self.agency_ein.choices.insert(
                    2,
                    self.agency_ein.choices.pop(
                        self.agency_ein.choices.index(agency)),
                )

            # get choices for agency user select field
            if current_user.is_agency_admin():
                self.agency_user.choices = get_active_users_as_choices(
                    current_user.default_agency.ein)

            if current_user.is_agency_active(
            ) and not current_user.is_agency_admin():
                self.agency_user.choices = [
                    ("", "All"),
                    (current_user.get_id(), "My Requests"),
                ]
                self.agency_user.default = current_user.get_id()

            # process form for default values
            self.process()
Example #7
0
def delete(request_id):
    """
    Removes a user from a request and send notification emails.

    Expects a request body containing user's guid and confirmation string.
    Ex:
    {
        'user': '******',
        'remove-confirmation-string': 'remove'
    }
    :return:
    """
    agency_ein = Requests.query.filter_by(id=request_id).one().agency.ein

    if (current_user.is_agency and (
                current_user.is_super or (
                        current_user.is_agency_active(agency_ein) and
                        current_user.is_agency_admin(agency_ein)
                )
            )
        ):
        user_data = flask_request.form

        required_fields = ['user',
                           'remove-confirm-string']

        # TODO: Get copy from business, insert sentry issue key in message
        # Error handling to check if retrieved elements exist. Flash error message if elements does not exist.
        for field in required_fields:
            if user_data.get(field) is None:
                flash('Uh Oh, it looks like the {} is missing! '
                      'This is probably NOT your fault.'.format(field), category='danger')
                return redirect(url_for('request.view', request_id=request_id))

        valid_confirmation_string = "REMOVE"
        if user_data['remove-confirm-string'].upper() != valid_confirmation_string:
            flash('Uh Oh, it looks like the confirmation text is incorrect! '
                  'This is probably NOT your fault.', category='danger')
            return redirect(url_for('request.view', request_id=request_id))

        remove_user_request(request_id,
                            user_data['user'])
        return '', 200
    return '', 403
Example #8
0
def delete(request_id):
    """
    Removes a user from a request and send notification emails.

    Expects a request body containing user's guid and confirmation string.
    Ex:
    {
        'user': '******',
        'remove-confirmation-string': 'remove'
    }
    :return:
    """
    agency_ein = Requests.query.filter_by(id=request_id).one().agency.ein

    if (current_user.is_agency
            and (current_user.is_super or
                 (current_user.is_agency_active(agency_ein)
                  and current_user.is_agency_admin(agency_ein)))):
        user_data = flask_request.form

        required_fields = ['user', 'remove-confirm-string']

        # TODO: Get copy from business, insert sentry issue key in message
        # Error handling to check if retrieved elements exist. Flash error message if elements does not exist.
        for field in required_fields:
            if user_data.get(field) is None:
                flash('Uh Oh, it looks like the {} is missing! '
                      'This is probably NOT your fault.'.format(field),
                      category='danger')
                return redirect(url_for('request.view', request_id=request_id))

        valid_confirmation_string = "REMOVE"
        if user_data['remove-confirm-string'].upper(
        ) != valid_confirmation_string:
            flash(
                'Uh Oh, it looks like the confirmation text is incorrect! '
                'This is probably NOT your fault.',
                category='danger')
            return redirect(url_for('request.view', request_id=request_id))

        remove_user_request(request_id, user_data['user'])
        return '', 200
    return '', 403
Example #9
0
def get_active_users(agency_ein):
    """
    Retrieve the active users for the specified agency.

    :param agency_ein: Agency EIN (String)

    :return: JSON Object({"active_users": [('', 'All'), ('o8pj0k', 'John Doe')],
                          "is_admin": True}), 200
    """
    if current_user.is_agency_admin(agency_ein):
        return jsonify({"active_users": get_active_users_as_choices(agency_ein), "is_admin": True}), 200

    elif current_user.is_agency_active(agency_ein):
        active_users = [
            ('', 'All'),
            (current_user.get_id(), 'My Requests')
        ]
        return jsonify({"active_users": active_users, "is_admin": False}), 200

    else:
        return jsonify({}), 404
Example #10
0
def get_active_users(agency_ein):
    """
    Retrieve the active users for the specified agency.

    :param agency_ein: Agency EIN (String)

    :return: JSON Object({"active_users": [('', 'All'), ('o8pj0k', 'John Doe')],
                          "is_admin": True}), 200
    """
    if current_user.is_agency_admin(agency_ein):
        return jsonify({"active_users": get_active_users_as_choices(agency_ein), "is_admin": True}), 200

    elif current_user.is_agency_active(agency_ein):
        active_users = [
            ('', 'All'),
            (current_user.get_id(), 'My Requests')
        ]
        return jsonify({"active_users": active_users, "is_admin": False}), 200

    else:
        return jsonify({}), 404
Example #11
0
def get_request_types(agency_ein):
    """
    Retrieve the request types (custom request form names) for the specified agency.

    :param agency_ein: Agency EIN (String)

    :return: JSON Object({"request_type": [('', 'All'), ('Form Name', 'Form Name')]}), 200
    """
    if current_user.is_agency_active(agency_ein):
        agency = Agencies.query.filter_by(ein=agency_ein).one_or_none()
        if agency is not None and agency.agency_features['custom_request_forms']['enabled']:
            request_types = [
                (custom_request_form.form_name, custom_request_form.form_name)
                for custom_request_form in CustomRequestForms.query.filter_by(
                    agency_ein=agency_ein
                ).order_by(asc(CustomRequestForms.category), asc(CustomRequestForms.id)).all()
            ]
            request_types.insert(0, ("", "All"))
            return jsonify({"request_types": request_types}), 200

    return jsonify({}), 404
Example #12
0
def main(agency_ein=None):
    if not current_user.is_anonymous:
        if agency_ein is None:
            agency_ein = current_user.default_agency_ein
        if current_user.is_super:
            agency_form = SelectAgencyForm(agency_ein)
            agency_ein = agency_ein or agency_form.agencies.choices[0][0]
            user_form = ActivateAgencyUserForm(agency_ein)
            active_users = get_agency_active_users(agency_ein)
            agency_is_active = Agencies.query.filter_by(
                ein=agency_ein).one().is_active
            return render_template("admin/main.html",
                                   agency_ein=agency_ein,
                                   users=active_users,
                                   user_form=user_form,
                                   agency_form=agency_form,
                                   agency_is_active=agency_is_active)
        elif current_user.is_agency_admin(
                agency_ein) and current_user.is_agency_active(agency_ein):
            form = ActivateAgencyUserForm(agency_ein)
            active_users = get_agency_active_users(agency_ein)
            del active_users[active_users.index(current_user)]
            if len(current_user.agencies.all()) > 1:
                agency_form = SelectAgencyForm(agency_ein)
                return render_template("admin/main.html",
                                       agency_ein=agency_ein,
                                       users=active_users,
                                       agency_form=agency_form,
                                       user_form=form,
                                       multi_agency_admin=True)
            return render_template("admin/main.html",
                                   users=active_users,
                                   agency_ein=agency_ein,
                                   user_form=form)

    return abort(404)
Example #13
0
def post(request_id):
    """
    Create a new upload.

    Handles chunked files through the Content-Range header.
    For filesize validation and more upload logic, see:
        /static/js/upload/fileupload.js

    Optional request body parameters:
    - update (bool)
        save the uploaded file to the 'updated' directory
        (this indicates the file is meant to replace
        a previously uploaded file)
    - response_id (int)
        the id of a response associated with the file
        this upload is replacing
        - REQUIRED if 'update' is 'true'
        - ignored if 'update' is 'false'

    :returns: {
        "name": file name,
        "size": file size
    }
    """
    files = request.files
    file_ = files[next(files.keys())]
    filename = secure_filename(file_.filename)
    is_update = eval_request_bool(request.form.get('update'))
    agency_ein = Requests.query.filter_by(id=request_id).one().agency.ein
    if is_allowed(user=current_user, request_id=request_id, permission=permission.ADD_FILE) or \
            is_allowed(user=current_user, request_id=request_id, permission=permission.EDIT_FILE):
        response_id = request.form.get('response_id') if is_update else None
        if upload_exists(request_id, filename, response_id):
            response = {
                "files": [{
                    "name":
                    filename,
                    "error":
                    "A file with this name has already "
                    "been uploaded for this request."
                    # TODO: "link": <link-to-existing-file> ? would be nice
                }]
            }
        else:
            upload_path = os.path.join(
                current_app.config['UPLOAD_QUARANTINE_DIRECTORY'], request_id)
            if not os.path.exists(upload_path):
                os.mkdir(upload_path)
            filepath = os.path.join(upload_path, filename)
            key = get_upload_key(request_id, filename, is_update)

            try:
                if CONTENT_RANGE_HEADER in request.headers:
                    start, size = parse_content_range(
                        request.headers[CONTENT_RANGE_HEADER])

                    # Only validate mime type on first chunk
                    valid_file_type = True
                    file_type = None
                    if start == 0:
                        valid_file_type, file_type = is_valid_file_type(file_)
                        if current_user.is_agency_active(agency_ein):
                            valid_file_type = True
                        if os.path.exists(filepath):
                            # remove existing file (upload 'restarted' for same file)
                            os.remove(filepath)

                    if valid_file_type:
                        redis.set(key, upload_status.PROCESSING)
                        with open(filepath, 'ab') as fp:
                            fp.seek(start)
                            fp.write(file_.stream.read())
                        # scan if last chunk written
                        if os.path.getsize(filepath) == size:
                            scan_and_complete_upload.delay(
                                request_id, filepath, is_update, response_id)
                else:
                    valid_file_type, file_type = is_valid_file_type(file_)
                    if current_user.is_agency_active(agency_ein):
                        valid_file_type = True
                    if valid_file_type:
                        redis.set(key, upload_status.PROCESSING)
                        file_.save(filepath)
                        scan_and_complete_upload.delay(request_id, filepath,
                                                       is_update, response_id)

                if not valid_file_type:
                    response = {
                        "files": [{
                            "name":
                            filename,
                            "error":
                            "The file type '{}' is not allowed.".format(
                                file_type)
                        }]
                    }
                else:
                    response = {
                        "files": [{
                            "name": filename,
                            "original_name": file_.filename,
                            "size": os.path.getsize(filepath),
                        }]
                    }
            except Exception as e:
                redis.set(key, upload_status.ERROR)
                current_app.logger.exception(
                    "Upload for file '{}' failed: {}".format(filename, e))
                response = {
                    "files": [{
                        "name":
                        filename,
                        "error":
                        "There was a problem uploading this file."
                    }]
                }

        return jsonify(response), 200
Example #14
0
def patch(user_id):
    """
    Request Parameters:
    - title
    - organization
    - email
    - phone_number
    - fax_number
    - mailing_address
    - is_super
    - is_agency_active
    - is_agency_admin
    (Mailing Address)
    - zip
    - city
    - state
    - address_one
    - address_two

    Restrictions:
    - Anonymous Users
        - cannot access this endpoint
    - Agency Administrators
        - cannot change their agency status
        - can only update the agency status of users within their agency
        - cannot change any super user status
    - Super Users
        - cannot change their super user status
    - Agency Users
        - cannot change any user except for themselves or
          *anonymous* requesters for requests they are assigned to
        - cannot change super user or agency status
    - Public Users
        - can only update themselves
        - cannot change super user or agency status

    """
    if not current_user.is_anonymous:
        # attempt to parse user_id and find user
        try:
            guid, auth_type = user_id.split(USER_ID_DELIMITER)
            user_ = Users.query.filter_by(guid=guid,
                                          auth_user_type=auth_type).one()
        except (ValueError, NoResultFound, MultipleResultsFound):
            return jsonify({}), 404

        agency_ein = request.form.get('agency_ein', None)
        if agency_ein is None and not (auth_type
                                       == user_type_auth.ANONYMOUS_USER
                                       or 'is_super' in request.form):
            return jsonify({}), 404

        updating_self = current_user == user_
        current_user_is_agency_user = (
            current_user.is_agency and not current_user.is_super
            and not current_user.is_agency_admin(agency_ein)
            and current_user.is_agency_active(agency_ein))
        current_user_is_agency_admin = (
            current_user.is_agency and not current_user.is_super
            and current_user.is_agency_admin(agency_ein)
            and current_user.is_agency_active(agency_ein))
        same_agency = agency_ein in [
            agency.ein for agency in current_user.agencies.all()
        ]
        associated_anonymous_requester = (
            user_.is_anonymous_requester
            and current_user.user_requests.filter_by(
                request_id=user_.anonymous_request.id).first() is None)

        is_agency_admin = request.form.get('is_agency_admin')
        is_agency_active = request.form.get('is_agency_active')
        is_super = request.form.get('is_super')

        changing_status = any((is_agency_active, is_agency_admin, is_super))

        rform_copy = dict(request.form)
        try:
            rform_copy.pop('is_agency_admin')
            rform_copy.pop('is_agency_active')
            rform_copy.pop('agency_ein')
            changing_more_than_agency_status = len(rform_copy) != 0
        except KeyError:
            changing_more_than_agency_status = False

        # VALIDATE
        if ((
                updating_self and
            (
                # super user attempting to change their own super status
                (current_user.is_super and is_super is not None) or
                # agency admin or public user attempting to change their own agency/super status
                (changing_status and
                 (current_user_is_agency_admin or current_user.is_public)))) or
            (not updating_self and (
                # public user attempting to change another user
                current_user.is_public or
                # agency user attempting to change a agency/super status
                (current_user_is_agency_user and changing_status) or
                # agency user attempting to change a user that is not an anonymous requester
                # for a request they are assigned to
                (current_user_is_agency_user and
                 (not user_.is_anonymous_requester
                  or not associated_anonymous_requester)) or
                # agency admin attempting to change another user that is not in the same agency or
                # attempting to change more than just the agency status of a user
                (current_user_is_agency_admin
                 and not (associated_anonymous_requester
                          or user_.is_anonymous_requester) and
                 (not same_agency or changing_more_than_agency_status)) or
                # agency admin attempting to change an anonymous requester for a request
                # they are not assigned to
                (current_user_is_agency_admin
                 and associated_anonymous_requester)))):
            return jsonify({}), 403

        # UPDATE
        user_fields = [
            'email', 'phone_number', 'fax_number', 'title', 'organization'
        ]
        status_fields = ['is_agency_admin', 'is_agency_active', 'is_super']
        address_fields = ['zip', 'city', 'state', 'address_one', 'address_two']

        user_field_val = {
            'email': request.form.get('email'),
            'phone_number': request.form.get('phone'),
            'fax_number': request.form.get('fax'),
            'title': request.form.get('title'),
            'organization': request.form.get('organization'),
        }
        status_field_val = {
            'is_agency_admin': request.form.get('is_agency_admin'),
            'is_agency_active': request.form.get('is_agency_active'),
            'is_super': request.form.get('is_super')
        }
        address_field_val = {
            'address_one': request.form.get('address_one'),
            'address_two': request.form.get('address_two'),
            'zip': request.form.get('zipcode'),
            'city': request.form.get('city'),
            'state': request.form.get('state')
        }

        # check if missing contact information
        if (user_field_val['email'] == ''
                and user_field_val['phone_number'] == ''
                and user_field_val['fax_number'] == '' and
            (address_field_val['city'] == '' or address_field_val['zip'] == ''
             or address_field_val['state'] == ''
             or address_field_val['address_one'] == '')):
            return jsonify({"error": "Missing contact information."}), 400

        old = {}
        old_address = {}
        new = {}
        new_address = {}

        for field in status_fields:
            if status_field_val[field] is not None:
                if field == 'is_agency_admin':
                    cur_val = user_.is_agency_admin(agency_ein)
                elif field == 'is_agency_active':
                    cur_val = user_.is_agency_active(agency_ein)
                else:
                    cur_val = getattr(user_, field)
                new_val = eval_request_bool(status_field_val[field])
                if cur_val != new_val:
                    old[field] = cur_val
                    new[field] = new_val

        for field in user_fields:
            val = user_field_val[field]
            if val is not None:
                if val == '':
                    user_field_val[
                        field] = None  # null in db, not empty string
                cur_val = getattr(user_, field)
                new_val = user_field_val[field]
                if cur_val != new_val:
                    old[field] = cur_val
                    new[field] = new_val

        for field in address_fields:
            val = address_field_val[field]
            if val is not None:
                if val == '':
                    address_field_val[field] = None
                cur_val = (user_.mailing_address.get(field)
                           if user_.mailing_address else None)
                new_val = address_field_val[field]
                if cur_val != new_val:
                    old_address[field] = cur_val
                    new_address[field] = new_val

        if new or new_address:
            # in spite of not changing, the guid and auth type of
            # the user being updated is added to Events.new_value
            # in order to identify this user
            new['user_guid'] = user_.guid
            new['auth_user_type'] = user_.auth_user_type

            if new_address:
                new['mailing_address'] = new_address
            if old_address:
                old['mailing_address'] = old_address

            if ('is_agency_admin' in new) or ('is_agency_active' in new):
                new['agency_ein'] = agency_ein
                update_object(new, AgencyUsers, (guid, auth_type, agency_ein))
            else:
                update_object(new, Users, (guid, auth_type))

            # create event(s)
            event_kwargs = {
                'request_id':
                user_.anonymous_request.id
                if user_.is_anonymous_requester else None,
                'response_id':
                None,
                'user_guid':
                current_user.guid,
                'auth_user_type':
                current_user.auth_user_type,
                'timestamp':
                datetime.utcnow()
            }

            if changing_status:
                new_statuses = {}
                old_statuses = {}
                for field in status_fields:
                    if new.get(field) is not None:
                        new_statuses[field] = new.pop(field)
                        old_statuses[field] = old.pop(field)

                # TODO: a better way to store user identifiers (than in the value columns)
                new_statuses['user_guid'] = user_.guid
                new_statuses['auth_user_type'] = user_.auth_user_type
                new_statuses['agency_ein'] = agency_ein

                is_agency_active = new_statuses.get('is_agency_active')
                is_agency_admin = new_statuses.get('is_agency_admin')

                if is_agency_active is not None and not is_agency_active:
                    # remove ALL UserRequests
                    for user_request in user_.user_requests.all():
                        create_user_request_event(event_type.USER_REMOVED,
                                                  user_request)
                        delete_object(user_request)
                elif is_agency_admin is not None:

                    def set_permissions_and_create_event(user_req, perms):
                        """
                        Set permissions for a user request and create a
                        'user_permissions_changed' Event.

                        :param user_req: user request
                        :param perms: permissions to set for user request
                        """
                        old_permissions = user_req.permissions
                        user_request.set_permissions(perms)
                        create_user_request_event(event_type.USER_PERM_CHANGED,
                                                  user_req, old_permissions)

                    if is_agency_admin:
                        permissions = Roles.query.filter_by(
                            name=role_name.AGENCY_ADMIN).one().permissions
                        # create UserRequests for ALL existing requests under user's agency where user is not assigned
                        # for where the user *is* assigned, only change the permissions
                        for req in user_.agencies.filter_by(
                                ein=agency_ein).one().requests:
                            user_request = UserRequests.query.filter_by(
                                request_id=req.id,
                                user_guid=user_.guid,
                                auth_user_type=user_.auth_user_type).first()
                            if user_request is None:
                                user_request = UserRequests(
                                    user_guid=user_.guid,
                                    auth_user_type=user_.auth_user_type,
                                    request_id=req.id,
                                    request_user_type=user_type_request.AGENCY,
                                    permissions=permissions)
                                create_object(user_request)
                                create_user_request_event(
                                    event_type.USER_ADDED, user_request)
                            else:
                                set_permissions_and_create_event(
                                    user_request, permissions)

                    else:
                        # update ALL UserRequests (strip user of permissions)
                        for user_request in user_.user_requests.all():
                            set_permissions_and_create_event(
                                user_request, permission.NONE)

                # TODO: single email detailing user changes?

                create_object(
                    Events(type_=event_type.USER_STATUS_CHANGED,
                           previous_value=old_statuses,
                           new_value=new_statuses,
                           **event_kwargs))

            if old:  # something besides status changed ('new' holds user guid and auth type)
                create_object(
                    Events(type_=(event_type.REQUESTER_INFO_EDITED
                                  if user_.is_anonymous_requester else
                                  event_type.USER_INFO_EDITED),
                           previous_value=old,
                           new_value=new,
                           **event_kwargs))
            return jsonify({}), 200
        else:
            return jsonify({"message": "No changes detected."}), 200

    return jsonify({}), 403
Example #15
0
def post(request_id):
    """
    Create a new upload.

    Handles chunked files through the Content-Range header.
    For filesize validation and more upload logic, see:
        /static/js/upload/fileupload.js

    Optional request body parameters:
    - update (bool)
        save the uploaded file to the 'updated' directory
        (this indicates the file is meant to replace
        a previously uploaded file)
    - response_id (int)
        the id of a response associated with the file
        this upload is replacing
        - REQUIRED if 'update' is 'true'
        - ignored if 'update' is 'false'

    :returns: {
        "name": file name,
        "size": file size
    }
    """
    files = request.files
    file_ = files[next(files.keys())]
    filename = secure_filename(file_.filename)
    is_update = eval_request_bool(request.form.get('update'))
    agency_ein = Requests.query.filter_by(id=request_id).one().agency.ein
    if is_allowed(user=current_user, request_id=request_id, permission=permission.ADD_FILE) or \
            is_allowed(user=current_user, request_id=request_id, permission=permission.EDIT_FILE):
        response_id = request.form.get('response_id') if is_update else None
        if upload_exists(request_id, filename, response_id):
            response = {
                "files": [{
                    "name": filename,
                    "error": "A file with this name has already "
                             "been uploaded for this request."
                    # TODO: "link": <link-to-existing-file> ? would be nice
                }]
            }
        else:
            upload_path = os.path.join(
                current_app.config['UPLOAD_QUARANTINE_DIRECTORY'],
                request_id)
            if not os.path.exists(upload_path):
                os.mkdir(upload_path)
            filepath = os.path.join(upload_path, filename)
            key = get_upload_key(request_id, filename, is_update)

            try:
                if CONTENT_RANGE_HEADER in request.headers:
                    start, size = parse_content_range(
                        request.headers[CONTENT_RANGE_HEADER])

                    # Only validate mime type on first chunk
                    valid_file_type = True
                    file_type = None
                    if start == 0:
                        valid_file_type, file_type = is_valid_file_type(file_)
                        if current_user.is_agency_active(agency_ein):
                            valid_file_type = True
                        if os.path.exists(filepath):
                            # remove existing file (upload 'restarted' for same file)
                            os.remove(filepath)

                    if valid_file_type:
                        redis.set(key, upload_status.PROCESSING)
                        with open(filepath, 'ab') as fp:
                            fp.seek(start)
                            fp.write(file_.stream.read())
                        # scan if last chunk written
                        if os.path.getsize(filepath) == size:
                            scan_and_complete_upload.delay(request_id, filepath, is_update, response_id)
                else:
                    valid_file_type, file_type = is_valid_file_type(file_)
                    if current_user.is_agency_active(agency_ein):
                        valid_file_type = True
                    if valid_file_type:
                        redis.set(key, upload_status.PROCESSING)
                        file_.save(filepath)
                        scan_and_complete_upload.delay(request_id, filepath, is_update, response_id)

                if not valid_file_type:
                    response = {
                        "files": [{
                            "name": filename,
                            "error": "The file type '{}' is not allowed.".format(
                                file_type)
                        }]
                    }
                else:
                    response = {
                        "files": [{
                            "name": filename,
                            "original_name": file_.filename,
                            "size": os.path.getsize(filepath),
                        }]
                    }
            except Exception as e:
                sentry.captureException()
                redis.set(key, upload_status.ERROR)
                current_app.logger.exception("Upload for file '{}' failed: {}".format(filename, e))
                response = {
                    "files": [{
                        "name": filename,
                        "error": "There was a problem uploading this file."
                    }]
                }

        return jsonify(response), 200