Repository: rucio/rucio

Issue:
Do not allow the child rule to be the same as the parent
Motivation
----------

An action such as `$ rucio update-rule --child-rule-id 3d34489b97bd4cdb9e2a0b0509da1bfa 3d34489b97bd4cdb9e2a0b0509da1bfa` is currently possible. Especially due to #5930, it means that one can create unintended situations that might require direct database manipulation in order to fix.

Modification
------------

Add some protection against this.


Discussion:

Resolving patch:
diff --git a/lib/rucio/core/rule.py b/lib/rucio/core/rule.py
--- a/lib/rucio/core/rule.py
+++ b/lib/rucio/core/rule.py
@@ -1288,6 +1288,9 @@ def update_rule(rule_id, options, session=None):
                 child_rule = session.query(models.ReplicationRule).filter_by(id=options[key]).one()
                 if rule.scope != child_rule.scope or rule.name != child_rule.name:
                     raise InputValidationError('Parent and child rule must be set on the same dataset.')
+
+                if rule.id == options[key]:
+                    raise InputValidationError('Self-referencing parent/child-relationship.')
                 if child_rule.state != RuleState.OK:
                     rule.child_rule_id = options[key]
 


Test patch:
diff --git a/lib/rucio/tests/test_bin_rucio.py b/lib/rucio/tests/test_bin_rucio.py
--- a/lib/rucio/tests/test_bin_rucio.py
+++ b/lib/rucio/tests/test_bin_rucio.py
@@ -2054,6 +2054,46 @@ def test_update_rule_cancel_requests_args(self):
         assert '--stuck or --suspend must be specified when running --cancel-requests' in err
         assert exitcode != 0
 
+    def test_update_rule_no_child_selfassign(self):
+        """CLIENT(USER): do not permit to assign self as own child"""
+        tmp_file = file_generator()
+        tmp_fname = tmp_file[5:]
+        cmd = f'rucio upload --rse {self.def_rse} --scope {self.user} {tmp_file}'
+        exitcode, out, err = execute(cmd)
+        assert 'ERROR' not in err
+
+        tmp_rse = rse_name_generator()
+        cmd = f'rucio-admin rse add {tmp_rse}'
+        exitcode, out, err = execute(cmd)
+        assert not err
+
+        self.account_client.set_local_account_limit('root', tmp_rse, -1)
+
+        cmd = (f'rucio-admin rse set-attribute --rse {tmp_rse}'
+               f' --key spacetoken --value RULELOC')
+        exitcode, out, err = execute(cmd)
+        assert not err
+
+        # PREPARING THE RULES
+        # add rule
+        rule_expr = "spacetoken=RULELOC"
+        cmd = f"rucio add-rule {self.user}:{tmp_fname} 1 '{rule_expr}'"
+        exitcode, out, err = execute(cmd)
+        assert not err
+
+        # get the rules for the file
+        cmd = r"rucio list-rules {0}:{1} | grep {0}:{1} | cut -f1 -d\ ".\
+            format(self.user, tmp_file[5:])
+        exitcode, out, err = execute(cmd)
+        parentrule_id, _ = out.split()
+
+        # now for the test
+        # TODO: merge this with the other update_rule test from issue #5930
+        cmd = f"rucio update-rule --child-rule-id {parentrule_id} {parentrule_id}"
+        exitcode, out, err = execute(cmd)
+        # TODO: add a more specific assertion here.
+        assert err
+
     def test_update_rule_boost_rule_arg(self):
         """CLIENT(USER): update a rule with the `--boost_rule` option """
         self.account_client.set_local_account_limit('root', self.def_rse, -1)