Skip to content

Commit 7fc879e

Browse files
marianne013sfayerfstagni
committed
feat: Add finer permission model for Transformation System
Co-authored-by: Simon Fayer <[email protected]> Co-authored-by: fstagni <[email protected]>
1 parent 15575a7 commit 7fc879e

File tree

9 files changed

+273
-56
lines changed

9 files changed

+273
-56
lines changed

docs/source/AdministratorGuide/Configuration/ConfReference/Tips/Authorization/index.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ are showed in the next table:
3535
+----------------------------+------------------------------------------------------------------+-------------+
3636
| *PrivateLimitedDelegation* | Allow getting only limited proxies for one self | |
3737
+----------------------------+------------------------------------------------------------------+-------------+
38-
| *ProductionManagement* | Allow managing production | |
38+
| *ProductionManagement* | Allow managing all productions | |
39+
+----------------------------+------------------------------------------------------------------+-------------+
40+
| *ProductionSharing* | Allow managing productions owned by the same group | |
41+
+----------------------------+------------------------------------------------------------------+-------------+
42+
| *ProductionUser* | Allow managing productions owned by the same user | |
3943
+----------------------------+------------------------------------------------------------------+-------------+
4044
| *ProxyManagement* | Allow managing proxies | |
4145
+----------------------------+------------------------------------------------------------------+-------------+

docs/source/AdministratorGuide/Tutorials/installTS.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ Using the ``Configuration Manager`` application in the WebApp, create a new sect
9292
After restarting the ``ProxyManager``, you should now be able to get a proxy belonging to the ``dirac_prod`` group that
9393
will be automatically uploaded.
9494

95+
The ``ProductionManagement`` property allows users in the group to access and change all transformations. There is also
96+
a ``ProductionSharing`` property to only allow access to transformations in the same group and ``ProductionUser`` to
97+
only allow users to access their own transformations.
98+
9599
Add a ProdManager Shifter
96100
=========================
97101

src/DIRAC/Core/Security/Properties.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,12 @@ class SecurityProperty(str, Enum):
4444
PRIVATE_LIMITED_DELEGATION = "PrivateLimitedDelegation"
4545
#: Allow managing proxies
4646
PROXY_MANAGEMENT = "ProxyManagement"
47-
#: Allow managing production
47+
#: Allow managing all productions
4848
PRODUCTION_MANAGEMENT = "ProductionManagement"
49+
#: Allow managing all productions in the same group
50+
PRODUCTION_SHARING = "ProductionSharing"
51+
#: Allows user to manage productions they own only
52+
PRODUCTION_USER = "ProductionUser"
4953
#: Allow production request approval on behalf of PPG
5054
PPG_AUTHORITY = "PPGAuthority"
5155
#: Allow Bookkeeping Management

src/DIRAC/FrameworkSystem/Client/ComponentInstaller.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
OPERATOR,
9494
NORMAL_USER,
9595
TRUSTED_HOST,
96+
PRODUCTION_MANAGEMENT,
9697
)
9798

9899
from DIRAC.ConfigurationSystem.Client import PathFinder
@@ -447,6 +448,7 @@ def _getCentralCfg(self, installCfg):
447448
FULL_DELEGATION,
448449
PROXY_MANAGEMENT,
449450
OPERATOR,
451+
PRODUCTION_MANAGEMENT,
450452
]
451453

