diff --git a/scim2_server/operators.py b/scim2_server/operators.py index 975ab22..602d32a 100644 --- a/scim2_server/operators.py +++ b/scim2_server/operators.py @@ -258,9 +258,6 @@ class RemoveOperator(Operator): @classmethod def operation(cls, model: BaseModel, attribute: str, value: Any): alias = get_by_alias(type(model), attribute) - existing_value = getattr(model, alias) - if not existing_value: - return if model.get_field_annotation(alias, Mutability) in ( Mutability.read_only, @@ -268,6 +265,10 @@ def operation(cls, model: BaseModel, attribute: str, value: Any): ): raise SCIMException(Error.make_mutability_error()) + existing_value = getattr(model, alias) + if not existing_value: + return + if model.get_field_annotation(alias, Required) == Required.true: raise SCIMException(Error.make_invalid_value_error()) diff --git a/tests/test_operators.py b/tests/test_operators.py index 472c501..21c81ae 100644 --- a/tests/test_operators.py +++ b/tests/test_operators.py @@ -451,6 +451,16 @@ def test_simple_remove_operator_immutable(self): with pytest.raises(SCIMException, match="immutable"): RemoveOperator.operation(u, "groups", None) + def test_remove_operator_mutability_validation_on_empty_fields(self): + """Test that mutability constraints are enforced even on empty/unset fields.""" + u = User(id="123") # groups field is None/empty by default + with pytest.raises(SCIMException, match="mutability"): + RemoveOperator.operation(u, "groups", None) + + u2 = User() # id field is None/empty by default + with pytest.raises(SCIMException, match="mutability"): + RemoveOperator.operation(u2, "id", None) + def test_remove_operator_root_object(self): u = User() with pytest.raises(SCIMException, match="noTarget"):