def upgrade(tables, tester, progress_reporter): op = ProgressWrapper(original_op, progress_reporter) # ### commands auto generated by Alembic - please adjust! ### op.drop_index("oauthaccesstoken_refresh_token", table_name="oauthaccesstoken") op.drop_column(u"oauthaccesstoken", "refresh_token") op.drop_column("accesstoken", "code") op.drop_column("appspecificauthtoken", "token_code") op.drop_column("oauthaccesstoken", "access_token") op.drop_column("oauthapplication", "client_secret") op.drop_column("oauthauthorizationcode", "code") op.drop_column("repositorybuildtrigger", "private_key") op.drop_column("repositorybuildtrigger", "auth_token") # ### end Alembic commands ### # Overwrite all plaintext robot credentials. from app import app if app.config.get("SETUP_COMPLETE", False) or tester.is_testing(): while True: try: robot_account_token = RobotAccountToken.get(fully_migrated=False) logger.debug("Found robot account token %s migrate", robot_account_token.id) robot_account = robot_account_token.robot_account assert robot_account.robot result = ( User.update(email=str(uuid.uuid4())) .where( User.id == robot_account.id, User.robot == True, User.uuid == robot_account.uuid, ) .execute() ) assert result == 1 try: federated_login = FederatedLogin.get(user=robot_account) assert federated_login.service.name == "quayrobot" federated_login.service_ident = "robot:%s" % robot_account.id federated_login.save() except FederatedLogin.DoesNotExist: pass robot_account_token.fully_migrated = True robot_account_token.save() logger.debug("Finished migrating robot account token %s", robot_account_token.id) except RobotAccountToken.DoesNotExist: break
def verify_user(username_or_email, password): """ Verifies that the given username/email + password pair is valid. If the username or e-mail address is invalid, returns None. If the password specified does not match for the given user, either returns None or raises TooManyLoginAttemptsException if there have been too many invalid login attempts. Returns the user object if the login was valid. """ # Make sure we didn't get any unicode for the username. try: username_or_email.encode("ascii") except UnicodeEncodeError: return None # Fetch the user with the matching username or e-mail address. try: fetched = User.get((User.username == username_or_email) | (User.email == username_or_email)) except User.DoesNotExist: return None # If the user has any invalid login attempts, check to see if we are within the exponential # backoff window for the user. If so, we raise an exception indicating that the user cannot # login. now = datetime.utcnow() if fetched.invalid_login_attempts > 0: can_retry_at = exponential_backoff(fetched.invalid_login_attempts, EXPONENTIAL_BACKOFF_SCALE, fetched.last_invalid_login) if can_retry_at > now: retry_after = can_retry_at - now raise TooManyLoginAttemptsException("Too many login attempts.", retry_after.total_seconds()) # Hash the given password and compare it to the specified password. if (fetched.password_hash and hash_password(password, fetched.password_hash).decode("ascii") == fetched.password_hash): # If the user previously had any invalid login attempts, clear them out now. if fetched.invalid_login_attempts > 0: try: (User.update(invalid_login_attempts=0).where( User.id == fetched.id).execute()) # Mark that the user was accessed. _basequery.update_last_accessed(fetched) except ReadOnlyModeException: pass # Return the valid user. return fetched # Otherwise, update the user's invalid login attempts. try: (User.update( invalid_login_attempts=User.invalid_login_attempts + 1, last_invalid_login=now).where(User.id == fetched.id).execute()) except ReadOnlyModeException: pass # We weren't able to authorize the user return None
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!", } )