452454
for section in (

src/DIRAC/TransformationSystem/Service/TransformationManagerHandler.py

Lines changed: 118 additions & 51 deletions
Large diffs are not rendered by default.

src/DIRAC/TransformationSystem/Utilities/ReplicationCLIParameters.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Command Line Parameters for creating the Replication transformations Script
33
"""
44
from DIRAC import S_OK, S_ERROR, gLogger
5+
from DIRAC.Core.Security.Properties import SecurityProperty
56
from DIRAC.Core.Security.ProxyInfo import getProxyInfo
67
from DIRAC.ConfigurationSystem.Client.Helpers.Registry import getVOMSVOForGroup
78

@@ -151,9 +152,13 @@ def _checkProxy(self):
151152
groupProperties = proxyValues.get("groupProperties", [])
152153

153154
if groupProperties:
154-
if "ProductionManagement" not in groupProperties:
155+
if (
156+
SecurityProperty.PRODUCTION_MANAGEMENT not in groupProperties
157+
and SecurityProperty.PRODUCTION_SHARING not in groupProperties
158+
and SecurityProperty.PRODUCTION_USER not in groupProperties
159+
):
155160
self.errorMessages.append(
156-
"ERROR: Not allowed to create production, you need a ProductionManagement proxy."
161+
"ERROR: Not allowed to create production, you need one of the Production properties."
157162
)
158163
return False
159164
else:
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#!/usr/bin/env python
2+
""" This tests the TransformationManager checkPermission function
3+
"""
4+
import unittest
5+
import functools
6+
import sys
7+
8+
import DIRAC
9+
from DIRAC import S_OK, S_ERROR
10+
11+
DIRAC.initialize() # Initialize configuration
12+
13+
from DIRAC.TransformationSystem.Service.TransformationManagerHandler import TransformationManagerHandlerMixin
14+
15+
16+
class TSCheckPermTestCase(unittest.TestCase):
17+
TEST_USERS = {
18+
# Users a1/a2 are just regular tf users with no sharing
19+
# a1 & a2 are in the same group
20+
"user_a1": {"DN": "/CN=user_a1", "group": "group_a", "properties": ["ProductionUser"]},
21+
"user_a2": {"DN": "/CN=user_a2", "group": "group_a", "properties": ["ProductionUser"]},
22+
# Users b1/b2 have production sharing for their group
23+
# b1 & b2 are in the same group
24+
"user_b1": {"DN": "/CN=user_b1", "group": "group_b", "properties": ["ProductionSharing"]},
25+
"user_b2": {"DN": "/CN=user_b2", "group": "group_b", "properties": ["ProductionSharing"]},
26+
# Admin user has full ProductionManagement permission
27+
"admin": {"DN": "/CN=dirac_admin", "group": "dirac_admin", "properties": ["ProductionManagement"]},
28+
# Vistor has no tf permissions at all
29+
"visitor": {"DN": "/CN=none", "group": "visitors", "properties": []},
30+
}
31+
TEST_TRANSFORMATIONS = {
32+
# Each transformation is owned by the matching user
33+
# e.g. user_a1 owns TF_A1 and so forth...
34+
"TF_A1": {"AuthorDN": "/CN=user_a1", "AuthorGroup": "group_a"},
35+
"TF_A2": {"AuthorDN": "/CN=user_a2", "AuthorGroup": "group_a"},
36+
"TF_B1": {"AuthorDN": "/CN=user_b1", "AuthorGroup": "group_b"},
37+
"TF_B2": {"AuthorDN": "/CN=user_b2", "AuthorGroup": "group_b"},
38+
"TF_C1": {"AuthorDN": "/CN=user_c1", "AuthorGroup": "group_c"},
39+
}
40+
TEST_PERM_MATRIX = (
41+
# User a1 should only be able to access their own tf
42+
("user_a1", "TF_A1", True),
43+
("user_a1", "TF_A2", False),
44+
("user_a1", "TF_B1", False),
45+
("user_a1", "TF_B2", False),
46+
("user_a1", "TF_C1", False),
47+
# User a2 should only be able to access their own tf
48+
("user_a2", "TF_A1", False),
49+
("user_a2", "TF_A2", True),
50+
("user_a2", "TF_B1", False),
51+
("user_a2", "TF_B2", False),
52+
("user_a2", "TF_C1", False),
53+
# User b1 should have access to all in the B group
54+
("user_b1", "TF_A1", False),
55+
("user_b1", "TF_A2", False),
56+
("user_b1", "TF_B1", True),
57+
("user_b1", "TF_B2", True),
58+
("user_b1", "TF_C1", False),
59+
# User b2 should have access to all in the B group
60+
("user_b2", "TF_A1", False),
61+
("user_b2", "TF_A2", False),
62+
("user_b2", "TF_B1", True),
63+
("user_b2", "TF_B2", True),
64+
("user_b2", "TF_C1", False),
65+
# Admin should have access to all
66+
("admin", "TF_A1", True),
67+
("admin", "TF_A2", True),
68+
("admin", "TF_B1", True),
69+
("admin", "TF_B2", True),
70+
("admin", "TF_C1", True),
71+
# Vistor should have access to none
72+
("visitor", "TF_A1", False),
73+
("visitor", "TF_A2", False),
74+
("visitor", "TF_B1", False),
75+
("visitor", "TF_B2", False),
76+
("visitor", "TF_C1", False),
77+
# Only admin should have access to a non-existant tfName
78+
# (as admin is a short-circuit check that doesn't actually go to the DB)
79+
("user_a1", "TF_ZZ", False),
80+
("user_a2", "TF_ZZ", False),
81+
("user_b1", "TF_ZZ", False),
82+
("user_b2", "TF_ZZ", False),
83+
("admin", "TF_ZZ", True),
84+
("visitor", "TF_ZZ", False),
85+
)
86+
87+
class _mockTFDatabase:
88+
"""A class that mocks up the Transformation database."""
89+
90+
def getTransformation(self, tfName):
91+
if tfName in TSCheckPermTestCase.TEST_TRANSFORMATIONS:
92+
return S_OK(TSCheckPermTestCase.TEST_TRANSFORMATIONS[tfName])
93+
return S_ERROR(f"Failed to find transformation {tfName}")
94+
95+
def _mockGetRemoteCredentials(self, tfmHandler):
96+
"""Mocks up get remote credentails based on the current user."""
97+
return self.currentUser
98+
99+
def _setUser(self, user):
100+
"""Sets the current user for the mocked getRemoteCredentials function from the test dict."""
101+
if user in self.TEST_USERS:
102+
self.currentUser = self.TEST_USERS[user]
103+
return
104+
self.currentUser = self.TEST_USERS["visitor"]
105+
106+
def setUp(self):
107+
"""Create TransformationManagerHandlerMixin and then replace the key functions directly
108+
with the mocked up ones.
109+
"""
110+
self.handler = TransformationManagerHandlerMixin()
111+
self.handler.getRemoteCredentials = functools.partial(self._mockGetRemoteCredentials, self)
112+
self.handler.transformationDB = self._mockTFDatabase()
113+
self._setUser(None)
114+
115+
def test_checkPerms(self):
116+
for user, tfName, expRes in self.TEST_PERM_MATRIX:
117+
self._setUser(user)
118+
res = self.handler.checkPermissions(tfName)
119+
self.assertEqual(
120+
expRes, res["OK"], f"User {user} access to tf {tfName}: {res['OK']} but expected {expRes}."
121+
)
122+
123+
124+
if __name__ == "__main__":
125+
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TSCheckPermTestCase)
126+
testResult = unittest.TextTestRunner(verbosity=2).run(suite)
127+
sys.exit(not testResult.wasSuccessful())

tests/Integration/all_integration_server_tests.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ echo -e "*** $(date -u) **** MONITORING TESTS ****\n"
3737
pytest --no-check-dirac-environment "${THIS_DIR}/Monitoring/Test_MonitoringReporter.py" |& tee -a "${SERVER_TEST_OUTPUT}"; (( ERR |= "${?}" ))
3838
pytest --no-check-dirac-environment "${THIS_DIR}/Monitoring/Test_MonitoringDB.py" |& tee -a "${SERVER_TEST_OUTPUT}"; (( ERR |= "${?}" ))
3939

40+
#-------------------------------------------------------------------------------#
41+
echo -e "*** $(date -u) **** TS TESTS ****\n"
42+
pytest --no-check-dirac-environment "${THIS_DIR}/TransformationSystem/Test_TS_CheckPerms.py" |& tee -a "${SERVER_TEST_OUTPUT}"; (( ERR |= "${?}" ))
43+
4044
#-------------------------------------------------------------------------------#
4145
echo -e "*** $(date -u) **** RSS TESTS ****\n"
4246
pytest --no-check-dirac-environment "${THIS_DIR}/ResourceStatusSystem/Test_FullChain.py" |& tee -a "${SERVER_TEST_OUTPUT}"; (( ERR |= "${?}" ))

tests/Jenkins/utilities.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ diracUserAndGroup() {
562562
exit 1
563563
fi
564564

565-
if ! dirac-admin-add-group -G prod -U adminusername,ciuser,trialUser -P Operator,FullDelegation,ProxyManagement,ServiceAdministrator,JobAdministrator,CSAdministrator,AlarmsManagement,FileCatalogManagement,SiteManager,NormalUser,VmRpcOperation "${DEBUG}"; then
565+
if ! dirac-admin-add-group -G prod -U adminusername,ciuser,trialUser -P Operator,FullDelegation,ProxyManagement,ServiceAdministrator,JobAdministrator,CSAdministrator,AlarmsManagement,FileCatalogManagement,SiteManager,NormalUser,VmRpcOperation,ProductionManagement "${DEBUG}"; then
566566
echo 'ERROR: dirac-admin-add-group failed' >&2
567567
exit 1
568568
fi

0 commit comments

Comments
 (0)