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 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() 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, agency_ein=None): super(SelectAgencyForm, self).__init__() if current_user.is_super: # Super Users will always see every agency in the dropdown. self.agencies.choices = [ (agency.ein, '({status}) {agency_name}'.format( status='ACTIVE', agency_name=agency.name) if agency.is_active else '{agency_name}'.format( agency_name=agency.name)) for agency in Agencies.query.order_by( Agencies.is_active.desc(), Agencies._name.asc()).all() ] else: # Multi-Agency Admin Users will only see the agencies that they administer in the dropdown. agency_choices = [] for agency in current_user.agencies.order_by( Agencies.is_active.desc(), Agencies._name.asc()).all(): if current_user.is_agency_admin(agency.ein): agency_tuple = ( agency.ein, '({status}) {agency_name}'.format( status='ACTIVE', agency_name=agency.name) if agency.is_active else '{agency_name}'.format( agency_name=agency.name)) agency_choices.append(agency_tuple) self.agencies.choices = agency_choices if agency_ein: for agency in self.agencies.choices: if agency[0] == agency_ein: self.agencies.choices.insert( 0, self.agencies.choices.pop( self.agencies.choices.index(agency))) 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() # process form for default values self.process()
def edit(request_id): """ Updates a users permissions on a request and sends notification emails. Expects a request body containing the user's guid and updated permissions. Ex: { 'user': '******', 1: true, 5: false } :return: """ current_user_request = current_user.user_requests.filter_by( request_id=request_id).one() current_request = current_user_request.request if (current_user.is_agency and (current_user.is_super or current_user.is_agency_admin(current_request.agency.ein) or current_user_request.has_permission( permission.EDIT_USER_REQUEST_PERMISSIONS))): user_data = flask_request.form point_of_contact = True if role_name.POINT_OF_CONTACT in user_data else False required_fields = ['user'] for field in required_fields: if not user_data.get(field): 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)) try: permissions = [int(i) for i in user_data.getlist('permission')] edit_user_request(request_id=request_id, user_guid=user_data.get('user'), permissions=permissions, point_of_contact=point_of_contact) except UserRequestException as e: sentry.captureException() flash(e, category='warning') return redirect(url_for('request.view', request_id=request_id)) return 'OK', 200 return abort(403)
def edit(request_id): """ Updates a users permissions on a request and sends notification emails. Expects a request body containing the user's guid and updated permissions. Ex: { 'user': '******', 1: true, 5: false } :return: """ current_user_request = current_user.user_requests.filter_by(request_id=request_id).one() current_request = current_user_request.request if ( current_user.is_agency and ( current_user.is_super or current_user.is_agency_admin(current_request.agency.ein) or current_user_request.has_permission(permission.EDIT_USER_REQUEST_PERMISSIONS) ) ): user_data = flask_request.form point_of_contact = True if role_name.POINT_OF_CONTACT in user_data else False required_fields = ['user'] for field in required_fields: if not user_data.get(field): 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)) try: permissions = [int(i) for i in user_data.getlist('permission')] edit_user_request(request_id=request_id, user_guid=user_data.get('user'), permissions=permissions, point_of_contact=point_of_contact) except UserRequestException as e: sentry.captureException() flash(e, category='warning') return redirect(url_for('request.view', request_id=request_id)) return 'OK', 200 return abort(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 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 create(request_id): """ Creates a users permissions entry for a request and sends notification emails. Expects a request body containing the user's guid and new permissions. Ex: { 'user': '******', 1: true, 5: false } :return: """ current_user_request = current_user.user_requests.filter_by( request_id=request_id).first() current_request = current_user_request.request if (current_user.is_agency and (current_user.is_super or current_user.is_agency_admin(current_request.agency.ein) or current_user_request.has_permission(permission.ADD_USER_TO_REQUEST))): user_data = flask_request.form required_fields = ['user'] for field in required_fields: if not user_data.get(field): 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)) try: permissions = [int(i) for i in user_data.getlist('permission')] add_user_request(request_id=request_id, user_guid=user_data.get('user'), permissions=permissions) except UserRequestException as e: flash(str(e), category='warning') return redirect(url_for('request.view', request_id=request_id)) return redirect(url_for('request.view', request_id=request_id)) return abort(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 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 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 """ # Anonymous users cannot access endpoint if current_user.is_anonymous: return jsonify({'error': 'Anonymous users cannot access this endpoint'}), 403 # Public users cannot access endpoint if current_user.is_public: return jsonify({'error': 'Public users cannot access this endpoint'}), 403 # Unauthenticated users cannot access endpoint if not current_user.is_authenticated: return jsonify({'error': 'User must be authenticated to access endpoint'}), 403 # Retrieve the user user_ = Users.query.filter_by(guid=user_id).one_or_none() # If the user does not exist, return 404 - Not Found if not user_: return jsonify({'error': 'Specified user does not exist.'}), 404 # Gather Form details is_agency_admin = eval_request_bool(request.form.get('is_agency_admin')) if request.form.get('is_agency_admin', None) else None is_agency_active = eval_request_bool(request.form.get('is_agency_active')) if request.form.get('is_agency_active', None) else None is_super = eval_request_bool(request.form.get('is_super')) if request.form.get('is_super', None) else None agency_ein = request.form.get('agency_ein', None) # Checks that apply if user is changing their own profile changing_self = current_user == user_ # Agency User Restrictions (applies to Admins and Regular Users) if user_.is_agency: # Endpoint can only be used for a specific agency if not agency_ein: return jsonify({'error': 'agency_ein must be provided to modify an agency user'}), 400 # Agency must exist and be active to modify users agency = Agencies.query.filter_by(ein=agency_ein).one_or_none() if not agency and agency.is_active: return jsonify({'error': 'Agency must exist in the database and be active'}), 400 if not current_user.is_super: # Current user must belong to agency specified by agency_ein current_user_is_agency_admin = current_user.is_agency_admin(agency.ein) if not current_user_is_agency_admin: return jsonify({'error': 'Current user must belong to agency specified by agency_ein'}), 400 user_in_agency = AgencyUsers.query.filter(AgencyUsers.user_guid == user_.guid, AgencyUsers.agency_ein == agency_ein).one_or_none() if user_in_agency is None: return jsonify({'error': 'User to be modified must belong to agency specified by agency_ein'}), 400 # Non-Agency Admins cannot access endpoint to modify other agency_users if not current_user.is_super and not current_user.is_agency_admin(agency_ein): return jsonify({'error': 'User must be agency admin to modify users'}), 403 # Cannot modify super status when changing agency active or agency admin status if (is_agency_admin or is_agency_active) and is_super: return jsonify({ 'error': 'Cannot modify super status when changing agency active or agency admin status'}), 400 if changing_self: # Super users cannot change their own is_super value if current_user.is_super and is_super: return jsonify({'error': 'Super users cannot change their own `super` status'}), 400 if is_agency_admin: return jsonify({'error': 'Agency Administrators cannot change their administrator permissions'}), 400 elif user_.is_public: if current_user != user_: return jsonify({'error': 'Public user attributes cannot be modified by agency users.'}), 400 elif user_.is_anonymous_requester: ur = current_user.user_requests.filter_by(request_id=user_.anonymous_request.id).one_or_none() if not ur: return jsonify({ 'error': 'Agency users can only modify anonymous requesters for requests where they are assigned.'}), 403 if not ur.has_permission(permission.EDIT_REQUESTER_INFO): return jsonify({'error': 'Current user does not have EDIT_REQUESTER_INFO permission'}), 403 # Gather User Fields user_editable_fields = user_attrs.UserEditableFieldsDict( 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'), address_one=request.form.get('address_one'), address_two=request.form.get('address_two'), zip=request.form.get('zipcode'), city=request.form.get('city'), ) status_field_val = user_attrs.UserStatusDict( 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') ) if changing_self: if not user_editable_fields.is_valid: return jsonify({"error": "Missing contact information."}), 400 else: if user_.is_agency and not status_field_val.is_valid: return jsonify({"error": "User status values invalid"}), 400 # Status Values for Events old_status = {} new_status = {} # User Attributes for Events old_user_attrs = {'_mailing_address': {}} new_user_attrs = {'_mailing_address': {}} for key, value in status_field_val.items(): if value is not None: if key == 'is_agency_admin': cur_val = user_.is_agency_admin(agency_ein) elif key == 'is_agency_active': cur_val = user_.is_agency_active(agency_ein) else: cur_val = getattr(user_, key) new_val = eval_request_bool(status_field_val[key]) if cur_val != new_val: old_status[key] = cur_val new_status[key] = new_val for key, value in user_editable_fields.items(): # Address is a dictionary and needs to be handled separately if key == 'address': for address_key, address_value in value.items(): cur_val = (user_.mailing_address.get(address_key) if user_.mailing_address else None) new_val = address_value if cur_val != new_val: old_user_attrs['_mailing_address'][address_key] = cur_val new_user_attrs['_mailing_address'][address_key] = new_val continue if value is not None: cur_val = getattr(user_, key) new_val = user_editable_fields[key] if cur_val != new_val: old_user_attrs[key] = cur_val new_user_attrs[key] = new_val # Update the Users object if new_user_attrs is not None (empty dict) if new_user_attrs and new_user_attrs.get('_mailing_address') and old_user_attrs: update_object( new_user_attrs, Users, user_id ) # GUID is added to the 'new' value in events to identify the user that was changed new_user_attrs['user_guid'] = user_.guid # 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, 'timestamp': datetime.utcnow() } create_object(Events( type_=(event_type.REQUESTER_INFO_EDITED if user_.is_anonymous_requester else event_type.USER_INFO_EDITED), previous_value=old_user_attrs, new_value=new_user_attrs, **event_kwargs )) if new_status: redis_key = "{current_user_guid}-{update_user_guid}-{agency_ein}-{timestamp}".format( current_user_guid=current_user.guid, update_user_guid=user_.guid, agency_ein=agency_ein, timestamp=datetime.now()) old_status['user_guid'] = user_.guid new_status['user_guid'] = user_.guid old_status['agency_ein'] = agency_ein new_status['agency_ein'] = agency_ein # Update agency active status and create associated event. Occurs first because a user can be # activated / deactivated with admin status set to True. if is_agency_active is not None: update_object( new_status, AgencyUsers, (user_.guid, agency_ein) ) event_kwargs = { 'request_id': user_.anonymous_request.id if user_.is_anonymous_requester else None, 'response_id': None, 'user_guid': current_user.guid, 'timestamp': datetime.utcnow() } if is_agency_active: create_object(Events( type_=event_type.AGENCY_USER_ACTIVATED, previous_value=old_status, new_value=new_status, **event_kwargs )) if is_agency_admin is not None and is_agency_admin: make_user_admin.apply_async(args=(user_.guid, current_user.guid, agency_ein), task_id=redis_key) return jsonify({'status': 'success', 'message': 'Update task has been scheduled.'}), 200 else: create_object(Events( type_=event_type.AGENCY_USER_DEACTIVATED, previous_value=old_status, new_value=new_status, **event_kwargs )) remove_user_permissions.apply_async( args=(user_.guid, current_user.guid, agency_ein, event_type.AGENCY_USER_DEACTIVATED), task_id=redis_key) return jsonify({'status': 'success', 'message': 'Update task has been scheduled.'}), 200 return jsonify({'status': 'success', 'message': 'Agency user successfully updated'}), 200 # Update agency admin status and create associated event. elif is_agency_admin is not None: new_status['agency_ein'] = agency_ein update_object( new_status, AgencyUsers, (user_.guid, agency_ein) ) event_kwargs = { 'request_id': user_.anonymous_request.id if user_.is_anonymous_requester else None, 'response_id': None, 'user_guid': current_user.guid, 'timestamp': datetime.utcnow() } if is_agency_admin: create_object(Events( type_=event_type.USER_MADE_AGENCY_ADMIN, previous_value=old_status, new_value=new_status, **event_kwargs )) make_user_admin.apply_async(args=(user_.guid, current_user.guid, agency_ein), task_id=redis_key) return jsonify({'status': 'success', 'message': 'Update task has been scheduled.'}), 200 else: create_object(Events( type_=event_type.USER_MADE_AGENCY_USER, previous_value=old_status, new_value=new_status, **event_kwargs )) remove_user_permissions.apply_async( args=(user_.guid, current_user.guid, agency_ein, event_type.USER_MADE_AGENCY_USER), task_id=redis_key) return jsonify({'status': 'success', 'message': 'Update task has been scheduled.'}), 200 # Update user super status and create associated event. elif is_super is not None: new_status['agency_ein'] = agency_ein update_object( new_status, AgencyUsers, (user_.guid, agency_ein) ) event_kwargs = { 'request_id': user_.anonymous_request.id if user_.is_anonymous_requester else None, 'response_id': None, 'user_guid': current_user.guid, 'timestamp': datetime.utcnow() } if is_super: create_object(Events( type_=event_type.USER_MADE_SUPER_USER, previous_value=old_status, new_value=new_status, **event_kwargs )) else: create_object(Events( type_=event_type.USER_REMOVED_FROM_SUPER, previous_value=old_status, new_value=new_status, **event_kwargs )) return jsonify({'status': 'success', 'message': 'Agency user successfully updated'}), 200 # Always returns 200 so that we can use the data from the response in the client side javascript return jsonify({'status': 'Not Modified', 'message': 'No changes detected'}), 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 """ # Anonymous users cannot access endpoint if current_user.is_anonymous: return jsonify( {'error': 'Anonymous users cannot access this endpoint'}), 403 # Public users cannot access endpoint if current_user.is_public: return jsonify({'error': 'Public users cannot access this endpoint'}), 403 # Unauthenticated users cannot access endpoint if not current_user.is_authenticated: return jsonify( {'error': 'User must be authenticated to access endpoint'}), 403 # Retrieve the user user_ = Users.query.filter_by(guid=user_id).one_or_none() # If the user does not exist, return 404 - Not Found if not user_: return jsonify({'error': 'Specified user does not exist.'}), 404 # Gather Form details is_agency_admin = eval_request_bool( request.form.get('is_agency_admin')) if request.form.get( 'is_agency_admin', None) else None is_agency_active = eval_request_bool( request.form.get('is_agency_active')) if request.form.get( 'is_agency_active', None) else None is_super = eval_request_bool( request.form.get('is_super')) if request.form.get('is_super', None) else None agency_ein = request.form.get('agency_ein', None) # Checks that apply if user is changing their own profile changing_self = current_user == user_ # Agency User Restrictions (applies to Admins and Regular Users) if user_.is_agency: # Endpoint can only be used for a specific agency if not agency_ein: return jsonify({ 'error': 'agency_ein must be provided to modify an agency user' }), 400 # Agency must exist and be active to modify users agency = Agencies.query.filter_by(ein=agency_ein).one_or_none() if not agency and agency.is_active: return jsonify( {'error': 'Agency must exist in the database and be active'}), 400 if not current_user.is_super: # Current user must belong to agency specified by agency_ein current_user_is_agency_admin = current_user.is_agency_admin( agency.ein) if not current_user_is_agency_admin: return jsonify({ 'error': 'Current user must belong to agency specified by agency_ein' }), 400 user_in_agency = AgencyUsers.query.filter( AgencyUsers.user_guid == user_.guid, AgencyUsers.agency_ein == agency_ein).one_or_none() if user_in_agency is None: return jsonify({ 'error': 'User to be modified must belong to agency specified by agency_ein' }), 400 # Non-Agency Admins cannot access endpoint to modify other agency_users if not current_user.is_super and not current_user.is_agency_admin( agency_ein): return jsonify( {'error': 'User must be agency admin to modify users'}), 403 # Cannot modify super status when changing agency active or agency admin status if (is_agency_admin or is_agency_active) and is_super: return jsonify({ 'error': 'Cannot modify super status when changing agency active or agency admin status' }), 400 if changing_self: # Super users cannot change their own is_super value if current_user.is_super and is_super: return jsonify({ 'error': 'Super users cannot change their own `super` status' }), 400 if is_agency_admin: return jsonify({ 'error': 'Agency Administrators cannot change their administrator permissions' }), 400 elif user_.is_public: if current_user != user_: return jsonify({ 'error': 'Public user attributes cannot be modified by agency users.' }), 400 elif user_.is_anonymous_requester: ur = current_user.user_requests.filter_by( request_id=user_.anonymous_request.id).one_or_none() if not ur: return jsonify({ 'error': 'Agency users can only modify anonymous requesters for requests where they are assigned.' }), 403 if not ur.has_permission(permission.EDIT_REQUESTER_INFO): return jsonify({ 'error': 'Current user does not have EDIT_REQUESTER_INFO permission' }), 403 # Gather User Fields user_editable_fields = user_attrs.UserEditableFieldsDict( 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'), 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')) status_field_val = user_attrs.UserStatusDict( 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')) if changing_self: if not user_editable_fields.is_valid: return jsonify({"error": "Missing contact information."}), 400 else: if user_.is_agency and not status_field_val.is_valid: return jsonify({"error": "User status values invalid"}), 400 # Status Values for Events old_status = {} new_status = {} # User Attributes for Events old_user_attrs = defaultdict(dict) new_user_attrs = defaultdict(dict) for key, value in status_field_val.items(): if value is not None: if key == 'is_agency_admin': cur_val = user_.is_agency_admin(agency_ein) elif key == 'is_agency_active': cur_val = user_.is_agency_active(agency_ein) else: cur_val = getattr(user_, key) new_val = eval_request_bool(status_field_val[key]) if cur_val != new_val: old_status[key] = cur_val new_status[key] = new_val for key, value in user_editable_fields.items(): # Address is a dictionary and needs to be handled separately if key == 'address': for address_key, address_value in value.items(): cur_val = (user_.mailing_address.get(address_key) if user_.mailing_address else None) new_val = address_value if cur_val != new_val: old_user_attrs['_mailing_address'][address_key] = cur_val new_user_attrs['_mailing_address'][address_key] = new_val continue if value is not None: cur_val = getattr(user_, key) new_val = user_editable_fields[key] if cur_val != new_val: old_user_attrs[key] = cur_val new_user_attrs[key] = new_val # Update the Users object if new_user_attrs is not None (empty dict) if new_user_attrs and old_user_attrs: update_object(new_user_attrs, Users, user_id) # GUID is added to the 'new' value in events to identify the user that was changed new_user_attrs['user_guid'] = user_.guid # 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, 'timestamp': datetime.utcnow() } create_object( Events(type_=(event_type.REQUESTER_INFO_EDITED if user_.is_anonymous_requester else event_type.USER_INFO_EDITED), previous_value=old_user_attrs, new_value=new_user_attrs, **event_kwargs)) if new_status: redis_key = "{current_user_guid}-{update_user_guid}-{agency_ein}-{timestamp}".format( current_user_guid=current_user.guid, update_user_guid=user_.guid, agency_ein=agency_ein, timestamp=datetime.now()) old_status['user_guid'] = user_.guid new_status['user_guid'] = user_.guid old_status['agency_ein'] = agency_ein new_status['agency_ein'] = agency_ein # Update agency active status and create associated event. Occurs first because a user can be # activated / deactivated with admin status set to True. if is_agency_active is not None: update_object(new_status, AgencyUsers, (user_.guid, agency_ein)) event_kwargs = { 'request_id': user_.anonymous_request.id if user_.is_anonymous_requester else None, 'response_id': None, 'user_guid': current_user.guid, 'timestamp': datetime.utcnow() } if is_agency_active: create_object( Events(type_=event_type.AGENCY_USER_ACTIVATED, previous_value=old_status, new_value=new_status, **event_kwargs)) if is_agency_admin is not None and is_agency_admin: make_user_admin.apply_async(args=(user_.guid, current_user.guid, agency_ein), task_id=redis_key) return jsonify({ 'status': 'success', 'message': 'Update task has been scheduled.' }), 200 else: create_object( Events(type_=event_type.AGENCY_USER_DEACTIVATED, previous_value=old_status, new_value=new_status, **event_kwargs)) remove_user_permissions.apply_async( args=(user_.guid, current_user.guid, agency_ein, event_type.AGENCY_USER_DEACTIVATED), task_id=redis_key) return jsonify({ 'status': 'success', 'message': 'Update task has been scheduled.' }), 200 return jsonify({ 'status': 'success', 'message': 'Agency user successfully updated' }), 200 # Update agency admin status and create associated event. elif is_agency_admin is not None: new_status['agency_ein'] = agency_ein update_object(new_status, AgencyUsers, (user_.guid, agency_ein)) event_kwargs = { 'request_id': user_.anonymous_request.id if user_.is_anonymous_requester else None, 'response_id': None, 'user_guid': current_user.guid, 'timestamp': datetime.utcnow() } if is_agency_admin: create_object( Events(type_=event_type.USER_MADE_AGENCY_ADMIN, previous_value=old_status, new_value=new_status, **event_kwargs)) make_user_admin.apply_async(args=(user_.guid, current_user.guid, agency_ein), task_id=redis_key) return jsonify({ 'status': 'success', 'message': 'Update task has been scheduled.' }), 200 else: create_object( Events(type_=event_type.USER_MADE_AGENCY_USER, previous_value=old_status, new_value=new_status, **event_kwargs)) remove_user_permissions.apply_async( args=(user_.guid, current_user.guid, agency_ein, event_type.USER_MADE_AGENCY_USER), task_id=redis_key) return jsonify({ 'status': 'success', 'message': 'Update task has been scheduled.' }), 200 # Update user super status and create associated event. elif is_super is not None: new_status['agency_ein'] = agency_ein update_object(new_status, AgencyUsers, (user_.guid, agency_ein)) event_kwargs = { 'request_id': user_.anonymous_request.id if user_.is_anonymous_requester else None, 'response_id': None, 'user_guid': current_user.guid, 'timestamp': datetime.utcnow() } if is_super: create_object( Events(type_=event_type.USER_MADE_SUPER_USER, previous_value=old_status, new_value=new_status, **event_kwargs)) else: create_object( Events(type_=event_type.USER_REMOVED_FROM_SUPER, previous_value=old_status, new_value=new_status, **event_kwargs)) return jsonify({ 'status': 'success', 'message': 'Agency user successfully updated' }), 200 # Always returns 200 so that we can use the data from the response in the client side javascript return jsonify({ 'status': 'Not Modified', 'message': 'No changes detected' }), 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