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
Exemple #2
0
    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
Exemple #3
0
    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
Exemple #5
0
    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
Exemple #6
0
 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"]
Exemple #7
0
 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
Exemple #9
0
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
Exemple #10
0
    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
Exemple #11
0
    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})
Exemple #12
0
 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
Exemple #13
0
    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
Exemple #14
0
    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
Exemple #16
0
    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