Repository: rucio/rucio
Issue: Lack of protection against an invalid rule ID for move-rule operation

Motivation
----------

```console
$ rucio move-rule foo CERN-PROD_DATADISK
2022-08-16 13:45:37,133	ERROR	An unknown exception occurred.
Details: no error information passed (http status code: 500)
```

Then on the server log:
```
Internal Error
(builtins.ValueError) badly formed hexadecimal UUID string
[SQL: SELECT atlas_rucio.rules.id AS atlas_rucio_rules_id, atlas_rucio.rules.subscription_id AS atlas_rucio_rules_subscription_id, atlas_rucio.rules.account AS atlas_rucio_rules_account, atlas_rucio.rules.scope AS atlas_rucio_rules_scope, atlas_rucio.rules.name AS atlas_rucio_rules_name, atlas_rucio.rules.did_type AS atlas_rucio_rules_did_type, atlas_rucio.rules.state AS atlas_rucio_rules_state, atlas_rucio.rules.error AS atlas_rucio_rules_error, atlas_rucio.rules.rse_expression AS atlas_rucio_rules_rse_expression, atlas_rucio.rules.copies AS atlas_rucio_rules_copies, atlas_rucio.rules.expires_at AS atlas_rucio_rules_expires_at, atlas_rucio.rules.weight AS atlas_rucio_rules_weight, atlas_rucio.rules.locked AS atlas_rucio_rules_locked, atlas_rucio.rules.locks_ok_cnt AS atlas_rucio_rules_locks_ok_cnt, atlas_rucio.rules.locks_replicating_cnt AS atlas_rucio_rules_locks_replicating_cnt, atlas_rucio.rules.locks_stuck_cnt AS atlas_rucio_rules_locks_stuck_cnt, atlas_rucio.rules.source_replica_expression AS atlas_rucio_rules_source_replica_expression, atlas_rucio.rules.activity AS atlas_rucio_rules_activity, atlas_rucio.rules.grouping AS atlas_rucio_rules_grouping, atlas_rucio.rules.notification AS atlas_rucio_rules_notification, atlas_rucio.rules.stuck_at AS atlas_rucio_rules_stuck_at, atlas_rucio.rules.purge_replicas AS atlas_rucio_rules_purge_replicas, atlas_rucio.rules.ignore_availability AS atlas_rucio_rules_ignore_availability, atlas_rucio.rules.ignore_account_limit AS atlas_rucio_rules_ignore_account_limit, atlas_rucio.rules.priority AS atlas_rucio_rules_priority, atlas_rucio.rules.comments AS atlas_rucio_rules_comments, atlas_rucio.rules.child_rule_id AS atlas_rucio_rules_child_rule_id, atlas_rucio.rules.eol_at AS atlas_rucio_rules_eol_at, atlas_rucio.rules.split_container AS atlas_rucio_rules_split_container, atlas_rucio.rules.meta AS atlas_rucio_rules_meta, atlas_rucio.rules.created_at AS atlas_rucio_rules_created_at, atlas_rucio.rules.updated_at AS atlas_rucio_rules_updated_at
FROM atlas_rucio.rules
WHERE atlas_rucio.rules.id = :id_1]
  File "/usr/local/lib/python3.6/site-packages/rucio/web/rest/flaskapi/v1/common.py", line 54, in dispatch_request
    return super(ErrorHandlingMethodView, self).dispatch_request(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/flask/views.py", line 158, in dispatch_request
    return current_app.ensure_sync(meth)(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/web/rest/flaskapi/v1/rules.py", line 583, in post
    vo=request.environ.get('vo'))
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 388, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/api/rule.py", line 327, in move_replication_rule
    return rule.move_rule(**kwargs, session=session)
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 402, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/rule.py", line 1404, in move_rule
    rule = session.query(models.ReplicationRule).filter_by(id=rule_id).one()
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/query.py", line 2856, in one
    return self._iter().one()
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/query.py", line 2897, in _iter
    execution_options={"_sa_orm_load_options": self.load_options},
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/orm/session.py", line 1692, in execute
    result = conn._execute_20(statement, params or {}, execution_options)
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 1614, in _execute_20
    return meth(self, args_10style, kwargs_10style, execution_options)
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/sql/elements.py", line 326, in _execute_on_connection
    self, multiparams, params, execution_options
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 1491, in _execute_clauseelement
    cache_hit=cache_hit,
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 1709, in _execute_context
    e, util.text_type(statement), parameters, None, None
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 2027, in _handle_dbapi_exception
    sqlalchemy_exception, with_traceback=exc_info[2], from_=e
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/util/compat.py", line 207, in raise_
    raise exception
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/base.py", line 1703, in _execute_context
    dialect, self, conn, execution_options, *args, **kw
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/default.py", line 1095, in _init_compiled
    for key in compiled_params
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/engine/default.py", line 1095, in <dictcomp>
    for key in compiled_params
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/sql/type_api.py", line 1631, in process
    return impl_processor(process_param(value, dialect))
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/types.py", line 59, in process_bind_param
    return uuid.UUID(value).bytes
  File "/usr/lib64/python3.6/uuid.py", line 140, in __init__
    raise ValueError('badly formed hexadecimal UUID string')
```
Modification
------------




Changed files:
lib/rucio/tests/test_rule.py, lib/rucio/core/rule.py

diff --git a/lib/rucio/core/rule.py b/lib/rucio/core/rule.py
index e69c9b3c7..57093529f 100644
--- a/lib/rucio/core/rule.py
+++ b/lib/rucio/core/rule.py
@@ -1450,6 +1450,8 @@ def move_rule(rule_id: str, rse_expression: str, override: Optional[Dict[str, An
 
         return new_rule_id[0]
 
+    except StatementError:
+        raise RucioException('Badly formatted rule id (%s)' % (rule_id))
     except NoResultFound:
         raise RuleNotFound('No rule with the id %s found' % (rule_id))
 
diff --git a/lib/rucio/tests/test_rule.py b/lib/rucio/tests/test_rule.py
index 6c902ddf5..b29172225 100644
--- a/lib/rucio/tests/test_rule.py
+++ b/lib/rucio/tests/test_rule.py
@@ -24,7 +24,7 @@ import rucio.api.rule
 from rucio.api.account import add_account
 from rucio.client.ruleclient import RuleClient
 from rucio.common.config import config_get_bool
-from rucio.common.exception import (RuleNotFound, AccessDenied, InsufficientAccountLimit, DuplicateRule, RSEWriteBlocked,
+from rucio.common.exception import (RucioException, RuleNotFound, AccessDenied, InsufficientAccountLimit, DuplicateRule, RSEWriteBlocked,
                                     RSEOverQuota, RuleReplaceFailed, ManualRuleApprovalBlocked, InputValidationError,
                                     UnsupportedOperation, InvalidValueForKey)
 from rucio.common.policy import get_policy
@@ -864,6 +864,7 @@ class TestCore:
         assert(get_rule(rule_id2)['source_replica_expression'] == source_replica_expression)
 
         pytest.raises(RuleReplaceFailed, move_rule, rule_id, self.rse3)
+        pytest.raises(RucioException, move_rule, 'foo', self.rse3)
 
     def test_add_rule_with_scratchdisk(self, vo, mock_scope, did_factory, jdoe_account):
         """ REPLICATION RULE (CORE): Add a replication rule for scratchdisk"""