def build_search_results( username: str, password: str, aliases: Tuple[Alias, ...], search_cache: LRUCache) -> Tuple[List[OeciCase], List[str]]: errors = [] search_results: List[OeciCase] = [] alias_match = search_cache[aliases] if alias_match: return alias_match else: for alias in aliases: session = requests.Session() try: login_response = Crawler.attempt_login( session, username, password) alias_search_result = Crawler.search( session, login_response, alias.first_name, alias.last_name, alias.middle_name, alias.birth_date, ) search_results += alias_search_result except InvalidOECIUsernamePassword as e: error(401, str(e)) except OECIUnavailable as e: error(404, str(e)) except Exception as e: errors.append(str(e)) finally: session.close() if not errors: search_cache[aliases] = search_results, errors return search_results, errors
def post(self): data = request.get_json() if data is None: error(400, "No json data in request body") check_data_fields(data, ["email", "password"]) user_db_result = user_db_util.read( g.database, user_db_util.identify_by_email(g.database, data["email"])) if not user_db_result or not check_password_hash( user_db_result["hashed_password"], data["password"]): error(401, "Invalid username or password") user = from_dict(data_class=User, data=user_db_result) User.login_user(user) response = make_response() if user.admin: response.set_cookie( "is_admin", expires=time.time() + 365 * 24 * 60 * 60, # type: ignore # 1 year lifetime matches flask login cookie ) return response, 200
def post(self): """ Attempts to log in to the OECI web site using the provided username and password if successful, encrypt those credentials and return them in a cookie. If the credentials """ data = request.get_json() if data is None: error(400, "No json data in request body") check_data_fields(data, ["oeci_username", "oeci_password"]) credentials = {"oeci_username": data["oeci_username"], "oeci_password": data["oeci_password"]} cipher = DataCipher(key=current_app.config.get("SECRET_KEY")) encrypted_credentials = cipher.encrypt(credentials) response = make_response() # TODO: We will need an OECILogout endpoint to remove httponly=true cookies from frontend response.set_cookie( "oeci_token", secure=os.getenv("TIER") == "production", httponly=False, samesite="strict", expires=time.time() + 15 * 60, # 15 minutes value=encrypted_credentials, ) return response, 201
def get(self, user_id): """ Fetch a single user's data if a user_id is specified. Otherwise fetch the list of all users. Returned info contains user_id, name, group name,email, admin status, and date_created. """ if user_id: return get_from_user_id(user_id) else: # No user_id given; this is a GET all users request. if not current_user.is_admin: error(403, "Logged in user not admin ") user_db_data = user_db_util.fetchall(g.database) response_data: Dict[str, List[Dict[str, str]]] = {"users": []} for user_entry in user_db_data: response_data["users"].append({ "id": user_entry["user_id"], "email": user_entry["email"], "name": user_entry["name"], "group": user_entry["group_name"], "admin": user_entry["admin"], "timestamp": user_entry["date_created"], }) return jsonify(response_data), 201
def post(self): request_data = request.get_json() if request_data is None or not request_data.get("aliases"): error(400, "No json data in request body") check_data_fields(request_data, ["aliases"]) for alias in request_data["aliases"]: check_data_fields( alias, ["first_name", "last_name", "middle_name", "birth_date"]) cipher = DataCipher(key=current_app.config.get("SECRET_KEY")) if not "oeci_token" in request.cookies.keys(): error(401, "Missing login credentials to OECI.") decrypted_credentials = cipher.decrypt(request.cookies["oeci_token"]) username, password = decrypted_credentials[ "oeci_username"], decrypted_credentials["oeci_password"] record, ambiguous_record, questions = RecordCreator.build_record( username, password, request_data["aliases"]) if questions: session["ambiguous_record"] = pickle.dumps(ambiguous_record) try: save_result(request_data, record) except Exception as ex: logging.error("Saving search result failed with exception: %s" % ex, stack_info=True) record_summary = RecordSummarizer.summarize(record, questions) response_data = {"data": {"record": record_summary}} encoded_response = json.dumps(response_data, cls=ExpungeModelEncoder) return encoded_response
def _oeci_login_params(request): cipher = DataCipher(key=current_app.config.get("SECRET_KEY")) if not "oeci_token" in request.cookies.keys(): error(401, "Missing login credentials to OECI.") decrypted_credentials = cipher.decrypt(request.cookies["oeci_token"]) return decrypted_credentials["oeci_username"], decrypted_credentials[ "oeci_password"]
def post(self): ambiguous_record_data = session.get("ambiguous_record") if ambiguous_record_data: ambiguous_record = pickle.loads(ambiguous_record_data) request_data = request.get_json() questions = request_data.get("questions") return Disambiguate.build_response(ambiguous_record, questions) else: error( 428, "Must hit the search endpoint with question generating records first." )
def build_record( username: str, password: str, aliases: List[Dict[str, str]] ) -> Tuple[Record, AmbiguousRecord, Dict[str, Question]]: ambiguous_cases_accumulator: List[AmbiguousCase] = [] questions_accumulator: List[Question] = [] errors = [] for alias in aliases: crawler = Crawler() login_result = crawler.login(username, password, close_session=False) if login_result is False: error(401, "Attempted login to OECI failed") try: search_result = crawler.search( alias["first_name"], alias["last_name"], alias["middle_name"], alias["birth_date"], ) ambiguous_cases, questions = search_result ambiguous_cases_accumulator += ambiguous_cases questions_accumulator += questions except Exception as e: errors.append(str(e)) if errors: record = Record((), tuple(errors)) ambiguous_record = [record] return record, ambiguous_record, {} else: ambiguous_record: AmbiguousRecord = [] # type: ignore for cases in product(*ambiguous_cases_accumulator): cases_with_unique_case_number: List[Case] = [ list(group)[0] for key, group in groupby( sorted(list(cases), key=lambda case: case.case_number), lambda case: case.case_number) ] ambiguous_record.append( Record(tuple(cases_with_unique_case_number))) record = RecordCreator.analyze_ambiguous_record(ambiguous_record) questions_as_dict = dict( list( map(lambda q: (q.ambiguous_charge_id, q), questions_accumulator))) return record, ambiguous_record, questions_as_dict
def get_from_user_id(user_id): user_db_data = user_db_util.read(g.database, user_id) if not user_db_data: error(404, "User id not recognized") if not current_user.is_admin and current_user.user_id != user_id: error(403, "Logged in user not admin and doesn't match requested user id.") response_data = { "user_id": user_db_data["user_id"], "email": user_db_data["email"], "name": user_db_data["name"], "group_name": user_db_data["group_name"], "admin": user_db_data["admin"], "timestamp": user_db_data["date_created"], } return jsonify(response_data), 201
def post(self): request_data = request.get_json() if request_data is None or not request_data.get("names"): error(400, "No json data in request body") for alias in request_data["names"]: check_data_fields( alias, ["first_name", "last_name", "middle_name", "birth_date"]) record = build_record() record_summary = RecordSummarizer.summarize(record) response_data = {"data": {"record": record_summary}} current_app.json_encoder = ExpungeModelEncoder return response_data # Json-encoding happens automatically here
def post(self): data = request.get_json() if data is None: error(400, "No json data in request body") check_data_fields(data, ["email", "password"]) user_db_result = user_db_util.read( g.database, user_db_util.identify_by_email(g.database, data["email"])) if not user_db_result or not check_password_hash( user_db_result["hashed_password"], data["password"]): error(401, "Invalid username or password") user = from_dict(data_class=User, data=user_db_result) User.login_user(user) return jsonify({"is_admin": user.admin})
def post(self): """ Attempts to log in to the OECI web site using the provided username and password if successful, encrypt those credentials and return them in a cookie. If the credentials """ data = request.get_json() if data is None: error(400, "No json data in request body") check_data_fields(data, ["oeci_username", "oeci_password"]) credentials = {"oeci_username": data["oeci_username"], "oeci_password": data["oeci_password"]} crawler_session = requests.Session() try: Crawler.attempt_login(crawler_session, credentials["oeci_username"], credentials["oeci_password"]) except InvalidOECIUsernamePassword as e: error(401, str(e)) except OECIUnavailable as e: error(404, str(e)) finally: crawler_session.close() cipher = DataCipher(key=current_app.config.get("SECRET_KEY")) encrypted_credentials = cipher.encrypt(credentials) response = make_response() # TODO: We will need an OECILogout endpoint to remove httponly=true cookies from frontend response.set_cookie( "oeci_token", secure=os.getenv("TIER") == "production", httponly=False, samesite="strict", expires=time.time() + 2 * 60 * 60, # type: ignore # 2 hour lifetime value=encrypted_credentials, ) return response, 201
def post(self): request_data = request.get_json() if request_data is None or not request_data.get("names"): error(400, "No json data in request body") for alias in request_data["names"]: check_data_fields( alias, ["first_name", "last_name", "middle_name", "birth_date"]) cipher = DataCipher(key=current_app.config.get("SECRET_KEY")) if not "oeci_token" in request.cookies.keys(): error(401, "Missing login credentials to OECI.") decrypted_credentials = cipher.decrypt(request.cookies["oeci_token"]) crawler = Crawler() login_result = crawler.login(decrypted_credentials["oeci_username"], decrypted_credentials["oeci_password"], close_session=False) if login_result is False: error(401, "Attempted login to OECI failed") cases: List[Case] = [] for alias in request_data["names"]: cases += crawler.search( alias["first_name"], alias["last_name"], alias["middle_name"], alias["birth_date"], ).cases cases_with_unique_case_number = [ list(group)[0] for key, group in groupby(cases, lambda case: case.case_number) ] record = Record(cases_with_unique_case_number) expunger = Expunger(record) expunger.run() try: save_result(request_data, record) except Exception as ex: logging.error("Saving search result failed with exception: %s" % ex, stack_info=True) record_summary = RecordSummarizer.summarize(record) response_data = {"data": {"record": record_summary}} current_app.json_encoder = ExpungeModelEncoder return response_data # Json-encoding happens automatically here
def post(self): request_data = request.get_json() if request_data is None: error(400, "No json data in request body") check_data_fields(request_data, ["first_name", "last_name", "middle_name", "birth_date"]) cipher = DataCipher(key=current_app.config.get("SECRET_KEY")) decrypted_credentials = cipher.decrypt(request.cookies["oeci_token"]) login_result = ( decrypted_credentials["oeci_username"] == "username" and decrypted_credentials["oeci_password"] == "password" ) if login_result is False: error(401, "Attempted login to OECI failed") record = build_record() response_data = {"data": {"record": record}} current_app.json_encoder = ExpungeModelEncoder return response_data # Json-encoding happens automatically here
def post(self): """ Create a new user with provided email, password, and admin flag. - If required fields are missing in the request, return 400 - Password must be 8 or more characters long. Otherwise return 422 - Email must not already be in use by an existing user. Otherwise return 422 - If success, return 201 with the new user's email, admin flag, and creation timestamp. """ data = request.get_json() if data is None: error(400, "No json data in request body") check_data_fields(data, ["email", "name", "group_name", "password", "admin"]) if len(data["password"]) < 8: error(422, "New password is less than 8 characters long!") password_hash = generate_password_hash(data["password"]) try: create_user_result = user_db_util.create( g.database, email=data["email"], name=data["name"], group_name=data["group_name"], password_hash=password_hash, admin=data["admin"], ) except UniqueViolation: error(422, "User with that email address already exists") response_data = { "user_id": create_user_result["user_id"], "email": create_user_result["email"], "admin": create_user_result["admin"], "name": create_user_result["name"], "group_name": create_user_result["group_name"], "timestamp": create_user_result["date_created"], } return jsonify(response_data), 201
def post(self): request_data = request.get_json() if request_data is None: error(400, "No json data in request body") check_data_fields(request_data, ["first_name", "last_name", "middle_name", "birth_date"]) cipher = DataCipher(key=current_app.config.get("SECRET_KEY")) if not "oeci_token" in request.cookies.keys(): error(401, "Missing login credentials to OECI.") decrypted_credentials = cipher.decrypt(request.cookies["oeci_token"]) crawler = Crawler() login_result = crawler.login( decrypted_credentials["oeci_username"], decrypted_credentials["oeci_password"], close_session=False ) if login_result is False: error(401, "Attempted login to OECI failed") record = crawler.search( request_data["first_name"], request_data["last_name"], request_data["middle_name"], request_data["birth_date"], ) expunger = Expunger(record) expunger.run() try: save_result(request_data, record) except Exception as ex: logging.error("Saving search result failed with exception: %s" % ex, stack_info=True) record.summary = RecordSummarizer.summarize(record) response_data = {"data": {"record": record}} current_app.json_encoder = ExpungeModelEncoder return response_data # Json-encoding happens automatically here
def put_from_user_id(user_id): user_db_data = user_db_util.read(g.database, user_id) if not user_db_data: error(404, "User id not recognized.") if not current_user.is_admin and current_user.user_id != user_id: error(403, "Logged in user not admin and doesn't match requested user id.") data = request.get_json() if data is None: error(400, "No json data in request body") if not any([ key in data.keys() for key in ["email", "name", "group_name", "password", "admin"] ]): error( 400, "Json data must define one or more of: \ email, name, group_name, password, admin", ) if ("admin" in data.keys()) and (data["admin"] is True) and (not current_user.is_admin): error(403, "Logged in user can not grant self admin privileges.") if "password" in data.keys() and len(data["password"]) > 0: if len(data["password"]) < 8: error(422, "New password is less than 8 characters long.") data["hashed_password"] = generate_password_hash(data["password"]) try: update_user_result = user_db_util.update(g.database, user_id, data) except UniqueViolation: error(422, "User with that email address already exists") if update_user_result is None: # Returns None if the user doesn't exist. We already checked this, # but if it still fails, throw 404 error(404, "User id not recognized") response_data = { "user_id": update_user_result["user_id"], "email": update_user_result["email"], "admin": update_user_result["admin"], "name": update_user_result["name"], "group_name": update_user_result["group_name"], "timestamp": update_user_result["date_modified"], } return jsonify(response_data), 200