Exemple #1
0
def create_robot(robot_shortname, parent, description="", unstructured_metadata=None):
    (username_valid, username_issue) = validate_username(robot_shortname)
    if not username_valid:
        raise InvalidRobotException(
            "The name for the robot '%s' is invalid: %s" % (robot_shortname, username_issue)
        )

    username = format_robot_username(parent.username, robot_shortname)

    try:
        User.get(User.username == username)

        msg = "Existing robot with name: %s" % username
        logger.info(msg)
        raise InvalidRobotException(msg)
    except User.DoesNotExist:
        pass

    service = LoginService.get(name="quayrobot")
    try:
        with db_transaction():
            created = User.create(username=username, email=str(uuid.uuid4()), robot=True)
            token = random_string_generator(length=64)()
            RobotAccountToken.create(robot_account=created, token=token, fully_migrated=True)
            FederatedLogin.create(
                user=created, service=service, service_ident="robot:%s" % created.id
            )
            RobotAccountMetadata.create(
                robot_account=created,
                description=description[0:255],
                unstructured_json=unstructured_metadata or {},
            )
            return created, token
    except Exception as ex:
        raise DataModelException(ex.message)
Exemple #2
0
def create_user_noverify(
    username, email, email_required=True, prompts=tuple(), is_possible_abuser=False
):
    if email_required:
        if not validate_email(email):
            raise InvalidEmailAddressException("Invalid email address: %s" % email)
    else:
        # If email addresses are not required and none was specified, then we just use a unique
        # ID to ensure that the database consistency check remains intact.
        email = email or str(uuid.uuid4())

    (username_valid, username_issue) = validate_username(username)
    if not username_valid:
        raise InvalidUsernameException("Invalid namespace %s: %s" % (username, username_issue))

    try:
        existing = User.get((User.username == username) | (User.email == email))
        logger.info("Existing user with same username or email.")

        # A user already exists with either the same username or email
        if existing.username == username:
            assert not existing.robot

            msg = (
                "Username has already been taken by an organization and cannot be reused: %s"
                % username
            )
            if not existing.organization:
                msg = "Username has already been taken by user cannot be reused: %s" % username

            raise InvalidUsernameException(msg)

        raise InvalidEmailAddressException("Email has already been used: %s" % email)
    except User.DoesNotExist:
        # This is actually the happy path
        logger.debug("Email and username are unique!")

    # Create the user.
    try:
        default_expr_s = _convert_to_s(config.app_config["DEFAULT_TAG_EXPIRATION"])
        default_max_builds = config.app_config.get("DEFAULT_NAMESPACE_MAXIMUM_BUILD_COUNT")
        threat_max_builds = config.app_config.get("THREAT_NAMESPACE_MAXIMUM_BUILD_COUNT")

        if is_possible_abuser and threat_max_builds is not None:
            default_max_builds = threat_max_builds

        new_user = User.create(
            username=username,
            email=email,
            removed_tag_expiration_s=default_expr_s,
            maximum_queued_builds_count=default_max_builds,
        )
        for prompt in prompts:
            create_user_prompt(new_user, prompt)

        return new_user
    except Exception as ex:
        raise DataModelException(ex.message)
def test_cleanup_old_robots(initialized_db):
  before_robot_count = User.select().where(User.robot == True).count()
  before_user_count = User.select().count()

  # Run the cleanup once, and ensure it does nothing.
  cleanup_old_robots(force=True)

  after_robot_count = User.select().where(User.robot == True).count()
  after_user_count = User.select().count()

  assert before_robot_count == after_robot_count
  assert before_user_count == after_user_count

  # Create some orphan robots.
  created = set()
  for index in range(0, 50):
    created.add('doesnotexist+a%s' % index)
    created.add('anothernamespace+b%s' % index)

    User.create(username='******' % index, robot=True)
    User.create(username='******' % index, robot=True)

  before_robot_count = User.select().where(User.robot == True).count()
  before_user_count = User.select().count()

  cleanup_old_robots(page_size=10, force=True)

  after_robot_count = User.select().where(User.robot == True).count()
  after_user_count = User.select().count()

  assert before_robot_count == after_robot_count + len(created)
  assert before_user_count == after_user_count + len(created)

  for name in created:
    with pytest.raises(User.DoesNotExist):
      User.get(username=name)
Exemple #4
0
def test_readreplica(init_db_path, tmpdir_factory):
    primary_file = str(tmpdir_factory.mktemp("data").join("primary.db"))
    replica_file = str(tmpdir_factory.mktemp("data").join("replica.db"))

    # Copy the initialized database to two different locations.
    shutil.copy2(init_db_path, primary_file)
    shutil.copy2(init_db_path, replica_file)

    db_config = {
        "DB_URI": "sqlite:///{0}".format(primary_file),
        "DB_READ_REPLICAS": [{"DB_URI": "sqlite:///{0}".format(replica_file)},],
        "DB_CONNECTION_ARGS": {"threadlocals": True, "autorollback": True,},
        "DB_TRANSACTION_FACTORY": lambda x: FakeTransaction(),
        "FOR_TESTING": True,
        "DATABASE_SECRET_KEY": "anothercrazykey!",
    }

    # Initialize the DB with the primary and the replica.
    configure(db_config)
    assert not read_only_config.obj.is_readonly
    assert read_only_config.obj.read_replicas

    # Ensure we can read the data.
    devtable_user = User.get(username="******")
    assert devtable_user.username == "devtable"

    # Configure with a bad primary. Reading should still work since we're hitting the replica.
    db_config["DB_URI"] = "sqlite:///does/not/exist"
    configure(db_config)

    assert not read_only_config.obj.is_readonly
    assert read_only_config.obj.read_replicas

    devtable_user = User.get(username="******")
    assert devtable_user.username == "devtable"

    # Force us to hit the master and ensure it doesn't work.
    with db_disallow_replica_use():
        with pytest.raises(OperationalError):
            User.get(username="******")

    # Test read replica again.
    devtable_user = User.get(username="******")
    assert devtable_user.username == "devtable"

    # Try to change some data. This should fail because the primary is broken.
    with pytest.raises(OperationalError):
        devtable_user.email = "newlychanged"
        devtable_user.save()

    # Fix the primary and try again.
    db_config["DB_URI"] = "sqlite:///{0}".format(primary_file)
    configure(db_config)

    assert not read_only_config.obj.is_readonly
    assert read_only_config.obj.read_replicas

    devtable_user.email = "newlychanged"
    devtable_user.save()

    # Mark the system as readonly.
    db_config["DB_URI"] = "sqlite:///{0}".format(primary_file)
    db_config["REGISTRY_STATE"] = "readonly"
    configure(db_config)

    assert read_only_config.obj.is_readonly
    assert read_only_config.obj.read_replicas

    # Ensure all write operations raise a readonly mode exception.
    with pytest.raises(ReadOnlyModeException):
        devtable_user.email = "newlychanged2"
        devtable_user.save()

    with pytest.raises(ReadOnlyModeException):
        User.create(username="******")

    with pytest.raises(ReadOnlyModeException):
        User.delete().where(User.username == "foo").execute()

    with pytest.raises(ReadOnlyModeException):
        User.update(username="******").where(User.username == "foo").execute()

    # Reset the config on the DB, so we don't mess up other tests.
    configure(
        {
            "DB_URI": "sqlite:///{0}".format(primary_file),
            "DB_CONNECTION_ARGS": {"threadlocals": True, "autorollback": True,},
            "DB_TRANSACTION_FACTORY": lambda x: FakeTransaction(),
            "DATABASE_SECRET_KEY": "anothercrazykey!",
        }
    )