Exemplo n.º 1
0
            def _handler(**kwargs):
                try:
                    user: User
                    if not self.app.debug:
                        user = User.get(
                            discord_id=self.discord.fetch_user().id)
                        if user is None or not user.admin:
                            return "User not authorized", 403
                    else:
                        user = list(User.select().limit(1))[0]

                    return handler(user, **kwargs)

                except flask_discord.exceptions.Unauthorized:
                    return "Unknown user", 404
Exemplo n.º 2
0
def login_with_guest(sio: ServerApp, encrypted_login_request: bytes):
    if sio.guest_encrypt is None:
        raise NotAuthorizedForAction()

    try:
        login_request_bytes = sio.guest_encrypt.decrypt(encrypted_login_request)
    except cryptography.fernet.InvalidToken:
        raise NotAuthorizedForAction()

    try:
        login_request = json.loads(login_request_bytes.decode("utf-8"))
        name = login_request["name"]
        date = datetime.datetime.fromisoformat(login_request["date"])
    except (UnicodeDecodeError, json.JSONDecodeError, KeyError, ValueError) as e:
        raise InvalidAction(str(e))

    if _get_now() - date > datetime.timedelta(days=1):
        raise NotAuthorizedForAction()

    user: User = User.create(name=f"Guest: {name}")

    with sio.session() as session:
        session["user-id"] = user.id

    return _create_client_side_session(sio, user)
Exemplo n.º 3
0
def test_login_with_guest(flask_app, clean_database, mocker):
    # Setup
    mocker.patch("randovania.server.user_session._get_now",
                 return_value=datetime.datetime(year=2020, month=9, day=4))
    mock_create_session = mocker.patch(
        "randovania.server.user_session._create_client_side_session",
        autospec=True)
    enc_request = b"encrypted stuff"

    sio = MagicMock()
    sio.guest_encrypt.decrypt.return_value = json.dumps({
        "name":
        "Someone",
        "date":
        '2020-09-05T17:12:09.941661',
    }).encode("utf-8")

    with flask_app.test_request_context():
        flask.request.sid = 7890
        result = user_session.login_with_guest(sio, enc_request)

    # Assert
    sio.guest_encrypt.decrypt.assert_called_once_with(enc_request)
    user: User = User.get_by_id(1)
    assert user.name == "Guest: Someone"

    mock_create_session.assert_called_once_with(sio, user)
    assert result is mock_create_session.return_value
Exemplo n.º 4
0
 def get_current_user(self) -> User:
     try:
         return User.get_by_id(self.get_session()["user-id"])
     except KeyError:
         raise NotLoggedIn()
     except peewee.DoesNotExist:
         raise InvalidSession()
Exemplo n.º 5
0
def _create_client_side_session(sio: ServerApp, user: Optional[User]) -> dict:
    """

    :param user: If the session's user was already retrieved, pass it along to avoid an extra query.
    :return:
    """
    session = sio.get_session()
    encrypted_session = sio.fernet_encrypt.encrypt(
        json.dumps(session).encode("utf-8"))
    if user is None:
        user = User.get_by_id(session["user-id"])
    elif user.id != session["user-id"]:
        raise RuntimeError(f"Provided user does not match the session's user")

    logger().info(
        f"Client at {sio.current_client_ip()} is user {user.name} ({user.id})."
    )

    return {
        "user":
        user.as_json,
        "sessions": [
            membership.session.create_list_entry()
            for membership in GameSessionMembership.select().where(
                GameSessionMembership.user == user)
        ],
        "encoded_session_b85":
        base64.b85encode(encrypted_session),
    }
Exemplo n.º 6
0
def restore_user_session(sio: ServerApp, encrypted_session: bytes,
                         session_id: Optional[int]):
    try:
        decrypted_session: bytes = sio.fernet_encrypt.decrypt(
            encrypted_session)
        session = json.loads(decrypted_session.decode("utf-8"))

        if "discord-access-token" in session:
            user, result = _create_session_with_discord_token(
                sio, session["discord-access-token"])
        else:
            user = User.get_by_id(session["user-id"])
            sio.save_session(session)
            result = _create_client_side_session(sio, user)

        if session_id is not None:
            sio.join_game_session(
                GameSessionMembership.get_by_ids(user.id, session_id))

        return result

    except UserNotAuthorized:
        raise

    except (KeyError, peewee.DoesNotExist, json.JSONDecodeError,
            InvalidTokenError) as e:
        # InvalidTokenError: discord token expired and couldn't renew
        logger().info("Client at %s was unable to restore session: (%s) %s",
                      sio.current_client_ip(), str(type(e)), str(e))
        raise InvalidSession()

    except Exception:
        logger().exception("Error decoding user session")
        raise InvalidSession()
