Repository: rucio/rucio

Issue:
Define group/LFN correspondence check
### Description

We need to add additional checks for `group` scopes and lfns similar to what is done for `user` scopes. 

https://github.com/rucio/rucio/blob/9ae42908a0e188ece017bfec1b89cf2e354b383e/lib/rucio/common/schema/cms.py#L443-#L465

### Motivation

_No response_

### Change

_No response_


Discussion:
Hi @bari12
I think a `CMS` tag would be more appropriate for the issue.
Or is it ok if I use the `enhancement` component in my commit message?

Resolving patch:
diff --git a/lib/rucio/common/schema/cms.py b/lib/rucio/common/schema/cms.py
--- a/lib/rucio/common/schema/cms.py
+++ b/lib/rucio/common/schema/cms.py
@@ -445,21 +445,34 @@ def validate_cms_did(obj):
     Special checking for DIDs
     Most of the checking is done with JSON schema, but this check
     makes sure user LFNs are in the correct /store/user/rucio/USERNAME namespace
+    makes sure group LFNs are in the correct /store/group/rucio/GROUPNAME namespace
     """
     if not obj:
         return
 
-    lfn = obj['name']
     did_type = obj['type']
+    lfn = obj['name']
     scope = obj['scope']
-    if scope.startswith('user.') and did_type == 'FILE':
-        _, user = scope.split('.', 1)
-        if not lfn.startswith('/store/user/rucio/%s/' % user):
-            raise InvalidObject("Problem with LFN %(lfn)s : Not allowed for user %(user)s" % locals())
-    if lfn.startswith('/store/user') and not lfn.startswith('/store/user/rucio/'):
-        raise InvalidObject("Problem with LFN %(lfn)s : Legacy user files are not managed with Rucio")
-    if lfn.startswith('/store/user/rucio') and not scope.startswith('user.'):
-        raise InvalidObject("Problem with LFN %(lfn)s : Only user scopes allowed in /store/user/rucio")
-    if scope == 'logs' and did_type == 'FILE':
+
+    if did_type != "FILE":
+        return
+
+    verify_scope_lfn_match(lfn, scope, "user")
+    verify_scope_lfn_match(lfn, scope, "group")
+
+    if scope == 'logs':
         if not lfn.startswith('/store/logs/'):
             raise InvalidObject("Problem with LFN %(lfn)s : Logs must start with /store/logs" % locals())
+
+
+def verify_scope_lfn_match(lfn, scope, scope_type):
+    if lfn.startswith(f'/store/{scope_type}') and not lfn.startswith(f'/store/{scope_type}/rucio/'):
+        raise InvalidObject(f"Problem with LFN {lfn} : Legacy {scope_type} files are not managed with Rucio")
+
+    if lfn.startswith(f'/store/{scope_type}/rucio') and not scope.startswith(f'{scope_type}.'):
+        raise InvalidObject(f"Problem with LFN {lfn}: Only {scope_type} scopes allowed in /store/{scope_type}/rucio")
+
+    if scope.startswith(f'{scope_type}.'):
+        _, account = scope.split('.', 1)
+        if not lfn.startswith(f'/store/{scope_type}/rucio/{account}/'):
+            raise InvalidObject(f"Problem with LFN {lfn} : Not allowed for {scope_type} {account}")


Test patch:
diff --git a/lib/rucio/tests/test_schema_cms.py b/lib/rucio/tests/test_schema_cms.py
--- a/lib/rucio/tests/test_schema_cms.py
+++ b/lib/rucio/tests/test_schema_cms.py
@@ -60,7 +60,6 @@ def test_dids(self):
             '/store/temp/user/ewv/Higgs-123/PrivateSample/v1/1000/a_X-2.root',
             '/store/temp/user/cinquilli.nocern/Higgs-123/PrivateSample/v1/1000/a_X-2.root',
             '/store/temp/group/Exotica/Higgs-123/PrivateSample/v1/1000/a_X-2.root',
-            '/store/group/Exotica/Higgs-123/PrivateSample/v1/1000/a_X-2.root',
             '/store/temp1/acquisition_10-A/MuElectron-10_100/RAW-RECO/vX-1/1000/a_X-2.root',
             '/store/temp/lustre1/acquisition_10-A/MuElectron-10_100/RAW-RECO/vX-1/1000/a_X-2.root',
             '/store/backfill/1/acquisition_10-A/MuElectron-10_100/RAW-RECO/vX-1/1000/a_X-2.root',
@@ -79,10 +78,6 @@ def test_dids(self):
             '/store/backfill/1/Run2012B/Cosmics/RAW-RECO/PromptSkim-v1/000/194/912/00000/F65F4AFE-14AC-DF11-B3BE-00215E21F32E.root',
             '/store/results/qcd/QCD_Pt80/StoreResults-Summer09-MC_31X_V3_7TeV-Jet30U-JetAODSkim-0a98be42532eba1f0545cc9b086ec3c3/QCD_Pt80/USER/StoreResults-Summer09-MC_31X_V3_7TeV-Jet30U-JetAODSkim-0a98be42532eba1f0545cc9b086ec3c3/0000/C44630AC-C0C7-DE11-AD4E-0019B9CAC0F8.root',  # noqa: E501
             '/store/results/qcd/StoreResults/QCD_Pt_40_2017_14TeV_612_SLHC6_patch1/USER/QCD_Pt_40_2017_14TeV_612_SLHC6_patch1_6be6d116203e430d91d7e1d6d9a88cd7-v1/00000/028DDC2A-63A8-E311-BB40-842B2B5546DE.root',
-            '/store/group/higgs/SDMu9_Zmumu/Zmumu/OctX_HZZ3lepSkim_SDMu9/1eb161a436e69f7af28d18145e4ce909/3lepSkim_SDMu9_1.root',
-            '/store/group/e-gamma_ecal/SDMu9_Zmumu/Zmumu/OctX_HZZ3lepSkim_SDMu9/1eb161a436e69f7af28d18145e4ce909/3lepSkim_SDMu9_1.root',
-            '/store/group/B2G/SDMu9_Zmumu/Zmumu/OctX_HZZ3lepSkim_SDMu9/1eb161a436e69f7af28d18145e4ce909/3lepSkim_SDMu9_1.root',
-            '/store/group/phys_higgs/meridian/HGGProd/GluGluToHToGG_M-125_8TeV-powheg-pythia6-Summer12-START53_V7D-v2/meridian/GluGlu_HToGG_M-125_8TeV-powheg-LHE_v1/GluGluToHToGG_M-125_8TeV-powheg-pythia6-Summer12-START53_V7D-v2/fb576e5b6a5810681def50b608ec31ad/Hadronizer_TuneZ2star_8TeV_Powheg_pythia_tauola_cff_py_GEN_SIM_DIGI_L1_DIGI2RAW_RAW2DIGI_L1Reco_RECO_PU_1_1_ukQ.root',  # noqa: E501
             '/store/lhe/7365/TprimeTprimeToTHTH_M-400_TuneZ2star_8TeV-madgraph_50219221.lhe',
             '/store/lhe/10860/LQToUE_BetaHalf_vector_YM-MLQ300LG0KG0.lhe.xz',
             '/store/lhe/7365/mysecondary/0001/TprimeTprimeToTHTH_M-400_TuneZ2star_8TeV-madgraph_50219221.lhe',
@@ -105,6 +100,11 @@ def test_dids(self):
             '/store/lhe/10860/11%11/1111/LQToUE_BetaHalf_vector_YM-MLQ300LG0KG0.lhe.xz',
             '/store/user/ewv/Higgs-123/PrivateSample/v1/1000/a_X-2.root',
             '/store/user/cinquilli.nocern/Higgs-123/PrivateSample/v1/1000/a_X-2.root',
+            '/store/group/higgs/SDMu9_Zmumu/Zmumu/OctX_HZZ3lepSkim_SDMu9/1eb161a436e69f7af28d18145e4ce909/3lepSkim_SDMu9_1.root',
+            '/store/group/e-gamma_ecal/SDMu9_Zmumu/Zmumu/OctX_HZZ3lepSkim_SDMu9/1eb161a436e69f7af28d18145e4ce909/3lepSkim_SDMu9_1.root',
+            '/store/group/B2G/SDMu9_Zmumu/Zmumu/OctX_HZZ3lepSkim_SDMu9/1eb161a436e69f7af28d18145e4ce909/3lepSkim_SDMu9_1.root',
+            '/store/group/phys_higgs/meridian/HGGProd/GluGluToHToGG_M-125_8TeV-powheg-pythia6-Summer12-START53_V7D-v2/meridian/GluGlu_HToGG_M-125_8TeV-powheg-LHE_v1/GluGluToHToGG_M-125_8TeV-powheg-pythia6-Summer12-START53_V7D-v2/fb576e5b6a5810681def50b608ec31ad/Hadronizer_TuneZ2star_8TeV_Powheg_pythia_tauola_cff_py_GEN_SIM_DIGI_L1_DIGI2RAW_RAW2DIGI_L1Reco_RECO_PU_1_1_ukQ.root',  # noqa: E501
+            '/store/group/Exotica/Higgs-123/PrivateSample/v1/1000/a_X-2.root',
         ]
 
         # Good datasets pass for both scopes