rucio 35.7.0__py3-none-any.whl → 37.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rucio might be problematic. Click here for more details.

Files changed (266) hide show
  1. rucio/alembicrevision.py +1 -1
  2. rucio/{daemons/c3po/collectors → cli}/__init__.py +1 -0
  3. rucio/cli/account.py +216 -0
  4. rucio-35.7.0.data/scripts/rucio → rucio/cli/bin_legacy/rucio.py +769 -486
  5. rucio-35.7.0.data/scripts/rucio-admin → rucio/cli/bin_legacy/rucio_admin.py +476 -423
  6. rucio/cli/command.py +272 -0
  7. rucio/cli/config.py +72 -0
  8. rucio/cli/did.py +191 -0
  9. rucio/cli/download.py +128 -0
  10. rucio/cli/lifetime_exception.py +33 -0
  11. rucio/cli/replica.py +162 -0
  12. rucio/cli/rse.py +293 -0
  13. rucio/cli/rule.py +158 -0
  14. rucio/cli/scope.py +40 -0
  15. rucio/cli/subscription.py +73 -0
  16. rucio/cli/upload.py +60 -0
  17. rucio/cli/utils.py +226 -0
  18. rucio/client/accountclient.py +0 -1
  19. rucio/client/baseclient.py +33 -24
  20. rucio/client/client.py +45 -1
  21. rucio/client/didclient.py +5 -3
  22. rucio/client/downloadclient.py +6 -8
  23. rucio/client/replicaclient.py +0 -2
  24. rucio/client/richclient.py +317 -0
  25. rucio/client/rseclient.py +4 -4
  26. rucio/client/uploadclient.py +26 -12
  27. rucio/common/bittorrent.py +234 -0
  28. rucio/common/cache.py +66 -29
  29. rucio/common/checksum.py +168 -0
  30. rucio/common/client.py +122 -0
  31. rucio/common/config.py +22 -35
  32. rucio/common/constants.py +61 -3
  33. rucio/common/didtype.py +72 -24
  34. rucio/common/dumper/__init__.py +45 -38
  35. rucio/common/dumper/consistency.py +75 -30
  36. rucio/common/dumper/data_models.py +63 -19
  37. rucio/common/dumper/path_parsing.py +19 -8
  38. rucio/common/exception.py +65 -8
  39. rucio/common/extra.py +5 -10
  40. rucio/common/logging.py +13 -13
  41. rucio/common/pcache.py +8 -7
  42. rucio/common/plugins.py +59 -27
  43. rucio/common/policy.py +12 -3
  44. rucio/common/schema/__init__.py +84 -34
  45. rucio/common/schema/generic.py +0 -17
  46. rucio/common/schema/generic_multi_vo.py +0 -17
  47. rucio/common/test_rucio_server.py +12 -6
  48. rucio/common/types.py +132 -52
  49. rucio/common/utils.py +93 -643
  50. rucio/core/account_limit.py +14 -12
  51. rucio/core/authentication.py +2 -2
  52. rucio/core/config.py +23 -42
  53. rucio/core/credential.py +14 -15
  54. rucio/core/did.py +5 -1
  55. rucio/core/did_meta_plugins/elasticsearch_meta.py +407 -0
  56. rucio/core/did_meta_plugins/filter_engine.py +62 -3
  57. rucio/core/did_meta_plugins/json_meta.py +2 -2
  58. rucio/core/did_meta_plugins/mongo_meta.py +43 -30
  59. rucio/core/did_meta_plugins/postgres_meta.py +75 -39
  60. rucio/core/identity.py +6 -5
  61. rucio/core/importer.py +4 -3
  62. rucio/core/lifetime_exception.py +2 -2
  63. rucio/core/lock.py +8 -7
  64. rucio/core/message.py +6 -0
  65. rucio/core/monitor.py +30 -29
  66. rucio/core/naming_convention.py +2 -2
  67. rucio/core/nongrid_trace.py +2 -2
  68. rucio/core/oidc.py +11 -9
  69. rucio/core/permission/__init__.py +79 -37
  70. rucio/core/permission/generic.py +1 -7
  71. rucio/core/permission/generic_multi_vo.py +1 -7
  72. rucio/core/quarantined_replica.py +4 -3
  73. rucio/core/replica.py +464 -139
  74. rucio/core/replica_sorter.py +55 -59
  75. rucio/core/request.py +34 -32
  76. rucio/core/rse.py +301 -97
  77. rucio/core/rse_counter.py +1 -2
  78. rucio/core/rse_expression_parser.py +7 -7
  79. rucio/core/rse_selector.py +9 -7
  80. rucio/core/rule.py +41 -40
  81. rucio/core/rule_grouping.py +42 -40
  82. rucio/core/scope.py +5 -4
  83. rucio/core/subscription.py +26 -28
  84. rucio/core/topology.py +11 -11
  85. rucio/core/trace.py +2 -2
  86. rucio/core/transfer.py +29 -15
  87. rucio/core/volatile_replica.py +4 -3
  88. rucio/daemons/atropos/atropos.py +1 -1
  89. rucio/daemons/auditor/__init__.py +2 -2
  90. rucio/daemons/auditor/srmdumps.py +6 -6
  91. rucio/daemons/automatix/automatix.py +32 -21
  92. rucio/daemons/badreplicas/necromancer.py +2 -2
  93. rucio/daemons/bb8/nuclei_background_rebalance.py +1 -1
  94. rucio/daemons/bb8/t2_background_rebalance.py +1 -1
  95. rucio/daemons/common.py +15 -25
  96. rucio/daemons/conveyor/finisher.py +2 -2
  97. rucio/daemons/conveyor/poller.py +18 -28
  98. rucio/daemons/conveyor/receiver.py +2 -2
  99. rucio/daemons/conveyor/stager.py +1 -0
  100. rucio/daemons/conveyor/submitter.py +3 -3
  101. rucio/daemons/hermes/hermes.py +91 -30
  102. rucio/daemons/judge/evaluator.py +2 -2
  103. rucio/daemons/oauthmanager/oauthmanager.py +3 -3
  104. rucio/daemons/reaper/dark_reaper.py +7 -3
  105. rucio/daemons/reaper/reaper.py +12 -16
  106. rucio/daemons/rsedecommissioner/config.py +1 -1
  107. rucio/daemons/rsedecommissioner/profiles/generic.py +5 -4
  108. rucio/daemons/rsedecommissioner/profiles/types.py +7 -6
  109. rucio/daemons/rsedecommissioner/rse_decommissioner.py +1 -1
  110. rucio/daemons/storage/consistency/actions.py +8 -6
  111. rucio/daemons/tracer/kronos.py +4 -4
  112. rucio/db/sqla/constants.py +5 -0
  113. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +4 -4
  114. rucio/db/sqla/migrate_repo/versions/30d5206e9cad_increase_oauthrequest_redirect_msg_.py +37 -0
  115. rucio/db/sqla/models.py +157 -154
  116. rucio/db/sqla/session.py +58 -27
  117. rucio/db/sqla/types.py +2 -2
  118. rucio/db/sqla/util.py +2 -2
  119. rucio/gateway/account.py +18 -12
  120. rucio/gateway/account_limit.py +137 -60
  121. rucio/gateway/authentication.py +18 -12
  122. rucio/gateway/config.py +30 -20
  123. rucio/gateway/credential.py +9 -10
  124. rucio/gateway/did.py +70 -53
  125. rucio/gateway/dirac.py +6 -4
  126. rucio/gateway/exporter.py +3 -2
  127. rucio/gateway/heartbeat.py +6 -4
  128. rucio/gateway/identity.py +36 -51
  129. rucio/gateway/importer.py +3 -2
  130. rucio/gateway/lifetime_exception.py +3 -2
  131. rucio/gateway/meta_conventions.py +17 -6
  132. rucio/gateway/permission.py +4 -1
  133. rucio/gateway/quarantined_replica.py +3 -2
  134. rucio/gateway/replica.py +31 -22
  135. rucio/gateway/request.py +27 -18
  136. rucio/gateway/rse.py +69 -37
  137. rucio/gateway/rule.py +46 -26
  138. rucio/gateway/scope.py +3 -2
  139. rucio/gateway/subscription.py +14 -11
  140. rucio/gateway/vo.py +12 -8
  141. rucio/rse/__init__.py +3 -3
  142. rucio/rse/protocols/bittorrent.py +11 -1
  143. rucio/rse/protocols/cache.py +0 -11
  144. rucio/rse/protocols/dummy.py +0 -11
  145. rucio/rse/protocols/gfal.py +14 -9
  146. rucio/rse/protocols/globus.py +1 -1
  147. rucio/rse/protocols/http_cache.py +1 -1
  148. rucio/rse/protocols/posix.py +2 -2
  149. rucio/rse/protocols/protocol.py +84 -317
  150. rucio/rse/protocols/rclone.py +2 -1
  151. rucio/rse/protocols/rfio.py +10 -1
  152. rucio/rse/protocols/ssh.py +2 -1
  153. rucio/rse/protocols/storm.py +2 -13
  154. rucio/rse/protocols/webdav.py +74 -30
  155. rucio/rse/protocols/xrootd.py +2 -1
  156. rucio/rse/rsemanager.py +170 -53
  157. rucio/rse/translation.py +260 -0
  158. rucio/tests/common.py +23 -13
  159. rucio/tests/common_server.py +26 -9
  160. rucio/transfertool/bittorrent.py +15 -14
  161. rucio/transfertool/bittorrent_driver.py +5 -7
  162. rucio/transfertool/bittorrent_driver_qbittorrent.py +9 -8
  163. rucio/transfertool/fts3.py +20 -16
  164. rucio/transfertool/mock.py +2 -3
  165. rucio/vcsversion.py +4 -4
  166. rucio/version.py +7 -0
  167. rucio/web/rest/flaskapi/v1/accounts.py +17 -3
  168. rucio/web/rest/flaskapi/v1/auth.py +5 -5
  169. rucio/web/rest/flaskapi/v1/credentials.py +3 -2
  170. rucio/web/rest/flaskapi/v1/dids.py +21 -15
  171. rucio/web/rest/flaskapi/v1/identities.py +33 -9
  172. rucio/web/rest/flaskapi/v1/redirect.py +5 -4
  173. rucio/web/rest/flaskapi/v1/replicas.py +12 -8
  174. rucio/web/rest/flaskapi/v1/rses.py +15 -4
  175. rucio/web/rest/flaskapi/v1/traces.py +56 -19
  176. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/alembic.ini.template +1 -1
  177. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/alembic_offline.ini.template +1 -1
  178. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +3 -2
  179. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rucio.cfg.template +3 -19
  180. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +1 -18
  181. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/requirements.server.txt +97 -68
  182. rucio-37.0.0.data/scripts/rucio +133 -0
  183. rucio-37.0.0.data/scripts/rucio-admin +97 -0
  184. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-atropos +2 -2
  185. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-auditor +2 -1
  186. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-automatix +2 -2
  187. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-cache-client +17 -10
  188. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-receiver +1 -0
  189. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-kronos +1 -0
  190. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-minos +2 -2
  191. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-minos-temporary-expiration +2 -2
  192. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-necromancer +2 -2
  193. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-reaper +6 -6
  194. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-transmogrifier +2 -2
  195. rucio-37.0.0.dist-info/METADATA +92 -0
  196. {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/RECORD +237 -243
  197. {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/licenses/AUTHORS.rst +3 -0
  198. rucio/common/schema/atlas.py +0 -413
  199. rucio/common/schema/belleii.py +0 -408
  200. rucio/common/schema/domatpc.py +0 -401
  201. rucio/common/schema/escape.py +0 -426
  202. rucio/common/schema/icecube.py +0 -406
  203. rucio/core/permission/atlas.py +0 -1348
  204. rucio/core/permission/belleii.py +0 -1077
  205. rucio/core/permission/escape.py +0 -1078
  206. rucio/daemons/c3po/algorithms/__init__.py +0 -13
  207. rucio/daemons/c3po/algorithms/simple.py +0 -134
  208. rucio/daemons/c3po/algorithms/t2_free_space.py +0 -128
  209. rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +0 -130
  210. rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +0 -294
  211. rucio/daemons/c3po/c3po.py +0 -371
  212. rucio/daemons/c3po/collectors/agis.py +0 -108
  213. rucio/daemons/c3po/collectors/free_space.py +0 -81
  214. rucio/daemons/c3po/collectors/jedi_did.py +0 -57
  215. rucio/daemons/c3po/collectors/mock_did.py +0 -51
  216. rucio/daemons/c3po/collectors/network_metrics.py +0 -71
  217. rucio/daemons/c3po/collectors/workload.py +0 -112
  218. rucio/daemons/c3po/utils/__init__.py +0 -13
  219. rucio/daemons/c3po/utils/dataset_cache.py +0 -50
  220. rucio/daemons/c3po/utils/expiring_dataset_cache.py +0 -56
  221. rucio/daemons/c3po/utils/expiring_list.py +0 -62
  222. rucio/daemons/c3po/utils/popularity.py +0 -85
  223. rucio/daemons/c3po/utils/timeseries.py +0 -89
  224. rucio/rse/protocols/gsiftp.py +0 -92
  225. rucio-35.7.0.data/scripts/rucio-c3po +0 -85
  226. rucio-35.7.0.dist-info/METADATA +0 -72
  227. /rucio/{daemons/c3po → cli/bin_legacy}/__init__.py +0 -0
  228. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
  229. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/ldap.cfg.template +0 -0
  230. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
  231. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
  232. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
  233. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
  234. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
  235. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
  236. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
  237. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/tools/bootstrap.py +0 -0
  238. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/tools/merge_rucio_configs.py +0 -0
  239. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/tools/reset_database.py +0 -0
  240. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-abacus-account +0 -0
  241. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-abacus-collection-replica +0 -0
  242. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-abacus-rse +0 -0
  243. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-bb8 +0 -0
  244. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-cache-consumer +0 -0
  245. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-finisher +0 -0
  246. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-poller +0 -0
  247. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-preparer +0 -0
  248. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-stager +0 -0
  249. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-submitter +0 -0
  250. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-throttler +0 -0
  251. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-dark-reaper +0 -0
  252. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-dumper +0 -0
  253. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-follower +0 -0
  254. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-hermes +0 -0
  255. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-cleaner +0 -0
  256. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-evaluator +0 -0
  257. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-injector +0 -0
  258. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-repairer +0 -0
  259. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-oauth-manager +0 -0
  260. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-replica-recoverer +0 -0
  261. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-rse-decommissioner +0 -0
  262. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-storage-consistency-actions +0 -0
  263. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-undertaker +0 -0
  264. {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/WHEEL +0 -0
  265. {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/licenses/LICENSE +0 -0
  266. {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/top_level.txt +0 -0
rucio/core/oidc.py CHANGED
@@ -36,9 +36,8 @@ from oic.utils.authn.client import CLIENT_AUTHN_METHOD
36
36
  from sqlalchemy import delete, select, update
37
37
  from sqlalchemy.sql.expression import true
38
38
 
39
- from rucio.common import types
40
- from rucio.common.cache import make_region_memcached
41
- from rucio.common.config import config_get, config_get_int
39
+ from rucio.common.cache import MemcacheRegion
40
+ from rucio.common.config import config_get, config_get_bool, config_get_int
42
41
  from rucio.common.exception import CannotAuthenticate, CannotAuthorize, RucioException
43
42
  from rucio.common.stopwatch import Stopwatch
44
43
  from rucio.common.utils import all_oidc_req_claims_present, build_url, val_to_space_sep_str
@@ -52,12 +51,14 @@ from rucio.db.sqla.session import read_session, transactional_session
52
51
  if TYPE_CHECKING:
53
52
  from sqlalchemy.orm import Session
54
53
 
54
+ from rucio.common.types import InternalAccount
55
+
55
56
  # The WLCG Common JWT Profile dictates that the lifetime of access and ID tokens
56
57
  # should range from five minutes to six hours.
57
58
  TOKEN_MIN_LIFETIME: Final = config_get_int('oidc', 'token_min_lifetime', default=300)
58
59
  TOKEN_MAX_LIFETIME: Final = config_get_int('oidc', 'token_max_lifetime', default=21600)
59
60
 
60
- REGION: Final = make_region_memcached(expiration_time=TOKEN_MAX_LIFETIME)
61
+ REGION: Final = MemcacheRegion(expiration_time=TOKEN_MAX_LIFETIME)
61
62
  METRICS = MetricManager(module=__name__)
62
63
 
63
64
  # worokaround for a bug in pyoidc (as of Dec 2019)
@@ -218,11 +219,11 @@ def __initialize_oidc_clients() -> None:
218
219
  """
219
220
 
220
221
  try:
221
- ALL_OIDC_CLIENTS = __get_rucio_oidc_clients()
222
+ all_oidc_clients = __get_rucio_oidc_clients()
222
223
  global OIDC_CLIENTS
223
224
  global OIDC_ADMIN_CLIENTS
224
- OIDC_CLIENTS = ALL_OIDC_CLIENTS[0]
225
- OIDC_ADMIN_CLIENTS = ALL_OIDC_CLIENTS[1]
225
+ OIDC_CLIENTS = all_oidc_clients[0]
226
+ OIDC_ADMIN_CLIENTS = all_oidc_clients[1]
226
227
  except Exception as error:
227
228
  logging.debug("OIDC clients not properly loaded: %s", error)
228
229
  pass
@@ -297,7 +298,8 @@ def __get_init_oidc_client(token_object: models.Token = None, token_type: str =
297
298
  "state": kwargs.get('state', rndstr()),
298
299
  "nonce": kwargs.get('nonce', rndstr())}
299
300
  auth_args["scope"] = token_object.oidc_scope if token_object else kwargs.get('scope', " ")
300
- auth_args["audience"] = token_object.audience if token_object else kwargs.get('audience', " ")
301
+ if config_get_bool('oidc', 'supports_audience', raise_exception=False, default=True):
302
+ auth_args["audience"] = token_object.audience if token_object else kwargs.get('audience', " ")
301
303
 
302
304
  if token_object:
303
305
  issuer = token_object.identity.split(", ")[1].split("=")[1]
@@ -643,7 +645,7 @@ def get_token_oidc(
643
645
 
644
646
 
645
647
  @transactional_session
646
- def __get_admin_token_oidc(account: types.InternalAccount, req_scope, req_audience, issuer, *, session: "Session"):
648
+ def __get_admin_token_oidc(account: 'InternalAccount', req_scope, req_audience, issuer, *, session: "Session"):
647
649
  """
648
650
  Get a token for Rucio application to act on behalf of itself.
649
651
  client_credential flow is used for this purpose.
@@ -13,12 +13,15 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import importlib
16
+ import logging
16
17
  from configparser import NoOptionError, NoSectionError
17
18
  from os import environ
18
19
  from typing import TYPE_CHECKING, Any
19
20
 
21
+ import rucio.core.permission.generic
20
22
  from rucio.common import config, exception
21
- from rucio.common.utils import check_policy_package_version
23
+ from rucio.common.plugins import check_policy_package_version
24
+ from rucio.common.policy import get_policy
22
25
 
23
26
  if TYPE_CHECKING:
24
27
  from typing import Optional
@@ -27,6 +30,8 @@ if TYPE_CHECKING:
27
30
 
28
31
  from rucio.common.types import InternalAccount
29
32
 
33
+ LOGGER = logging.getLogger('policy')
34
+
30
35
  # dictionary of permission modules for each VO
31
36
  permission_modules = {}
32
37
 
@@ -37,83 +42,120 @@ except (NoOptionError, NoSectionError):
37
42
 
38
43
  # in multi-vo mode packages are loaded on demand when needed
39
44
  if not multivo:
40
- GENERIC_FALLBACK = 'generic'
45
+ generic_fallback = 'generic'
41
46
 
42
- if config.config_has_section('permission'):
43
- try:
44
- FALLBACK_POLICY = config.config_get('permission', 'policy')
45
- except (NoOptionError, NoSectionError):
46
- FALLBACK_POLICY = GENERIC_FALLBACK
47
- elif config.config_has_section('policy'):
48
- try:
49
- FALLBACK_POLICY = config.config_get('policy', 'permission')
50
- except (NoOptionError, NoSectionError):
51
- FALLBACK_POLICY = GENERIC_FALLBACK
52
- else:
53
- FALLBACK_POLICY = GENERIC_FALLBACK
47
+ fallback_policy = get_policy()
48
+ if fallback_policy == 'def':
49
+ fallback_policy = generic_fallback
54
50
 
55
51
  if config.config_has_section('policy'):
56
52
  try:
57
53
  if 'RUCIO_POLICY_PACKAGE' in environ:
58
- POLICY = environ['RUCIO_POLICY_PACKAGE']
54
+ policy = environ['RUCIO_POLICY_PACKAGE']
59
55
  else:
60
- POLICY = config.config_get('policy', 'package', check_config_table=False)
61
- check_policy_package_version(POLICY)
62
- POLICY = POLICY + ".permission"
56
+ policy = config.config_get('policy', 'package', check_config_table=False)
57
+ check_policy_package_version(policy)
58
+ policy = policy + ".permission"
63
59
  except (NoOptionError, NoSectionError):
64
60
  # fall back to old system for now
65
- POLICY = 'rucio.core.permission.' + FALLBACK_POLICY.lower()
61
+ policy = 'rucio.core.permission.' + fallback_policy.lower()
66
62
  else:
67
- POLICY = 'rucio.core.permission.' + GENERIC_FALLBACK.lower()
63
+ policy = 'rucio.core.permission.' + generic_fallback.lower()
68
64
 
69
65
  try:
70
- module = importlib.import_module(POLICY)
66
+ module = importlib.import_module(policy)
71
67
  except ModuleNotFoundError:
72
- raise exception.PolicyPackageNotFound(POLICY)
68
+ # if policy package does not contain permission module, load fallback module instead
69
+ # this allows a policy package to omit modules that do not need customisation
70
+ try:
71
+ LOGGER.warning('Unable to load permission module %s from policy package, falling back to %s'
72
+ % (policy, fallback_policy))
73
+ policy = 'rucio.core.permission.' + fallback_policy.lower()
74
+ module = importlib.import_module(policy)
75
+ except ModuleNotFoundError:
76
+ raise exception.PolicyPackageNotFound(policy)
77
+ except ImportError:
78
+ raise exception.ErrorLoadingPolicyPackage(policy)
73
79
  except ImportError:
74
- raise exception.ErrorLoadingPolicyPackage(POLICY)
80
+ raise exception.ErrorLoadingPolicyPackage(policy)
75
81
 
76
82
  permission_modules["def"] = module
77
83
 
78
84
 
79
85
  def load_permission_for_vo(vo: str) -> None:
80
- GENERIC_FALLBACK = 'generic_multi_vo'
86
+ generic_fallback = 'generic_multi_vo'
81
87
  if config.config_has_section('policy'):
82
88
  try:
83
89
  env_name = 'RUCIO_POLICY_PACKAGE_' + vo.upper()
84
90
  if env_name in environ:
85
- POLICY = environ[env_name]
91
+ policy = environ[env_name]
86
92
  else:
87
- POLICY = config.config_get('policy', 'package-' + vo)
88
- check_policy_package_version(POLICY)
89
- POLICY = POLICY + ".permission"
93
+ policy = config.config_get('policy', 'package-' + vo)
94
+ check_policy_package_version(policy)
95
+ policy = policy + ".permission"
90
96
  except (NoOptionError, NoSectionError):
91
97
  # fall back to old system for now
92
98
  try:
93
- POLICY = config.config_get('policy', 'permission')
99
+ policy = config.config_get('policy', 'permission')
94
100
  except (NoOptionError, NoSectionError):
95
- POLICY = GENERIC_FALLBACK
96
- POLICY = 'rucio.core.permission.' + POLICY.lower()
101
+ policy = generic_fallback
102
+ policy = 'rucio.core.permission.' + policy.lower()
97
103
  else:
98
- POLICY = 'rucio.core.permission.' + GENERIC_FALLBACK.lower()
104
+ policy = 'rucio.core.permission.' + generic_fallback.lower()
99
105
 
100
106
  try:
101
- module = importlib.import_module(POLICY)
107
+ module = importlib.import_module(policy)
102
108
  except ModuleNotFoundError:
103
- raise exception.PolicyPackageNotFound(POLICY)
109
+ # if policy package does not contain permission module, load fallback module instead
110
+ # this allows a policy package to omit modules that do not need customisation
111
+ try:
112
+ LOGGER.warning('Unable to load permission module %s from policy package, falling back to %s'
113
+ % (policy, generic_fallback))
114
+ policy = 'rucio.core.permission.' + generic_fallback.lower()
115
+ module = importlib.import_module(policy)
116
+ except ModuleNotFoundError:
117
+ raise exception.PolicyPackageNotFound(policy)
118
+ except ImportError:
119
+ raise exception.ErrorLoadingPolicyPackage(policy)
120
+ raise exception.PolicyPackageNotFound(policy)
104
121
  except ImportError:
105
- raise exception.ErrorLoadingPolicyPackage(POLICY)
122
+ raise exception.ErrorLoadingPolicyPackage(policy)
106
123
 
107
124
  permission_modules[vo] = module
108
125
 
109
126
 
127
+ class PermissionResult:
128
+ """
129
+ Represents the result of a permission check, allowing an optional message to be
130
+ included to give the user more information.
131
+ """
132
+ def __init__(self, allowed: bool, message: "Optional[str]" = "") -> None:
133
+ self.allowed = allowed
134
+ self.message = message
135
+
136
+ # allow this to be tested as a bool for backwards compatibility
137
+ def __bool__(self) -> bool:
138
+ return self.allowed
139
+
140
+
110
141
  def has_permission(
111
142
  issuer: "InternalAccount",
112
143
  action: str,
113
144
  kwargs: dict[str, Any],
114
145
  *,
115
146
  session: "Optional[Session]" = None
116
- ) -> bool:
147
+ ) -> PermissionResult:
117
148
  if issuer.vo not in permission_modules:
118
149
  load_permission_for_vo(issuer.vo)
119
- return permission_modules[issuer.vo].has_permission(issuer, action, kwargs, session=session)
150
+ try:
151
+ result = permission_modules[issuer.vo].has_permission(issuer, action, kwargs, session=session)
152
+ except TypeError:
153
+ # will be thrown if policy package is missing the action in its perm dictionary
154
+ result = None
155
+ # if this permission is missing from the policy package, fallback to generic
156
+ if result is None:
157
+ result = rucio.core.permission.generic.has_permission(issuer, action, kwargs, session=session)
158
+ # continue to support policy packages that just return a boolean and no message
159
+ if isinstance(result, bool):
160
+ result = PermissionResult(result)
161
+ return result
@@ -410,13 +410,7 @@ def perm_add_dids(issuer: "InternalAccount", kwargs: dict[str, Any], *, session:
410
410
  :returns: True if account is allowed, otherwise False
411
411
  """
412
412
  # Check the accounts of the issued rules
413
- if not _is_root(issuer) and not has_account_attribute(account=issuer, key='admin', session=session):
414
- for did in kwargs['dids']:
415
- for rule in did.get('rules', []):
416
- if rule['account'] != issuer:
417
- return False
418
-
419
- return _is_root(issuer) or has_account_attribute(account=issuer, key='admin', session=session)
413
+ return all(perm_add_did(issuer, kwargs=did, session=session) for did in kwargs['dids'])
420
414
 
421
415
 
422
416
  def perm_attach_dids(issuer: "InternalAccount", kwargs: dict[str, Any], *, session: "Optional[Session]" = None) -> bool:
@@ -412,13 +412,7 @@ def perm_add_dids(issuer, kwargs, *, session: "Optional[Session]" = None):
412
412
  :returns: True if account is allowed, otherwise False
413
413
  """
414
414
  # Check the accounts of the issued rules
415
- if not _is_root(issuer) and not has_account_attribute(account=issuer, key='admin', session=session):
416
- for did in kwargs['dids']:
417
- for rule in did.get('rules', []):
418
- if rule['account'] != issuer:
419
- return False
420
-
421
- return _is_root(issuer) or has_account_attribute(account=issuer, key='admin', session=session)
415
+ return all(perm_add_did(issuer, kwargs=did, session=session) for did in kwargs['dids'])
422
416
 
423
417
 
424
418
  def perm_attach_dids(issuer, kwargs, *, session: "Optional[Session]" = None):
@@ -13,7 +13,6 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import datetime
16
- from collections.abc import Iterable
17
16
  from typing import TYPE_CHECKING, Any, Optional
18
17
 
19
18
  from sqlalchemy import and_, delete, or_, select
@@ -24,6 +23,8 @@ from rucio.db.sqla import filter_thread_work, models
24
23
  from rucio.db.sqla.session import read_session, transactional_session
25
24
 
26
25
  if TYPE_CHECKING:
26
+ from collections.abc import Iterable
27
+
27
28
  from sqlalchemy.orm import Session
28
29
 
29
30
 
@@ -88,7 +89,7 @@ def add_quarantined_replicas(rse_id: str, replicas: list[dict[str, Any]], *, ses
88
89
 
89
90
 
90
91
  @transactional_session
91
- def delete_quarantined_replicas(rse_id: str, replicas: Iterable[dict[str, Any]], *, session: "Session") -> None:
92
+ def delete_quarantined_replicas(rse_id: str, replicas: "Iterable[dict[str, Any]]", *, session: "Session") -> None:
92
93
  """
93
94
  Delete file replicas.
94
95
 
@@ -158,7 +159,7 @@ def list_quarantined_replicas(rse_id: str, limit: int, worker_number: Optional[i
158
159
  limit
159
160
  )
160
161
  for path, bytes_, scope, name, created_at in session.execute(stmt).all():
161
- if not (scope, name) in quarantined_replicas:
162
+ if (scope, name) not in quarantined_replicas:
162
163
  quarantined_replicas[(scope, name)] = []
163
164
  replicas_clause.append(and_(models.RSEFileAssociation.scope == scope,
164
165
  models.RSEFileAssociation.name == name))