def edit_request_info(): """ Edits the title and agency request summary of a FOIL request through an API PUT method. Retrieves updated edited content from AJAX call on view_request page and stores changes into database. :return: JSON Response with updated content: either request title or agency request summary) """ edit_request = flask_request.form request_id = flask_request.form.get('pk') current_request = Requests.query.filter_by(id=request_id).first() previous_value = {} new_value = {} type_ = '' val = edit_request['value'].strip() if edit_request['name'] == 'title': previous_value['title'] = current_request.title new_value['title'] = val type_ = event_type.REQ_TITLE_EDITED elif edit_request['name'] == 'agency_request_summary': previous_value['agency_request_summary'] = current_request.agency_request_summary new_value['agency_request_summary'] = val type_ = event_type.REQ_AGENCY_REQ_SUM_EDITED update_object({edit_request['name']: val if val else None}, Requests, current_request.id) create_request_info_event(request_id, type_, previous_value, new_value) return jsonify(edit_request), 200
def edit_privacy(): """ Edits the privacy privacy options of a request's title and agency description. Retrieves updated privacy options from AJAX call on view_request page and stores changes into database. :return: JSON Response with updated title and agency description privacy options """ request_id = flask_request.form.get('id') current_request = Requests.query.filter_by(id=request_id).first() privacy = {} previous_value = {} new_value = {} title = flask_request.form.get('title') agency_request_summary = flask_request.form.get('agency_request_summary') type_ = '' if title is not None: privacy['title'] = title == 'true' previous_value['privacy'] = current_request.privacy['title'] new_value['privacy'] = privacy['title'] type_ = event_type.REQ_TITLE_PRIVACY_EDITED elif agency_request_summary is not None: privacy['agency_request_summary'] = agency_request_summary == 'true' previous_value['privacy'] = current_request.privacy[ 'agency_request_summary'] new_value['privacy'] = privacy['agency_request_summary'] type_ = event_type.REQ_AGENCY_REQ_SUM_PRIVACY_EDITED update_object({'privacy': privacy}, Requests, current_request.id) create_request_info_event(request_id, type_, previous_value, new_value) return jsonify(privacy), 200
def saml_slo(saml_sp): """Generate a SAML LogoutRequest for the user. Args: saml_sp (OneLogin_Saml2_Auth): SAML SP Instance Returns: Response Object: Redirect the user to the IdP for SLO. """ name_id = None session_index = None if 'samlNameId' in session: name_id = session['samlNameId'] if 'samlSessionIndex' in session: session_index = session['samlSessionIndex'] update_object( { 'session_id': None }, Users, current_user.guid ) return saml_sp.logout(name_id=name_id, session_index=session_index)
def edit_request_info(): """ Edits the title and agency description of a FOIL request through an API PUT method. Retrieves updated edited content from AJAX call on view_request page and stores changes into database. :return: JSON Response with updated content: either request title or agency description) """ edit_request = flask_request.form request_id = flask_request.form.get('pk') current_request = Requests.query.filter_by(id=request_id).first() previous_value = {} new_value = {} type_ = '' val = edit_request['value'].strip() if edit_request['name'] == 'title': previous_value['title'] = current_request.title new_value['title'] = val type_ = event_type.REQ_TITLE_EDITED elif edit_request['name'] == 'agency_request_summary': previous_value[ 'agency_request_summary'] = current_request.agency_request_summary new_value['agency_request_summary'] = val type_ = event_type.REQ_AGENCY_REQ_SUM_EDITED update_object({edit_request['name']: val if val else None}, Requests, current_request.id) create_request_info_event(request_id, type_, previous_value, new_value) return jsonify(edit_request), 200
def edit_privacy(): """ Edits the privacy privacy options of a request's title and agency request summary. Retrieves updated privacy options from AJAX call on view_request page and stores changes into database. :return: JSON Response with updated title and agency request summary privacy options """ request_id = flask_request.form.get('id') current_request = Requests.query.filter_by(id=request_id).first() privacy = {} previous_value = {} new_value = {} title = flask_request.form.get('title') agency_request_summary = flask_request.form.get('agency_request_summary') type_ = '' if title is not None: privacy['title'] = title == 'true' previous_value['privacy'] = current_request.privacy['title'] new_value['privacy'] = privacy['title'] type_ = event_type.REQ_TITLE_PRIVACY_EDITED elif agency_request_summary is not None: privacy['agency_request_summary'] = agency_request_summary == 'true' previous_value['privacy'] = current_request.privacy['agency_request_summary'] new_value['privacy'] = privacy['agency_request_summary'] type_ = event_type.REQ_AGENCY_REQ_SUM_PRIVACY_EDITED update_object({'privacy': privacy}, Requests, current_request.id) create_request_info_event(request_id, type_, previous_value, new_value) return jsonify(privacy), 200
def test_json_update(self): self.assertEqual(self.rf.request.privacy, { 'title': False, 'agency_request_summary': True }) # check single value change with patch('app.models.Requests.es_update'): update_object({'privacy': { 'title': True }}, Requests, self.request_id) req = self.refetch_request() self.assertEqual(req.privacy, { 'title': True, 'agency_request_summary': True }) # check multiple value changes with patch('app.models.Requests.es_update'): update_object( {'privacy': { 'title': False, 'agency_request_summary': False }}, Requests, self.request_id) req = self.refetch_request() self.assertEqual(req.privacy, { 'title': False, 'agency_request_summary': False })
def index(): fresh_login = request.args.get('fresh_login', False) if current_user.is_authenticated and fresh_login: if current_user.session_id is not None: return render_template('main/home.html', duplicate_session=True) update_object({'session_id': session.sid_s}, Users, (current_user.guid, current_user.auth_user_type)) return render_template('main/home.html')
def set_point_of_contact(request_id, user_request, point_of_contact): """ Toggles point of contact of a given request :param request_id: FOIL request ID :param user_request: UserRequest row to be changed :param point_of_contact: boolean flag for point of contact """ update_object({"point_of_contact": point_of_contact}, UserRequests, (user_request.user_guid, request_id))
def set_point_of_contact(request_id, user_request, point_of_contact): """ Toggles point of contact of a given request :param request_id: FOIL request ID :param user_request: UserRequest row to be changed :param point_of_contact: boolean flag for point of contact """ update_object({"point_of_contact": point_of_contact}, UserRequests, (user_request.user_guid, request_id) )
def test_multifield_update(self): new_title = "this title is betta, is fasta, is stronga" new_description = "it's the batman" with patch('app.models.Requests.es_update'): update_object({ 'title': new_title, 'description': new_description }, Requests, self.request_id) req = self.refetch_request() self.assertEqual([req.title, req.description], [new_title, new_description])
def test_es_update(self, es_update_patch): role = Roles.query.first() # check called for model with 'es_update' (Requests) update_object({'title': 'new and improved TITLE X50 DELUXE'}, Requests, self.request_id) es_update_patch.assert_called_once_with() # check not called for model without 'es_update' (Roles) try: update_object({'name': 'nombre'}, Roles, role.id) except AttributeError: self.fail('es_update() called when it should not have been.')
def index(): fresh_login = request.args.get('fresh_login', False) if current_user.is_authenticated and fresh_login: if current_user.session_id is not None: return render_template('main/home.html', duplicate_session=True) update_object( { 'session_id': session.sid_s }, Users, current_user.guid ) return render_template('main/home.html')
def fix_due_dates(): # for "America/New_York" """ Forgot to set due date hour to 5:00 PM in migration script before converting to utc. Besides having the incorrect time, this also means certain due dates do not fall on business days. """ from app.lib.db_utils import update_object for request in Requests.query.all(): update_object( { "due_date": request.due_date.replace( hour=22, minute=00, second=00, microsecond=00) }, Requests, request.id)
def fix_anonymous_requesters(): """ Ensures there is only one anonymous requester per request by creating a new anonymous requester (User) for every User Requests record with a duplicate anonymous requester guid and updates the User Requests record. The new user will be identical to the existing one with the exception of the guid. """ from app.constants import user_type_request from app.request.utils import generate_guid from app.lib.db_utils import create_object, update_object guids = db.engine.execute(""" SELECT user_requests.user_guid AS "GUID" FROM user_requests JOIN users ON user_requests.user_guid = users.guid AND user_requests.auth_user_type = users.auth_user_type WHERE user_requests.request_user_type = 'requester' GROUP BY user_requests.user_guid HAVING COUNT(user_requests.request_id) > 1; """) for guid, in guids: # get all User Requests with dups, excluding the first (since we need to change all but 1) for ur in UserRequests.query.filter_by( user_guid=guid, request_user_type=user_type_request.REQUESTER).offset(1): user = Users.query.filter_by( guid=guid, auth_user_type=user_type_auth.ANONYMOUS_USER).one() new_guid = generate_guid() print("{} -> {}".format(guid, new_guid)) # create new anonymous requester with new guid create_object( Users(guid=new_guid, auth_user_type=user_type_auth.ANONYMOUS_USER, email=user.email, first_name=user.first_name, last_name=user.last_name, title=user.title, organization=user.organization, email_validated=False, terms_of_use_accepted=False, phone_number=user.phone_number, fax_number=user.fax_number, mailing_address=user.mailing_address)) # update user request with new guid update_object({"user_guid": new_guid}, UserRequests, (ur.user_guid, ur.auth_user_type, ur.request_id))
def oauth_logout(): timed_out = eval_request_bool(request.args.get('timeout')) forced_logout = eval_request_bool(request.args.get('forced_logout')) if forced_logout: user_session = redis_get_user_session(current_user.session_id) if user_session is not None: user_session.destroy() if timed_out: flash("Your session timed out. Please login again", category='info') if 'token' in session: revoke_and_remove_access_token() if current_user.is_anonymous: return redirect(url_for("main.index")) update_object({'session_id': None}, Users, (current_user.guid, current_user.auth_user_type)) logout_user() session.destroy() if forced_logout: return redirect(url_for("auth.login")) return redirect(url_for("main.index"))
def generate_request_id(agency_ein): """ Generates an agency-specific FOIL request id. :param agency_ein: agency_ein ein used to generate the request_id :return: generated FOIL Request ID (FOIL - year - agency ein - 5 digits for request number) """ if agency_ein: agency = Agencies.query.filter_by(ein=agency_ein).one( ) # This is the actual agency (including sub-agencies) parent_ein = _get_parent_ein(agency.parent_ein) next_request_number = Agencies.query.filter_by(ein=parent_ein).one( ).next_request_number # Parent agencies handle the request counting, not sub-agencies update_object({'next_request_number': next_request_number + 1}, Agencies, parent_ein) agency_ein = agency.parent_ein request_id = "FOIL-{0:s}-{1!s}-{2:05d}".format( datetime.utcnow().strftime("%Y"), agency_ein, int(next_request_number)) return request_id return None
def update_openrecords_user(form): """ Update OpenRecords-specific user attributes. :param form: validated ManageUserAccountForm or ManageAgencyUserAccountForm :type form: app.auth.forms.ManageUserAccountForm or app.auth.forms.ManageAgencyUserAccountForm """ update_object( { 'title': form.title.data, 'organization': form.organization.data, 'notification_email': form.notification_email.data, 'phone_number': form.phone_number.data, 'fax_number': form.fax_number.data, 'mailing_address': create_mailing_address(form.address_one.data or None, form.city.data or None, form.state.data or None, form.zipcode.data or None, form.address_two.data or None) }, Users, (current_user.guid, current_user.auth_user_type)) if current_user.is_agency and current_user.default_agency_ein != form.default_agency.data: update_object({'is_primary_agency': False}, AgencyUsers, (current_user.guid, current_user.auth_user_type, current_user.default_agency_ein)) update_object({'is_primary_agency': True}, AgencyUsers, (current_user.guid, current_user.auth_user_type, form.default_agency.data))
def _update_user_data(user, guid, user_type, email, first_name, middle_initial, last_name): """ Update specified user with the information provided, which is assumed to have originated from an NYC Service Account, and set `email_validated` and `terms_of_use_accepted` (this function should be called AFTER email validation and terms-of-use acceptance has been completed). Update any database objects this user is associated with. - user_requests - events In order to prevent a possbile negative performance impact (due to foreign keys CASCADE), guid and user_type are compared with stored user attributes and are excluded from the update if both are identical. """ updated_data = { 'email': email, 'first_name': first_name, 'middle_initial': middle_initial, 'last_name': last_name, 'email_validated': True, 'terms_of_use_accepted': True, } if guid != user.guid or user_type != user.auth_user_type: updated_data.update(guid=guid, auth_user_type=user_type) update_events_values = Events.query.filter( Events.new_value['user_guid'].astext == user.guid, Events.new_value['auth_user_type'].astext == user.auth_user_type).all() for event in update_events_values: update_object( { 'new_value': { 'user_guid': guid, 'auth_user_type': user_type } }, Events, event.id) update_object(updated_data, Users, (user.guid, user.auth_user_type))
def patch(agency_ein): """ Only accessible by Super Users Currently only changes: is_active """ if not current_user.is_anonymous and current_user.is_super: is_active = request.form.get('is_active') if is_active is not None and Agencies.query.filter_by( ein=agency_ein).first() is not None: update_object({'is_active': eval_request_bool(is_active)}, Agencies, agency_ein) create_object( Events(request_id=None, user_guid=current_user.guid, auth_user_type=current_user.auth_user_type, type_=AGENCY_ACTIVATED, new_value={"ein": agency_ein}, timestamp=datetime.utcnow())) return '', 200 return '', 400 return '', 403
def generate_request_id(agency_ein): """ Generates an agency-specific FOIL request id. :param agency_ein: agency_ein ein used to generate the request_id :return: generated FOIL Request ID (FOIL - year - agency ein - 5 digits for request number) """ if agency_ein: agency = Agencies.query.filter_by( ein=agency_ein).one() # This is the actual agency (including sub-agencies) next_request_number = Agencies.query.filter_by( ein=agency.formatted_parent_ein).one().next_request_number # Parent agencies handle the request counting, not sub-agencies agency_ein = agency.parent_ein request_id = "FOIL-{0:s}-{1!s}-{2:05d}".format( datetime.utcnow().strftime("%Y"), agency_ein, int(next_request_number)) if Requests.query.filter_by(id=request_id).one_or_none(): update_object( {'next_request_number': next_request_number + 1}, Agencies, agency.ein ) request_id = generate_request_id(agency.ein) return request_id return None
def update_user(guid=None, auth_user_type=None, **kwargs): """ Updates a user if they exist in the database. :param guid: :param kwargs: Fields that need to be updated in the user. :return: GUID + UserType of the user (forms unique ID) """ user = str() if not guid: return None user = update_object(kwargs, Users, obj_id=(guid, auth_user_type)) if not user: return None return user
def edit_privacy(): """ Edits the privacy privacy options of a request's title and agency description. Retrieves updated privacy options from AJAX call on view_request page and stores changes into database. :return: JSON Response with updated title and agency description privacy options """ request_id = flask_request.form.get('id') current_request = Requests.query.filter_by(id=request_id).first() privacy = {} previous_value = {} new_value = {} title = flask_request.form.get('title') agency_desc = flask_request.form.get('agency_desc') type_ = '' if title is not None: privacy['title'] = title == 'true' previous_value['privacy'] = current_request.privacy['title'] new_value['privacy'] = title type_ = event_type.REQ_TITLE_PRIVACY_EDITED elif agency_desc is not None: privacy['agency_description'] = agency_desc == 'true' previous_value['privacy'] = current_request.privacy[ 'agency_description'] new_value['privacy'] = agency_desc type_ = event_type.REQ_AGENCY_DESC_PRIVACY_EDITED if not privacy['agency_description']: # if current_request.agency_description_release_date: previous_value['release_date'] = None release_date = calendar.addbusdays(datetime.utcnow(), offset=20) update_object( { 'privacy': privacy, 'agency_description_release_date': release_date }, Requests, current_request.id) new_value['release_date'] = release_date.isoformat() else: previous_value[ 'release_date'] = current_request.agency_description_release_date.isoformat( ) update_object( { 'privacy': privacy, 'agency_description_release_date': None }, Requests, current_request.id) new_value['release_date'] = None create_edit_event(request_id, type_, previous_value, new_value) return jsonify(privacy), 200 update_object({'privacy': privacy}, Requests, current_request.id) create_edit_event(request_id, type_, previous_value, new_value) return jsonify(privacy), 200
def update_openrecords_user(form): """ Update OpenRecords-specific user attributes. Args: form (app.auth.forms.ManageAgencyUserAccountForm OR app.auth.forms.ManageUserAccountForm): validated form """ update_object( { 'title': form.title.data, 'organization': form.organization.data, 'notification_email': form.notification_email.data, 'phone_number': form.phone_number.data, 'fax_number': form.fax_number.data, '_mailing_address': create_mailing_address( form.address_one.data or None, form.city.data or None, form.state.data or None, form.zipcode.data or None, form.address_two.data or None) }, Users, current_user.guid ) if current_user.is_agency and current_user.default_agency_ein != form.default_agency.data: update_object( {'is_primary_agency': False}, AgencyUsers, (current_user.guid, current_user.default_agency_ein) ) update_object( {'is_primary_agency': True}, AgencyUsers, (current_user.guid, form.default_agency.data) )
def update_agency_active_status(agency_ein, is_active): """ Update the active status of an agency. :param agency_ein: String identifier for agency (4 characters) :param is_active: Boolean value for agency active status (True = Active) :return: Boolean value (True if successfully changed active status) """ agency = Agencies.query.filter_by(ein=agency_ein).first() is_valid_agency = agency is not None activate_agency = eval_request_bool(is_active) if is_active is not None and is_valid_agency: update_object({'is_active': activate_agency}, Agencies, agency_ein) if activate_agency: create_object( Events(request_id=None, user_guid=current_user.guid, type_=AGENCY_ACTIVATED, previous_value={ "ein": agency_ein, "is_active": "False" }, new_value={ "ein": agency_ein, "is_active": "True" }, timestamp=datetime.utcnow())) # create request documents for request in agency.requests: request.es_create() return True else: create_object( Events(request_id=None, user_guid=current_user.guid, type_=AGENCY_DEACTIVATED, previous_value={ "ein": agency_ein, "is_active": "True" }, new_value={ "ein": agency_ein, "is_active": "False" }, timestamp=datetime.utcnow())) # remove requests from index for request in agency.requests: request.es_delete() # deactivate agency users for user in agency.active_users: update_object( { "is_agency_active": "False", "is_agency_admin": "False" }, AgencyUsers, (user.guid, agency_ein)) create_object( Events(request_id=None, user_guid=current_user.guid, type_=AGENCY_USER_DEACTIVATED, previous_value={ "user_guid": user.guid, "ein": agency_ein, "is_active": "True" }, new_value={ "user_guid": user.guid, "ein": agency_ein, "is_active": "False" }, timestamp=datetime.utcnow())) return True return False
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 update_agency_active_status(agency_ein, is_active): """ Update the active status of an agency. :param agency_ein: String identifier for agency (4 characters) :param is_active: Boolean value for agency active status (True = Active) :return: Boolean value (True if successfully changed active status) """ agency = Agencies.query.filter_by(ein=agency_ein).first() is_valid_agency = agency is not None activate_agency = eval_request_bool(is_active) if is_active is not None and is_valid_agency: update_object( {'is_active': activate_agency}, Agencies, agency_ein ) if activate_agency: create_object( Events( request_id=None, user_guid=current_user.guid, type_=AGENCY_ACTIVATED, previous_value={"ein": agency_ein, "is_active": "False"}, new_value={"ein": agency_ein, "is_active": "True"}, timestamp=datetime.utcnow() ) ) # create request documents for request in agency.requests: request.es_create() return True else: create_object( Events( request_id=None, user_guid=current_user.guid, type_=AGENCY_DEACTIVATED, previous_value={"ein": agency_ein, "is_active": "True"}, new_value={"ein": agency_ein, "is_active": "False"}, timestamp=datetime.utcnow() ) ) # remove requests from index for request in agency.requests: request.es_delete() # deactivate agency users for user in agency.active_users: update_object( {"is_agency_active": "False", "is_agency_admin": "False"}, AgencyUsers, (user.guid, agency_ein) ) create_object( Events( request_id=None, user_guid=current_user.guid, type_=AGENCY_USER_DEACTIVATED, previous_value={"user_guid": user.guid, "ein": agency_ein, "is_active": "True"}, new_value={"user_guid": user.guid, "ein": agency_ein, "is_active": "False"}, timestamp=datetime.utcnow() ) ) return True return False
def _update_user_data( user, guid, email, first_name=None, middle_initial=None, last_name=None, email_validated=False, is_nyc_employee=False, has_nyc_account=False, active=False, terms_of_use_accepted=False, is_anonymous_requester=False ): """ Update a users data with the updated values. Args: user (Users): User to be updated. guid (str): Updated GUID (Required) email (str): Updated email address first_name (str): Updated first name middle_initial (str): Updated middle initial last_name (str): Updated last name email_validated (bool): Updated email validation status is_nyc_employee (bool): Updated NYC Employee status has_nyc_account (bool): Updated NYC Account Status active (bool): Updated active status (is the user account active in the authentication provider) terms_of_use_accepted (bool): Terms of Use Accepted Status is_anonymous_requester (bool): Updated Anonymous Requester status """ current_data = user.val_for_events updated_data = { 'guid': guid, 'first_name': first_name, 'middle_initial': middle_initial, 'last_name': last_name, 'email': email, 'email_validated': email_validated, 'is_nyc_employee': is_nyc_employee, 'has_nyc_account': has_nyc_account, 'active': active, 'terms_of_use_accepted': terms_of_use_accepted, 'is_anonymous_requester': is_anonymous_requester } updates = {} for k, v in current_data.items(): _temp_update = updated_data.get(k, None) if _temp_update and _temp_update != v: updates[k] = updated_data.get(k) if updates: # Get all the events that need to be changed to associate with the updated GUID. events_to_update = Events.query.filter(Events.new_value['user_guid'].astext == user.guid).all() # Staging area for update events updated_events = [] # Loop through the events and create a dictionary that contains the updated GUID in the proper keys for event in events_to_update: update = EventsDict( id=event.id, request_id=event.request_id, user_guid=event.user_guid, response_id=event.response_id, type=event.type, timestamp=event.timestamp, previous_value=event.previous_value, new_value=event.new_value ) update['new_value']['user_guid'] = guid updated_events.append(('new_value', update['new_value'])) update_object( updates, Users, user.guid ) if updated_events: Events.query.filter(Events.new_value['user_guid'].astext == current_data['guid']).update(updated_events, synchronize_session=False) UserRequests.query.filter(UserRequests.user_guid == current_data['guid']).update([('user_guid', guid)]) request_ids = [ur.request_id for ur in UserRequests.query.with_entities(UserRequests.request_id).filter( UserRequests.user_guid == guid).all()] es_update_assigned_users.apply_async(args=[request_ids])
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 """ 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 update_request_statuses(): """ Update statuses for all requests that are now Due Soon or Overdue and send a notification email to agency admins listing the requests. """ with scheduler.app.app_context(): now = datetime.utcnow() due_soon_date = calendar.addbusdays( now, current_app.config['DUE_SOON_DAYS_THRESHOLD']).replace( hour=23, minute=59, second=59) # the entire day requests_overdue = Requests.query.filter( Requests.due_date < now, Requests.status != request_status.CLOSED).order_by( Requests.due_date.asc()).all() requests_due_soon = Requests.query.filter( Requests.due_date > now, Requests.due_date <= due_soon_date, Requests.status != request_status.CLOSED).order_by( Requests.due_date.asc()).all() agencies_to_requests_overdue = {} agencies_to_acknowledgments_overdue = {} agencies_to_requests_due_soon = {} agencies_to_acknowledgments_due_soon = {} def add_to_agencies_to_request_dict(req, agencies_to_request_dict): if req.agency.ein not in agencies_to_request_dict: agencies_to_request_dict[req.agency.ein] = [req] else: agencies_to_request_dict[req.agency.ein].append(req) # OVERDUE for request in requests_overdue: if request.was_acknowledged: add_to_agencies_to_request_dict(request, agencies_to_requests_overdue) else: add_to_agencies_to_request_dict( request, agencies_to_acknowledgments_overdue) if request.status != request_status.OVERDUE: update_object({"status": request_status.OVERDUE}, Requests, request.id) # DUE SOON for request in requests_due_soon: if request.was_acknowledged: add_to_agencies_to_request_dict(request, agencies_to_requests_due_soon) else: add_to_agencies_to_request_dict( request, agencies_to_acknowledgments_due_soon) if request.status != request_status.DUE_SOON: update_object({"status": request_status.DUE_SOON}, Requests, request.id) # get all possible agencies to email agency_eins = set( list(agencies_to_requests_overdue) + list(agencies_to_acknowledgments_overdue) + list(agencies_to_requests_due_soon) + list(agencies_to_acknowledgments_due_soon)) # mail to agency admins for each agency for agency_ein in agency_eins: agency_requests_overdue = agencies_to_requests_overdue.get( agency_ein, []) agency_acknowledgments_overdue = agencies_to_acknowledgments_overdue.get( agency_ein, []) agency_requests_due_soon = agencies_to_requests_due_soon.get( agency_ein, []) agency_acknowledgments_due_soon = agencies_to_acknowledgments_due_soon.get( agency_ein, []) user_emails = list( set(admin.notification_email or admin.email for admin in Agencies.query.filter_by( ein=agency_ein).one().administrators)) send_email( STATUSES_EMAIL_SUBJECT, to=user_emails, template=STATUSES_EMAIL_TEMPLATE, requests_overdue=agency_requests_overdue, acknowledgments_overdue=agency_acknowledgments_overdue, requests_due_soon=agency_requests_due_soon, acknowledgments_due_soon=agency_acknowledgments_due_soon) email = Emails( request.id, PRIVATE, to=','.join(user_emails), cc=None, bcc=None, subject=STATUSES_EMAIL_SUBJECT, body=render_template( STATUSES_EMAIL_TEMPLATE + ".html", requests_overdue=agency_requests_overdue, acknowledgments_overdue=agency_acknowledgments_overdue, requests_due_soon=agency_requests_due_soon, acknowledgments_due_soon=agency_acknowledgments_due_soon)) create_object(email) create_object( Events(request.id, user_guid=None, auth_user_type=None, type_=EMAIL_NOTIFICATION_SENT, previous_value=None, new_value=email.val_for_events, response_id=None, timestamp=datetime.utcnow()))
def _update_request_statuses(): """ Update statuses for all requests that are now Due Soon or Overdue and send a notification email to agency admins listing the requests. """ now = datetime.utcnow() due_soon_date = calendar.addbusdays( now, current_app.config['DUE_SOON_DAYS_THRESHOLD'] ).replace(hour=23, minute=59, second=59) # the entire day agencies = Agencies.query.with_entities(Agencies.ein).filter_by(is_active=True).all() for agency_ein, in agencies: requests_overdue = Requests.query.filter( Requests.due_date < now, Requests.status != request_status.CLOSED, Requests.agency_ein == agency_ein ).order_by( Requests.due_date.asc() ).all() requests_due_soon = Requests.query.filter( Requests.due_date > now, Requests.due_date <= due_soon_date, Requests.status != request_status.CLOSED, Requests.agency_ein == agency_ein ).order_by( Requests.due_date.asc() ).all() if not requests_overdue and not requests_due_soon: continue agency_requests_overdue = [] agency_acknowledgments_overdue = [] agency_requests_due_soon = [] agency_acknowledgments_due_soon = [] # OVERDUE for request in requests_overdue: if request.was_acknowledged: agency_requests_overdue.append(request) else: agency_acknowledgments_overdue.append(request) if request.status != request_status.OVERDUE: create_object( Events( request.id, user_guid=None, type_=REQ_STATUS_CHANGED, previous_value={"status": request.status}, new_value={"status": request_status.OVERDUE}, response_id=None, ) ) update_object( {"status": request_status.OVERDUE}, Requests, request.id) # DUE SOON for request in requests_due_soon: if request.was_acknowledged: agency_requests_due_soon.append(request) else: agency_acknowledgments_due_soon.append(request) if request.status != request_status.DUE_SOON: create_object( Events( request.id, user_guid=None, type_=REQ_STATUS_CHANGED, previous_value={"status": request.status}, new_value={"status": request_status.DUE_SOON}, response_id=None, ) ) update_object( {"status": request_status.DUE_SOON}, Requests, request.id) # mail to agency admins for each agency user_emails = list(set(admin.notification_email or admin.email for admin in Agencies.query.filter_by(ein=agency_ein).one().administrators)) send_email( STATUSES_EMAIL_SUBJECT, to=user_emails, template=STATUSES_EMAIL_TEMPLATE, requests_overdue=agency_requests_overdue, acknowledgments_overdue=agency_acknowledgments_overdue, requests_due_soon=agency_requests_due_soon, acknowledgments_due_soon=agency_acknowledgments_due_soon ) email = Emails( request.id, PRIVATE, to=','.join(user_emails), cc=None, bcc=None, subject=STATUSES_EMAIL_SUBJECT, body=render_template( STATUSES_EMAIL_TEMPLATE + ".html", requests_overdue=agency_requests_overdue, acknowledgments_overdue=agency_acknowledgments_overdue, requests_due_soon=agency_requests_due_soon, acknowledgments_due_soon=agency_acknowledgments_due_soon ) ) create_object(email) create_object( Events( request.id, user_guid=None, type_=EMAIL_NOTIFICATION_SENT, previous_value=None, new_value=email.val_for_events, response_id=None, timestamp=datetime.utcnow() ) )
def __update(self, data): update_object(data, Requests, self.request.id)
def _update_request_statuses(): """ Update statuses for all requests that are now Due Soon or Overdue and send a notification email to agency admins listing the requests. """ now = datetime.utcnow() due_soon_date = calendar.addbusdays( now, current_app.config['DUE_SOON_DAYS_THRESHOLD']).replace( hour=23, minute=59, second=59) # the entire day agencies = Agencies.query.with_entities( Agencies.ein).filter_by(is_active=True).all() for agency_ein, in agencies: requests_overdue = Requests.query.filter( Requests.due_date < now, Requests.status != request_status.CLOSED, Requests.agency_ein == agency_ein).order_by( Requests.due_date.asc()).all() requests_due_soon = Requests.query.filter( Requests.due_date > now, Requests.due_date <= due_soon_date, Requests.status != request_status.CLOSED, Requests.agency_ein == agency_ein).order_by( Requests.due_date.asc()).all() if not requests_overdue and not requests_due_soon: continue agency_requests_overdue = [] agency_acknowledgments_overdue = [] agency_requests_due_soon = [] agency_acknowledgments_due_soon = [] # OVERDUE for request in requests_overdue: if request.was_acknowledged: agency_requests_overdue.append(request) else: agency_acknowledgments_overdue.append(request) if request.status != request_status.OVERDUE: create_object( Events( request.id, user_guid=None, auth_user_type=None, type_=REQ_STATUS_CHANGED, previous_value={"status": request.status}, new_value={"status": request_status.OVERDUE}, response_id=None, )) update_object({"status": request_status.OVERDUE}, Requests, request.id) # DUE SOON for request in requests_due_soon: if request.was_acknowledged: agency_requests_due_soon.append(request) else: agency_acknowledgments_due_soon.append(request) if request.status != request_status.DUE_SOON: create_object( Events( request.id, user_guid=None, auth_user_type=None, type_=REQ_STATUS_CHANGED, previous_value={"status": request.status}, new_value={"status": request_status.DUE_SOON}, response_id=None, )) update_object({"status": request_status.DUE_SOON}, Requests, request.id) # mail to agency admins for each agency user_emails = list( set(admin.notification_email or admin.email for admin in Agencies.query.filter_by( ein=agency_ein).one().administrators)) send_email(STATUSES_EMAIL_SUBJECT, to=user_emails, template=STATUSES_EMAIL_TEMPLATE, requests_overdue=agency_requests_overdue, acknowledgments_overdue=agency_acknowledgments_overdue, requests_due_soon=agency_requests_due_soon, acknowledgments_due_soon=agency_acknowledgments_due_soon) email = Emails( request.id, PRIVATE, to=','.join(user_emails), cc=None, bcc=None, subject=STATUSES_EMAIL_SUBJECT, body=render_template( STATUSES_EMAIL_TEMPLATE + ".html", requests_overdue=agency_requests_overdue, acknowledgments_overdue=agency_acknowledgments_overdue, requests_due_soon=agency_requests_due_soon, acknowledgments_due_soon=agency_acknowledgments_due_soon)) create_object(email) create_object( Events(request.id, user_guid=None, auth_user_type=None, type_=EMAIL_NOTIFICATION_SENT, previous_value=None, new_value=email.val_for_events, response_id=None, timestamp=datetime.utcnow()))