Ejemplo n.º 1
0
def board_phase():
    LOG.info("Phase request")

    phase = _phase_from_trains(request.args.get("trains"))

    LOG.info(f"Phase: {phase}")

    return jsonify({"phase": phase})
Ejemplo n.º 2
0
def trains():
    LOG.info("Train request.")

    all_trains = get_train_info(g.game_name)
    train_strs = [
        str(train)
        for train in sorted(all_trains,
                            key=lambda train: (train.collect, train.visit))
    ]

    LOG.info(f"Train response: {all_trains}")

    return jsonify({"trains": train_strs})
Ejemplo n.º 3
0
def legal_tiles():
    game = get_game(g.game_name)

    coord = request.args.get("coord")

    LOG.info(f"Legal tiles request for {coord}.")

    legal_tile_ids = _legal_tile_ids_by_coord(get_game(g.game_name), coord)
    legal_tile_ids.sort(key=lambda tile_id:
                        f"{game.tiles[tile_id].upgrade_level}-{tile_id:0>3}")

    LOG.info(f"Legal tiles response for {coord}: {legal_tile_ids}")

    return jsonify({"legal-tile-ids": legal_tile_ids})
Ejemplo n.º 4
0
def legal_token_coords():
    game = get_game(g.game_name)
    private_companies = game.get_game_submodule("private_companies")
    if not private_companies:
        return jsonify({"coords": {}})

    LOG.info("Legal private company token coordinates request.")

    private_company_coords = private_companies.PRIVATE_COMPANY_COORDS

    LOG.info(
        f"Legal private company token coordinates response: {private_company_coords}"
    )

    return jsonify({"coords": private_company_coords})
Ejemplo n.º 5
0
def removable_railroads():
    LOG.info("Removable railroads request.")

    removable_railroads = _get_removable_railroads()

    LOG.info(f"Removable railroads response: {removable_railroads}")

    railroads_info = get_railroad_info(g.game_name)
    return jsonify({
        "railroads": list(sorted(removable_railroads)),
        "home-cities": {
            railroad: railroads_info[railroad]["home"]
            for railroad in removable_railroads
        }
    })
Ejemplo n.º 6
0
def closable_railroads():
    LOG.info("Closable railroads request.")

    game = get_game(g.game_name)
    closable_railroads = _get_closable_railroads(game)

    LOG.info(f"Closable railroads response: {closable_railroads}")

    railroads_info = get_railroad_info(game)
    return jsonify({
        "railroads": list(sorted(closable_railroads)),
        "home-cities": {
            railroad: railroads_info[railroad]["home"]
            for railroad in closable_railroads
        }
    })
Ejemplo n.º 7
0
def private_companies_open():
    game = get_game(g.game_name)
    private_companies = game.get_game_submodule("private_companies")
    if not private_companies:
        return jsonify({"private-companies": []})

    LOG.info("Open private companies request.")

    phase = _phase_from_trains(request.args.get("trains"))
    private_companies = [
        private_company for private_company in private_companies.COMPANIES
        if not game.private_is_closed(private_company, phase)
    ]

    LOG.info(f"Open private companies response: {private_companies}")

    return jsonify({"private-companies": private_companies})
Ejemplo n.º 8
0
def legal_tile_coords():
    LOG.info("Legal tile coordinates request.")

    current_coord = request.args.get("coord")
    existing_tile_coords = {
        coord
        for coord in json.loads(request.args.get("tile_coords")) if coord
    }

    legal_tile_coordinates = set(get_tile_coords(get_board(
        g.game_name))) - existing_tile_coords
    if current_coord:
        legal_tile_coordinates.add(current_coord)

    LOG.info(f"Legal tile coordinates response: {legal_tile_coordinates}")

    return jsonify({"tile-coords": list(sorted(legal_tile_coordinates))})
Ejemplo n.º 9
0
def cities():
    LOG.info("Cities request.")

    board = get_board(g.game_name)
    # all_cities = [str(cell) for cell in sorted(board.cells) if board.get_space(cell) and board.get_space(cell).is_city]
    all_cities = []
    split_cities = []
    for cell in sorted(board.cells):
        space = board.get_space(cell)
        if space and space.is_city:
            coord = str(cell)
            all_cities.append(coord)
            if isinstance(space, (boardtile.SplitCity, placedtile.SplitCity)):
                split_cities.append(coord)

    LOG.info(f"Cities response: {all_cities}")

    return jsonify({"cities": all_cities, "split-cities": split_cities})
Ejemplo n.º 10
0
def legal_orientations():
    coord = request.args.get("coord")
    tile_id = request.args.get("tileId")

    LOG.info(f"Legal orientations request for {tile_id} at {coord}.")

    orientations, translations = _get_orientations(get_game(g.game_name),
                                                   coord, tile_id)

    LOG.info(
        f"Legal orientations response for {tile_id} at {coord}: {orientations}"
    )

    return jsonify({
        "legal-orientations":
        list(sorted(orientations))
        if orientations is not None else orientations,
        "translations":
        translations
    })
