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
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
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()
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)
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()
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()
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
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
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
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
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
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)
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
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
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