Exemplo n.º 1
0
async def test_add(factory: ComponentFactory) -> None:
    admin_service = factory.create_admin_service()

    async with factory.session.begin():
        assert await admin_service.get_admins() == [Admin(username="******")]
        await admin_service.add_admin(
            "example", actor="admin", ip_address="192.168.0.1"
        )

    async with factory.session.begin():
        assert await admin_service.get_admins() == [
            Admin(username="******"),
            Admin(username="******"),
        ]
        assert await admin_service.is_admin("example")
        assert not await admin_service.is_admin("foo")

    async with factory.session.begin():
        with pytest.raises(PermissionDeniedError):
            await admin_service.add_admin(
                "foo", actor="bar", ip_address="127.0.0.1"
            )

    async with factory.session.begin():
        await admin_service.add_admin(
            "foo", actor="<bootstrap>", ip_address="127.0.0.1"
        )

    async with factory.session.begin():
        assert await admin_service.is_admin("foo")
        assert not await admin_service.is_admin("<bootstrap>")
Exemplo n.º 2
0
    def add_admin(self, username: str, *, actor: str, ip_address: str) -> None:
        """Add a new administrator.

        Parameters
        ----------
        username : `str`
            The administrator to delete.
        actor : `str`
            The person doing the deleting.
        ip_address : `str`
            The IP address from which the request came.

        Raises
        ------
        gafaelfawr.exceptions.PermissionDeniedError
            If the actor is not an admin.
        """
        if not self.is_admin(actor) and actor != "<bootstrap>":
            raise PermissionDeniedError(f"{actor} is not an admin")
        admin = Admin(username=username)
        history_entry = AdminHistoryEntry(
            username=username,
            action=AdminChange.add,
            actor=actor,
            ip_address=ip_address,
            event_time=datetime.now(timezone.utc),
        )
        with self._transaction_manager.transaction():
            self._admin_store.add(admin)
            self._admin_history_store.add(history_entry)
Exemplo n.º 3
0
 async def check_database() -> None:
     async with ComponentFactory.standalone() as factory:
         admin_service = factory.create_admin_service()
         expected = [Admin(username=u) for u in config.initial_admins]
         assert await admin_service.get_admins() == expected
         token_service = factory.create_token_service()
         bootstrap = TokenData.bootstrap_token()
         assert await token_service.list_tokens(bootstrap) == []
Exemplo n.º 4
0
def test_add(setup: SetupTest) -> None:
    admin_service = setup.factory.create_admin_service()

    assert admin_service.get_admins() == [Admin(username="******")]

    admin_service.add_admin("example", actor="admin", ip_address="192.168.0.1")

    assert admin_service.get_admins() == [
        Admin(username="******"),
        Admin(username="******"),
    ]
    assert admin_service.is_admin("example")
    assert not admin_service.is_admin("foo")

    with pytest.raises(PermissionDeniedError):
        admin_service.add_admin("foo", actor="bar", ip_address="127.0.0.1")

    admin_service.add_admin("foo", actor="<bootstrap>", ip_address="127.0.0.1")
    assert admin_service.is_admin("foo")
    assert not admin_service.is_admin("<bootstrap>")
Exemplo n.º 5
0
def initialize_database(config: Config, reset: bool = False) -> None:
    """Create and initialize a new database.

    Parameters
    ----------
    config : `gafaelfawr.config.Config`
        The Gafaelfawr configuration.
    reset : `bool`
        If set to `True`, drop all tables and reprovision the database.
        Useful when running tests with an external database.  Default is
        `False`.
    """
    logger = structlog.get_logger(config.safir.logger_name)

    # Check connectivity to the database and retry if needed.  This uses a
    # pre-ping to ensure the database is available and attempts to connect
    # five times with a two second delay between each attempt.
    success = False
    for _ in range(5):
        try:
            engine = create_engine(config.database_url, pool_pre_ping=True)
            if reset:
                drop_schema(engine)
            initialize_schema(engine)
            success = True
        except OperationalError:
            logger.info("database not ready, waiting two seconds")
            time.sleep(2)
            continue
        if success:
            logger.info("initialized database schema")
        break
    if not success:
        msg = "database schema initialization failed (database not reachable?)"
        logger.error(msg)

    session = Session(bind=engine)
    with TransactionManager(session).transaction():
        admin_store = AdminStore(session)
        if not admin_store.list():
            for admin in config.initial_admins:
                logger.info("adding initial admin %s", admin)
                admin_store.add(Admin(username=admin))
    session.close()
Exemplo n.º 6
0
    def delete_admin(
        self, username: str, *, actor: str, ip_address: str
    ) -> bool:
        """Delete an administrator.

        Parameters
        ----------
        username : `str`
            The administrator to delete.
        actor : `str`
            The person doing the deleting.
        ip_address : `str`
            The IP address from which the request came.

        Returns
        -------
        success : `bool`
            `True` if the administrator was found and deleted, `False` if they
            were not found.

        Raises
        ------
        gafaelfawr.exceptions.PermissionDeniedError
            If the actor is not an admin.
        """
        if not self.is_admin(actor) and actor != "<bootstrap>":
            raise PermissionDeniedError(f"{actor} is not an admin")
        admin = Admin(username=username)
        history_entry = AdminHistoryEntry(
            username=username,
            action=AdminChange.remove,
            actor=actor,
            ip_address=ip_address,
            event_time=datetime.now(timezone.utc),
        )
        with self._transaction_manager.transaction():
            if self.get_admins() == [admin]:
                raise PermissionDeniedError("Cannot delete the last admin")
            result = self._admin_store.delete(admin)
            if result:
                self._admin_history_store.add(history_entry)
        return result
Exemplo n.º 7
0
 def list(self) -> List[Admin]:
     """Return a list of current administrators."""
     return [
         Admin.from_orm(a) for a in self._session.query(SQLAdmin).order_by(
             SQLAdmin.username).all()
     ]