Ejemplo n.º 11
0
def process_migrate_data():
    migration_data = json.loads(session.pop(SESSION_COOKIE_KEY, "{}"))
    if migration_data:
        LOG.debug(f"Migration finalizing: found session data.")
        if _validate_migration_data(migration_data):
            LOG.debug(f"Migration data pre-conversion: {migration_data}")
            migration_data = _convert_migration_data(migration_data)
            LOG.debug(f"Migration data post-conversion: {migration_data}")
        else:
            LOG.debug(
                f"Migration validation failed: continuing without migration")
            migration_data = {}
    return migration_data
Ejemplo n.º 12
0
def legal_railroads():
    LOG.info("Legal railroads request.")

    existing_railroads = {
        railroad
        for railroad in json.loads(request.args.get("railroads", "{}"))
        if railroad
    }

    railroads_info = get_railroad_info(g.game_name)
    legal_railroads = set(railroads_info.keys()) - existing_railroads

    LOG.info(f"Legal railroads response: {legal_railroads}")

    return jsonify({
        "railroads": list(sorted(legal_railroads)),
        "home-cities": {
            railroad: railroads_info[railroad]["home"]
            for railroad in legal_railroads
        }
    })
Ejemplo n.º 13
0
def split_city_stations():
    LOG.info("Legal split city stations request.")

    existing_station_coords = {
        coord
        for coord in json.loads(request.args.get("stations", "{}")) if coord
    }

    split_city_station_coords = _get_split_city_stations(
        # The default values can change once the templates are generalized. And
        # they'll need to before implementing another game with split cities.
        request.args["coord"],
        request.args.get("tileId"),
        request.args.get("orientation"))

    legal_stations = sorted(split_city_station_coords -
                            existing_station_coords)

    LOG.info(f"Legal split city stations response: {legal_stations}")

    return jsonify({"split-city-stations": legal_stations})
Ejemplo n.º 14
0
def calculate_result():
    routes_json = get_calculate_result(request.args.get("jobId"))

    LOG.info(f"Calculate response: {routes_json}")

    return jsonify(routes_json)
Ejemplo n.º 15
0
def calculate():
    railroads_state_rows = json.loads(request.form.get("railroads-json"))
    removed_railroads = json.loads(request.form.get("removed-railroads-json"))
    closed_railroads = json.loads(request.form.get("closed-railroads-json"))
    private_companies_rows = json.loads(request.form.get("private-companies-json"))
    board_state_rows = json.loads(request.form.get("board-state-json"))
    railroad_name = request.form["railroad-name"]

    LOG.info("Calculate request.")
    LOG.info(f"Target railroad: {railroad_name}")
    LOG.info(f"Private companies: {private_companies_rows}")
    LOG.info(f"Railroad input: {railroads_state_rows}")
    LOG.info(f"Removed railroads: {removed_railroads}")
    LOG.info(f"Closed railroads: {closed_railroads}")
    LOG.info(f"Board input: {board_state_rows}")

    railroads_state_rows += [[name, "removed"] for name in removed_railroads]
    railroads_state_rows += [[name, "closed"] for name in closed_railroads]

    job = CALCULATOR_QUEUE.enqueue(calculate_worker, g.game_name, railroads_state_rows, private_companies_rows, board_state_rows, railroad_name, job_timeout="5m")

    return jsonify({"jobId": job.id})