Exemplo n.º 7
0
def login_with_discord(sio: ServerApp, code: str):
    oauth = OAuth2Session(
        client_id=sio.app.config["DISCORD_CLIENT_ID"],
        scope=["identify"],
        redirect_uri=sio.app.config["DISCORD_REDIRECT_URI"],
    )
    access_token = oauth.fetch_token(
        "https://discord.com/api/oauth2/token",
        code=code,
        client_secret=sio.app.config["DISCORD_CLIENT_SECRET"],
    )

    flask.session["DISCORD_OAUTH2_TOKEN"] = access_token
    discord_user = sio.discord.fetch_user()

    user: User = User.get_or_create(discord_id=discord_user.id,
                                    defaults={"name": discord_user.name})[0]
    if user.name != discord_user.name:
        user.name = discord_user.name
        user.save()

    with sio.session() as session:
        session["user-id"] = user.id
        session["discord-access-token"] = access_token

    return _create_client_side_session(sio, user)
Exemplo n.º 8
0
def restore_user_session(sio: ServerApp, encrypted_session: bytes,
                         session_id: Optional[int]):
    try:
        decrypted_session: bytes = sio.fernet_encrypt.decrypt(
            encrypted_session)
        session = json.loads(decrypted_session.decode("utf-8"))

        user = User.get_by_id(session["user-id"])

        if "discord-access-token" in session:
            # TODO: test if the discord access token is still valid
            flask.session["DISCORD_OAUTH2_TOKEN"] = session[
                "discord-access-token"]
        sio.get_server().save_session(flask.request.sid, session)

        if session_id is not None:
            sio.join_game_session(
                GameSessionMembership.get_by_ids(user.id, session_id))

        return _create_client_side_session(sio, user)

    except (KeyError, peewee.DoesNotExist, json.JSONDecodeError):
        raise InvalidSession()

    except Exception:
        logger().exception("Error decoding user session")
        raise InvalidSession()
Exemplo n.º 9
0
    def browser_discord_login_callback():
        sio.discord.callback()
        discord_user = sio.discord.fetch_user()

        user: User = User.get(discord_id=discord_user.id)
        if user is None:
            return "Unknown user", 404

        return flask.redirect(flask.url_for("browser_me"))
Exemplo n.º 10
0
            def _handler(**kwargs):
                try:
                    user: User = User.get(
                        discord_id=self.discord.fetch_user().id)
                    if user is None or not user.admin:
                        return "User not authorized", 403

                    return handler(user, **kwargs)

                except flask_discord.exceptions.Unauthorized:
                    return "Unknown user", 404
Exemplo n.º 11
0
def test_login_with_discord(mock_fetch_token: MagicMock, clean_database,
                            flask_app, existing):
    # Setup
    sio = MagicMock()
    session = {}
    sio.session.return_value.__enter__.return_value = session
    sio.get_session.return_value = session
    mock_fetch_token.return_value = "access_token"
    sio.fernet_encrypt.encrypt.return_value = b"encrypted"

    discord_user = sio.discord.fetch_user.return_value
    discord_user.id = 1234
    discord_user.name = "A Name"

    if existing:
        User.create(discord_id=discord_user.id, name="Someone else")

    # Run
    with flask_app.test_request_context():
        result = user_session.login_with_discord(sio, "code")

    # Assert
    mock_fetch_token.assert_called_once_with(
        ANY,
        "https://discord.com/api/oauth2/token",
        code="code",
        client_secret=sio.app.config["DISCORD_CLIENT_SECRET"],
    )
    user = User.get(User.discord_id == 1234)
    assert user.name == "A Name"

    assert session == {
        "user-id": user.id,
        "discord-access-token": "access_token",
    }
    assert result == {
        "user": user.as_json,
        "sessions": [],
        "encoded_session_b85": b'Wo~0~d2n=PWB',
    }
Exemplo n.º 12
0
def _create_session_with_discord_token(sio: ServerApp,
                                       access_token: str) -> Tuple[User, dict]:
    flask.session["DISCORD_OAUTH2_TOKEN"] = access_token
    discord_user = sio.discord.fetch_user()

    user: User = User.get_or_create(discord_id=discord_user.id,
                                    defaults={"name": discord_user.name})[0]
    if user.name != discord_user.name:
        user.name = discord_user.name
        user.save()

    if sio.enforce_role is not None:
        if not sio.enforce_role.verify_user(discord_user.id):
            logger().info(
                "User %s is not authorized for connecting to the server",
                discord_user.name)
            raise UserNotAuthorized()

    with sio.session() as session:
        session["user-id"] = user.id
        session["discord-access-token"] = access_token

    return user, _create_client_side_session(sio, user)
Exemplo n.º 13
0
def test_restore_user_session_with_discord(mock_create_session: MagicMock,
                                           flask_app, fernet, clean_database):
    sio = MagicMock()
    sio.fernet_encrypt = fernet

    user: User = User.create(id=1234, discord_id=5678, name="The Name")

    session = {
        "user-id": 1234,
        "discord-access-token": "access-token",
    }
    enc_session = fernet.encrypt(json.dumps(session).encode("utf-8"))

    # Run
    with flask_app.test_request_context():
        flask.request.sid = 7890
        result = user_session.restore_user_session(sio, enc_session, None)

    # Assert
    sio.get_server.return_value.save_session.assert_called_once_with(
        7890, session)
    mock_create_session.assert_called_once_with(sio, user)
    assert result is mock_create_session.return_value