Ejemplo n.º 16
0
def _validate_migration_data(migration_data):
    if not isinstance(migration_data, dict):
        LOG.debug(
            f"Migration validation failure: migration_data is not a dict")
        return {"error": "Failed to load migration data."}

    game = get_game("1846")
    railroad_info = get_railroad_info(game)
    private_companies = game.get_game_submodule("private_companies")
    for key, value in migration_data.items():
        # Make sure no control characters or non-ASCII characters are present.
        if not key.isascii() or not key.isprintable() or not value.isascii(
        ) or not value.isprintable():
            LOG.debug(
                f"Migration failure: key or value is not printable ASCII")
            return False

        try:
            migration_data[key] = json.dumps(json.loads(value))
        except Exception as exc:
            LOG.debug(
                f"Migration validation failure[{key}]: value not valid JSON: {value}"
            )
            return False

        if key == "placedTilesTable":
            tiles_json = json.loads(value)
            if not all(len(row) == 3 for row in tiles_json) or not all(
                    str(col).isalnum() for row in tiles_json for col in row):
                LOG.debug(f"Migration validation failure[{key}]: {value}")
                return False
        elif key == "railroadsTable":
            railroads_json = json.loads(value)
            for row in railroads_json:
                if len(row) not in (3,
                                    4) or row[0].strip() not in railroad_info:
                    LOG.debug(
                        f"Migration validation failure[{key}]: row wrong length, or railroad name invalid: {value}"
                    )
                    return False
                if row[1] and row[1].strip():
                    train_strs = [
                        train_str.strip().split("/", 1)
                        for train_str in row[1].strip().split(",")
                    ]
                    if not all(val.strip().isdigit()
                               for train_str in train_strs
                               for val in train_str):
                        LOG.debug(
                            f"Migration validation failure[{key}]: trains malformed: {value}"
                        )
                        return False
                if row[2] and row[2].strip():
                    if not all(station_str.strip().isalnum()
                               for station_str in row[2].strip().split(",")):
                        LOG.debug(
                            f"Migration validation failure[{key}]: stations malformed: {value}"
                        )
                        return False
        elif key == "removedRailroadsTable":
            removed_railroads = json.loads(value)
            for railroad in removed_railroads:
                if railroad.strip() not in railroad_info:
                    LOG.debug(
                        f"Migration validation failure[{key}]: railroad name invalid: {value}"
                    )
                    return False
        elif key == "privateCompaniesTable":
            private_companies_json = json.loads(value)
            for row in private_companies_json:
                if len(row) != 3 \
                        or row[0].strip() not in private_companies.COMPANIES \
                        or (row[1] and row[1].strip() not in railroad_info) \
                        or (row[2] and not row[2].strip().isalnum()):
                    LOG.debug(
                        f"Migration validation failure[{key}]: invalid row: {row}"
                    )
                    return False
        elif key == "hideCityPaths":
            if value not in ("true", "false"):
                LOG.debug(f"Migration validation failure[{key}]: {value}")
                return False
        else:
            LOG.debug(f"Migration validation failure: invalid key: {key}")
            return False

    return True
Ejemplo n.º 17
0
def complete_migration():
    LOG.info(f"Migration continued")

    id = request.args.get("id")
    if not id:
        LOG.debug(f"Migration failure: no ID provided")
        return redirect(url_for('.main'))

    try:
        uuid.UUID(id)
    except:
        LOG.debug(f"Migration failure: invalid ID provided; expected UUID")
        return redirect(url_for('.main'))

    LOG.info(f"Migration continuing for {id}")

    migration_data = redis_conn.hgetall(f"{REDIS_KEY_PREFIX}-{id}")
    if not migration_data:
        LOG.debug(f"Migration failure: no data to load")
        return redirect(url_for('.main'))

    # Once we get the data, we no longer need it, regardless of validity.
    redis_conn.delete(id)

    try:
        migration_data = {
            key.decode("ascii"): value.decode("ascii")
            for key, value in migration_data.items()
        }
    except UnicodeEncodeError as exc:
        LOG.debug(f"Migration failure: loaded data not all ASCII: {exc}")
        return redirect(url_for('.main'))

    if not _validate_migration_data(migration_data):
        return redirect(url_for('.main'))

    LOG.debug(f"Migration continuing: storing data in session")
    session[SESSION_COOKIE_KEY] = json.dumps(migration_data)

    return redirect(url_for('.main'))
Ejemplo n.º 18
0
def start_migration():
    # The processing of migration_data is meant to disrupt an attacker trying
    # to poison the redis instance. As such, the error message should not give
    # them any details as to what went wrong.
    error_message = {"error": "Failed to start the migration."}

    LOG.info(f"Migration requested")

    migration_data = request.data
    if not migration_data:
        LOG.debug(f"Migration failure: no migration data provided")
        return jsonify(error_message), 400

    migration_data = migration_data.decode("utf-8")
    if not migration_data.strip().isprintable() or not migration_data.strip(
    ).isascii():
        LOG.debug(f"Migration failure: migration data not all printable ASCII")
        return jsonify(error_message), 400

    try:
        migration_data_json = json.loads(migration_data)
    except Exception as exc:
        LOG.debug(
            f"Migration failure: migration data is not valid JSON: {exc}")
        return jsonify(error_message), 400

    try:
        validation_error = _validate_migration_data(migration_data_json)
    except Exception as exc:
        LOG.debug(
            f"Migration failure: an exception occurred during validation: {exc}"
        )
        return jsonify(error_message), 400

    if not validation_error:
        return jsonify(error_message), 400

    redis_id = str(uuid.uuid4())
    redis_key = f"{REDIS_KEY_PREFIX}-{redis_id}"
    redis_conn.hmset(redis_key, migration_data_json)
    redis_conn.expire(redis_key, 60)

    LOG.info(f"Migration initiated: {redis_id}")

    return jsonify({"id": redis_id}), 201