diff --git a/Pipfile b/Pipfile index dac7cdb..6d62678 100644 --- a/Pipfile +++ b/Pipfile @@ -16,6 +16,7 @@ uwsgi = ">=2.0" uwsgitop = ">=0.11" uwsgi-tools = ">=1.1.1" flask-mail = ">=0.9.0,<0.10.0" +# invenio-cern-sync = {git = "https://github.com/cerndocumentserver/invenio-cern-sync.git", ref = "v1.0.0"} [requires] python_version = "3.9" diff --git a/Pipfile.lock b/Pipfile.lock index 0d9db23..2e16b06 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -264,99 +264,114 @@ }, "charset-normalizer": { "hashes": [ - "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", - "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", - "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", - "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", - "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", - "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", - "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", - "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", - "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", - "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", - "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", - "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", - "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", - "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", - "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", - "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", - "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", - "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", - "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", - "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", - "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", - "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", - "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", - "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", - "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", - "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", - "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", - "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", - "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", - "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", - "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", - "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", - "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", - "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", - "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", - "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", - "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", - "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", - "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", - "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", - "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", - "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", - "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", - "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", - "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", - "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", - "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", - "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", - "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", - "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", - "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", - "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", - "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", - "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", - "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", - "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", - "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", - "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", - "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", - "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", - "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", - "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", - "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", - "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", - "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", - "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", - "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", - "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", - "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", - "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", - "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", - "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", - "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", - "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", - "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", - "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", - "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", - "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", - "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", - "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", - "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", - "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", - "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", - "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", - "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", - "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", - "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", - "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", - "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", - "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621", + "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", + "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", + "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", + "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", + "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", + "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", + "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", + "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", + "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", + "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", + "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", + "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab", + "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", + "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", + "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", + "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", + "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", + "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62", + "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", + "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", + "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", + "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", + "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", + "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455", + "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858", + "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", + "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", + "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", + "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", + "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", + "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea", + "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", + "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", + "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", + "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", + "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", + "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", + "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", + "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee", + "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", + "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", + "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51", + "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", + "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8", + "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", + "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613", + "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", + "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", + "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", + "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", + "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", + "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", + "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", + "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", + "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", + "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417", + "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", + "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", + "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", + "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", + "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", + "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149", + "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41", + "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574", + "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", + "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f", + "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", + "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654", + "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", + "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19", + "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", + "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578", + "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", + "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", + "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51", + "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", + "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", + "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", + "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", + "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade", + "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", + "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", + "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6", + "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", + "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", + "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6", + "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2", + "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12", + "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf", + "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", + "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7", + "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", + "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", + "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", + "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", + "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", + "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4", + "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", + "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", + "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", + "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748", + "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", + "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", + "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482" ], "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.2" + "version": "==3.4.0" }, "citeproc-py": { "hashes": [ @@ -590,11 +605,11 @@ }, "faker": { "hashes": [ - "sha256:dbf81295c948270a9e96cd48a9a3ebec73acac9a153d0c854fbbd0294557609f", - "sha256:e0593931bd7be9a9ea984b5d8c302ef1cec19392585d1e90d444199271d0a94d" + "sha256:8760fbb34564fbb2f394345eef24aec5b8f6506b6cfcefe8195ed66dd1032bdb", + "sha256:e8a15fd1b0f72992b008f5ea94c70d3baa0cb51b0d5a0e899c17b1d1b23d2771" ], "markers": "python_version >= '3.8'", - "version": "==30.1.0" + "version": "==30.3.0" }, "fastjsonschema": { "hashes": [ @@ -903,7 +918,6 @@ "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f" ], - "markers": "python_version >= '3' and (platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32'))))))", "version": "==3.1.1" }, "html5lib": { @@ -1001,11 +1015,11 @@ }, "invenio-administration": { "hashes": [ - "sha256:0fe98b4ae8078a69cd21d95fbe3e7d6201494c0a6f5027faac0ce2538d65d4f9", - "sha256:5726cf14c2d7a766adb901d5f85adc703e4137877e959bb686a9b2d7ddfc1eee" + "sha256:1c870679fa011058dae692049cf521e8cbff77fb526c90c4c4b9aa409980df1d", + "sha256:c14b864809834e5820e51d1f1aeda3365b16d1c05afa8405803f243ad7a9125f" ], "markers": "python_version >= '3.7'", - "version": "==2.7.2" + "version": "==2.8.2" }, "invenio-app": { "hashes": [ @@ -1020,11 +1034,11 @@ "opensearch2" ], "hashes": [ - "sha256:1ca69f6115ae1cf340a037c67d26b552e4b0c8efc49586ee7414b0e703d0f74c", - "sha256:71c0da56bbf5da62e1808ec0712f6de62e0ceb5f22c14c6dfa3629bd9e04a784" + "sha256:24cb1069ae946bb49e99cb4b247f56eb36104544281a5fecce4a1218b7b5f3df", + "sha256:92f62dad44e9305a1fe8252a5fda896657b5517863c3ea7b713b0a6ef2ac87bf" ], "markers": "python_version >= '3.7'", - "version": "==13.0.0b1.dev8" + "version": "==13.0.0b1.dev10" }, "invenio-assets": { "hashes": [ @@ -1071,19 +1085,11 @@ }, "invenio-communities": { "hashes": [ -<<<<<<< HEAD - "sha256:3b9004dfd1fb021bdf605ec34c90b723b1e3d9e76898270b1d248fbbf73cebd0", - "sha256:8a6adf4c52204af0f1c62c902aef8887957c61614f7735b0c20bee120ff31657" - ], - "markers": "python_version >= '3.8'", - "version": "==16.0.0" -======= "sha256:2c40df89e7a495967de0802780326f47574c29c2b4ae7cf21a25bebd06a680b7", "sha256:ca18ea5931699aef1d93c34e7cdb14402b4dc080362487e1d059dd631f0736d3" ], "markers": "python_version >= '3.8'", "version": "==17.1.2" ->>>>>>> 0d8fc23 (setup: upgrade and remove obsolete files) }, "invenio-config": { "hashes": [ @@ -1155,11 +1161,11 @@ }, "invenio-jobs": { "hashes": [ - "sha256:dd01a454063d6897f614e0a5836c72d749bafc3c782743fe445568212084fde0", - "sha256:e1ed6ac61c7ad040406ae504567628d4518d3c57e1e1e17e1103be53cde6e5a1" + "sha256:7f5f54b72c03507da95454da291a0993fbc88c66625336352aaa38aeb88e3d40", + "sha256:b9e8ce875a4523b67e24e68ebe3ac0f06326bb6b247dc348cf46cb112642f020" ], "markers": "python_version >= '3.7'", - "version": "==0.5.1" + "version": "==1.1.0" }, "invenio-jsonschemas": { "hashes": [ @@ -1253,19 +1259,19 @@ }, "invenio-rdm-records": { "hashes": [ - "sha256:97d41d81644ced34473716341668d8eac5634f4eb027b9cca6dff074f2cfc1c4", - "sha256:b01f4a336a9c0596d0f38aa2a54ff6369f8505a46f23b74f0f826ff607208890" + "sha256:a4adf4f3b1b7156a681d254283291101ea7bd93812b6daf97fd5371e7f57a058", + "sha256:eaae89ae01c62b2762ad82ba1f04871eac2f79f58196aa46ad237e2cc5c7e45c" ], "markers": "python_version >= '3.7'", - "version": "==14.0.0" + "version": "==15.2.0" }, "invenio-records": { "hashes": [ - "sha256:c43fe294b58db80e5a778cde48ea1d2e6ed80c6ce53e1950cffe2e3a890289a9", - "sha256:e5691a5bbdbf42b8a3f8985190fad4de163e1a439b08367d2f702c3c6d4a73e5" + "sha256:799a55450a6bfcdff1116f3ea2370c04ed345d22c414f083a3973c2101608480", + "sha256:7c03c59343eac62f2ce4ba72f03ea7664f91c1946300c53966ec39d9e65c3526" ], "markers": "python_version >= '3.7'", - "version": "==2.3.0" + "version": "==2.4.0" }, "invenio-records-files": { "hashes": [ @@ -1308,11 +1314,11 @@ }, "invenio-requests": { "hashes": [ - "sha256:4f365584b880ebf11acbc7cec108efa768b67ae074be3df668df2347c7a37eb6", - "sha256:82cdf01855050117966139a4db33f025dae426ca2c93a0d71d242d44b1efb9a4" + "sha256:65d007c98dc19b6d750ffd456e0031d081084bdda7d50fec2c0bc47a324760b4", + "sha256:795f81545f0051ef3595676d8d9ca0587247b5bff37f234a4fd4ba0ad6abf3ab" ], "markers": "python_version >= '3.7'", - "version": "==5.1.1" + "version": "==5.2.0" }, "invenio-rest": { "hashes": [ @@ -1334,11 +1340,11 @@ }, "invenio-search-ui": { "hashes": [ - "sha256:1bbaa79cd2a1a400e26cc44971cc60df4031d4b674c474956fb8dcd812a2434b", - "sha256:34b330fd56f4d5030adebedf019e58f166526a8986809a23515fb27e585bb298" + "sha256:46d31dc2b5e606d65c0ab6051b67a61da9a021705f455291a6bcb472e0e600fc", + "sha256:9ab47cc99f661e249614eaa5654450863f4fb20543d990356dddc8602a0b3896" ], "markers": "python_version >= '3.7'", - "version": "==2.9.1" + "version": "==3.0.0" }, "invenio-stats": { "hashes": [ @@ -1374,19 +1380,11 @@ }, "invenio-vocabularies": { "hashes": [ -<<<<<<< HEAD - "sha256:4cc84bfebe771107691856afd92cea509a9c58598e03bf3161e2f9ab60e19794", - "sha256:ff7267a11751bbc252a6097f652430e3769d1406c0249f60319f2f1576f244c9" - ], - "markers": "python_version >= '3.7'", - "version": "==6.0.0" -======= "sha256:0de6b7aae2a5dfdd75badc3b43434fb793a2104005af0c6ec1ad1a06c7282252", "sha256:a96088938750a39e0eea3c9ddcae82fe77daeb5c7034e2714059f81a6a7fd25c" ], "markers": "python_version >= '3.7'", "version": "==6.3.1" ->>>>>>> 0d8fc23 (setup: upgrade and remove obsolete files) }, "invenio-webhooks": { "hashes": [ @@ -1491,11 +1489,11 @@ }, "jsonschema-specifications": { "hashes": [ - "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", - "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c" + "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", + "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf" ], - "markers": "python_version >= '3.8'", - "version": "==2023.12.1" + "markers": "python_version >= '3.9'", + "version": "==2024.10.1" }, "jupyter-client": { "hashes": [ @@ -1691,10 +1689,10 @@ }, "lxml-html-clean": { "hashes": [ - "sha256:177ebe822b39d1b68df7c0c34ba005cb087b23d3791dae87efb3a2bb162ef398", - "sha256:cc34178e34673025c49c3d7f4bd48754e9e4b23875df2308f43c21733d8437fb" + "sha256:081e6378c68ebb4113940ed82a3534c99e24ba1ca5ad5ce8868c7c4d264618f1", + "sha256:d9f7d8ae36092f4ed5079cfbf95ff06d3c6fd04f4a861422ce251ece72d3c4b5" ], - "version": "==0.2.2" + "version": "==0.3.1" }, "mako": { "hashes": [ @@ -1714,69 +1712,70 @@ }, "markupsafe": { "hashes": [ - "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf", - "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff", - "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", - "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3", - "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532", - "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f", - "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", - "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df", - "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4", - "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", - "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", - "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", - "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8", - "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371", - "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2", - "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465", - "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52", - "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6", - "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", - "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad", - "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", - "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0", - "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029", - "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", - "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a", - "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", - "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5", - "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", - "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf", - "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9", - "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", - "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", - "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3", - "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", - "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46", - "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc", - "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a", - "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", - "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900", - "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", - "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea", - "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", - "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5", - "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e", - "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", - "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f", - "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50", - "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", - "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", - "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4", - "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff", - "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2", - "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46", - "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", - "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf", - "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", - "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5", - "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab", - "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd", - "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.5" + "sha256:0778de17cff1acaeccc3ff30cd99a3fd5c50fc58ad3d6c0e0c4c58092b859396", + "sha256:0f84af7e813784feb4d5e4ff7db633aba6c8ca64a833f61d8e4eade234ef0c38", + "sha256:17b2aea42a7280db02ac644db1d634ad47dcc96faf38ab304fe26ba2680d359a", + "sha256:242d6860f1fd9191aef5fae22b51c5c19767f93fb9ead4d21924e0bcb17619d8", + "sha256:244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b", + "sha256:26627785a54a947f6d7336ce5963569b5d75614619e75193bdb4e06e21d447ad", + "sha256:2a4b34a8d14649315c4bc26bbfa352663eb51d146e35eef231dd739d54a5430a", + "sha256:2ae99f31f47d849758a687102afdd05bd3d3ff7dbab0a8f1587981b58a76152a", + "sha256:312387403cd40699ab91d50735ea7a507b788091c416dd007eac54434aee51da", + "sha256:3341c043c37d78cc5ae6e3e305e988532b072329639007fd408a476642a89fd6", + "sha256:33d1c36b90e570ba7785dacd1faaf091203d9942bc036118fab8110a401eb1a8", + "sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344", + "sha256:3ffb4a8e7d46ed96ae48805746755fadd0909fea2306f93d5d8233ba23dda12a", + "sha256:40621d60d0e58aa573b68ac5e2d6b20d44392878e0bfc159012a5787c4e35bc8", + "sha256:40f1e10d51c92859765522cbd79c5c8989f40f0419614bcdc5015e7b6bf97fc5", + "sha256:45d42d132cff577c92bfba536aefcfea7e26efb975bd455db4e6602f5c9f45e7", + "sha256:48488d999ed50ba8d38c581d67e496f955821dc183883550a6fbc7f1aefdc170", + "sha256:4935dd7883f1d50e2ffecca0aa33dc1946a94c8f3fdafb8df5c330e48f71b132", + "sha256:4c2d64fdba74ad16138300815cfdc6ab2f4647e23ced81f59e940d7d4a1469d9", + "sha256:4c8817557d0de9349109acb38b9dd570b03cc5014e8aabf1cbddc6e81005becd", + "sha256:4ffaaac913c3f7345579db4f33b0020db693f302ca5137f106060316761beea9", + "sha256:5a4cb365cb49b750bdb60b846b0c0bc49ed62e59a76635095a179d440540c346", + "sha256:62fada2c942702ef8952754abfc1a9f7658a4d5460fabe95ac7ec2cbe0d02abc", + "sha256:67c519635a4f64e495c50e3107d9b4075aec33634272b5db1cde839e07367589", + "sha256:6a54c43d3ec4cf2a39f4387ad044221c66a376e58c0d0e971d47c475ba79c6b5", + "sha256:7044312a928a66a4c2a22644147bc61a199c1709712069a344a3fb5cfcf16915", + "sha256:730d86af59e0e43ce277bb83970530dd223bf7f2a838e086b50affa6ec5f9295", + "sha256:800100d45176652ded796134277ecb13640c1a537cad3b8b53da45aa96330453", + "sha256:80fcbf3add8790caddfab6764bde258b5d09aefbe9169c183f88a7410f0f6dea", + "sha256:82b5dba6eb1bcc29cc305a18a3c5365d2af06ee71b123216416f7e20d2a84e5b", + "sha256:852dc840f6d7c985603e60b5deaae1d89c56cb038b577f6b5b8c808c97580f1d", + "sha256:8ad4ad1429cd4f315f32ef263c1342166695fad76c100c5d979c45d5570ed58b", + "sha256:8ae369e84466aa70f3154ee23c1451fda10a8ee1b63923ce76667e3077f2b0c4", + "sha256:93e8248d650e7e9d49e8251f883eed60ecbc0e8ffd6349e18550925e31bd029b", + "sha256:973a371a55ce9ed333a3a0f8e0bcfae9e0d637711534bcb11e130af2ab9334e7", + "sha256:9ba25a71ebf05b9bb0e2ae99f8bc08a07ee8e98c612175087112656ca0f5c8bf", + "sha256:a10860e00ded1dd0a65b83e717af28845bb7bd16d8ace40fe5531491de76b79f", + "sha256:a4792d3b3a6dfafefdf8e937f14906a51bd27025a36f4b188728a73382231d91", + "sha256:a7420ceda262dbb4b8d839a4ec63d61c261e4e77677ed7c66c99f4e7cb5030dd", + "sha256:ad91738f14eb8da0ff82f2acd0098b6257621410dcbd4df20aaa5b4233d75a50", + "sha256:b6a387d61fe41cdf7ea95b38e9af11cfb1a63499af2759444b99185c4ab33f5b", + "sha256:b954093679d5750495725ea6f88409946d69cfb25ea7b4c846eef5044194f583", + "sha256:bbde71a705f8e9e4c3e9e33db69341d040c827c7afa6789b14c6e16776074f5a", + "sha256:beeebf760a9c1f4c07ef6a53465e8cfa776ea6a2021eda0d0417ec41043fe984", + "sha256:c91b394f7601438ff79a4b93d16be92f216adb57d813a78be4446fe0f6bc2d8c", + "sha256:c97ff7fedf56d86bae92fa0a646ce1a0ec7509a7578e1ed238731ba13aabcd1c", + "sha256:cb53e2a99df28eee3b5f4fea166020d3ef9116fdc5764bc5117486e6d1211b25", + "sha256:cbf445eb5628981a80f54087f9acdbf84f9b7d862756110d172993b9a5ae81aa", + "sha256:d06b24c686a34c86c8c1fba923181eae6b10565e4d80bdd7bc1c8e2f11247aa4", + "sha256:d98e66a24497637dd31ccab090b34392dddb1f2f811c4b4cd80c230205c074a3", + "sha256:db15ce28e1e127a0013dfb8ac243a8e392db8c61eae113337536edb28bdc1f97", + "sha256:db842712984e91707437461930e6011e60b39136c7331e971952bb30465bc1a1", + "sha256:e24bfe89c6ac4c31792793ad9f861b8f6dc4546ac6dc8f1c9083c7c4f2b335cd", + "sha256:e81c52638315ff4ac1b533d427f50bc0afc746deb949210bc85f05d4f15fd772", + "sha256:e9393357f19954248b00bed7c56f29a25c930593a77630c719653d51e7669c2a", + "sha256:ee3941769bd2522fe39222206f6dd97ae83c442a94c90f2b7a25d847d40f4729", + "sha256:f31ae06f1328595d762c9a2bf29dafd8621c7d3adc130cbb46278079758779ca", + "sha256:f94190df587738280d544971500b9cafc9b950d32efcb1fba9ac10d84e6aa4e6", + "sha256:fa7d686ed9883f3d664d39d5a8e74d3c5f63e603c2e3ff0abcba23eac6542635", + "sha256:fb532dd9900381d2e8f48172ddc5a59db4c445a11b9fab40b3b786da40d3b56b", + "sha256:fe32482b37b4b00c7a52a07211b479653b7fe4f22b2e481b9a9b099d8a430f2f" + ], + "markers": "python_version >= '3.9'", + "version": "==3.0.1" }, "marshmallow": { "hashes": [ @@ -2339,22 +2338,6 @@ ], "version": "==0.2.3" }, - "pyasn1": { - "hashes": [ - "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", - "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034" - ], - "markers": "python_version >= '3.8'", - "version": "==0.6.1" - }, - "pyasn1-modules": { - "hashes": [ - "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd", - "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c" - ], - "markers": "python_version >= '3.8'", - "version": "==0.4.1" - }, "pycountry": { "hashes": [ "sha256:b2163a246c585894d808f18783e19137cb70a0c18fb36748dc01fc6f109c1646" @@ -2443,13 +2426,6 @@ ], "version": "==1.2" }, - "python-ldap": { - "hashes": [ - "sha256:7edb0accec4e037797705f3a05cbf36a9fde50d08c8f67f2aef99a2628fab828" - ], - "markers": "python_version >= '3.6'", - "version": "==3.4.4" - }, "python-slugify": { "hashes": [ "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8", @@ -3631,11 +3607,11 @@ }, "zipstream-ng": { "hashes": [ - "sha256:d5a30ac73ae70ce32e1bde937da6839f01cad1f4effeedda5bfa08c6f6b8f73d", - "sha256:f92023b9ca578cd7fdd94ec733c65664ecf7ee32493e38cdf8e365a1316e9ffc" + "sha256:b7129d2c15d26934b3e1cb22256593b6bdbd03c553c26f4199a5bf05110642bc", + "sha256:e7196cb845cf924ed12e7a3b38404ef9e82a5a699801295f5f4cf601449e2bf6" ], "markers": "python_full_version >= '3.5.0'", - "version": "==1.7.1" + "version": "==1.8.0" } }, "develop": { @@ -3649,12 +3625,12 @@ }, "check-manifest": { "hashes": [ - "sha256:058cd30057714c39b96ce4d83f254fc770e3145c7b1932b5940b4e3efb5521ef", - "sha256:64a640445542cf226919657c7b78d02d9c1ca5b1c25d7e66e0e1ff325060f416" + "sha256:6ab3e3aa72a008da3314b432f4c768c9647b4d6d8032f9e1a4672a572118e48c", + "sha256:d300f9f292986aa1a30424af44eb45c5644e0a810e392e62d553b24bb3393494" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.49" + "version": "==0.50" }, "importlib-metadata": { "hashes": [ diff --git a/invenio.cfg b/invenio.cfg index 5168f92..6a61843 100644 --- a/invenio.cfg +++ b/invenio.cfg @@ -186,6 +186,7 @@ DATACITE_DATACENTER_SYMBOL = "" # See https://github.com/inveniosoftware/invenio-accounts/blob/master/invenio_accounts/config.py ACCOUNTS_DEFAULT_USERS_VERIFIED = True # ensure that users are verified by default ACCOUNTS_DEFAULT_USER_VISIBILITY = "public" # enables users to be searchable for invites +ACCOUNTS_DEFAULT_EMAIL_VISIBILITY = "public" ACCOUNTS_LOCAL_LOGIN_ENABLED = True # enable local login PERMANENT_SESSION_LIFETIME = timedelta(days=10) SECURITY_REGISTERABLE = True # local login: allow users to register @@ -201,83 +202,45 @@ SECURITY_SEND_PASSWORD_RESET_EMAIL = False SECURITY_SEND_PASSWORD_RESET_NOTICE_EMAIL = False SECURITY_SEND_REGISTER_EMAIL = False -# Invenio-OAuthclient -# =================== -# See https://github.com/inveniosoftware/invenio-oauthclient/blob/master/invenio_oauthclient/config.py -from invenio_oauthclient.contrib.keycloak import KeycloakSettingsHelper -from cds_rdm.oidc import ( - cern_info_handler, - cern_info_serializer, - confirm_registration_form, - cern_groups_serializer, - cern_groups_handler, - cern_setup_handler, -) -from urllib.parse import quote - -CERN_KEYCLOAK_BASE_URL = os.environ.get("INVENIO_CERN_KEYCLOAK_BASE_URL", "https://keycloak-qa.cern.ch/") - -_keycloak_helper = KeycloakSettingsHelper( - title="CERN", - description="CERN SSO authentication", - base_url=CERN_KEYCLOAK_BASE_URL, - realm="cern", - app_key="CERN_APP_CREDENTIALS", - logout_url="{}auth/realms/cern/protocol/openid-connect/logout?redirect_uri={}".format( - CERN_KEYCLOAK_BASE_URL, - quote(os.environ.get("INVENIO_SITE_UI_URL", SITE_UI_URL)) - ), -) -OAUTHCLIENT_CERN_REALM_URL = _keycloak_helper.realm_url -OAUTHCLIENT_CERN_USER_INFO_URL = _keycloak_helper.user_info_url -OAUTHCLIENT_CERN_VERIFY_EXP = True -OAUTHCLIENT_CERN_VERIFY_AUD = False -OAUTHCLIENT_CERN_USER_INFO_FROM_ENDPOINT = True - -handlers = _keycloak_helper.get_handlers() -handlers["signup_handler"] = { - **handlers["signup_handler"], - "info": cern_info_handler, - "info_serializer": cern_info_serializer, - "groups_serializer": cern_groups_serializer, - "groups": cern_groups_handler, - "setup": cern_setup_handler, -} -rest_handlers = _keycloak_helper.get_rest_handlers() -rest_handlers["signup_handler"] = { - **rest_handlers["signup_handler"], - "info": cern_info_handler, - "info_serializer": cern_info_serializer, - "groups_serializer": cern_groups_serializer, - "groups": cern_groups_handler, - "setup": cern_setup_handler, -} - -OAUTHCLIENT_SIGNUP_FORM = confirm_registration_form - -OAUTH_REMOTE_APP_NAME = "cern" - +# Invenio-CERN-Sync/CERN SSO +# ========================== +from invenio_cern_sync.sso import cern_remote_app_name, cern_keycloak OAUTHCLIENT_REMOTE_APPS = { - OAUTH_REMOTE_APP_NAME: _keycloak_helper.remote_app, + cern_remote_app_name: cern_keycloak.remote_app, } CERN_APP_CREDENTIALS = { "consumer_key": "CHANGE ME", "consumer_secret": "CHANGE ME", } +CERN_SYNC_KEYCLOAK_BASE_URL = "https://auth.cern.ch/" +CERN_SYNC_AUTHZ_BASE_URL = "https://authorization-service-api.web.cern.ch/" +INVENIO_CERN_SYNC_KEYCLOAK_BASE_URL = "https://auth.cern.ch/" # set env var when testing + + +OAUTHCLIENT_CERN_REALM_URL = cern_keycloak.realm_url +OAUTHCLIENT_CERN_USER_INFO_URL = cern_keycloak.user_info_url +OAUTHCLIENT_CERN_VERIFY_EXP = True +OAUTHCLIENT_CERN_VERIFY_AUD = False +OAUTHCLIENT_CERN_USER_INFO_FROM_ENDPOINT = True from invenio_oauthclient.views.client import auto_redirect_login -ACCOUNTS_LOGIN_VIEW_FUNCTION = auto_redirect_login # autoredirect to external login if enabled -OAUTHCLIENT_AUTO_REDIRECT_TO_EXTERNAL_LOGIN = True # autoredirect to external login +OAUTHCLIENT_AUTO_REDIRECT_TO_EXTERNAL_LOGIN = True # enable autoredirect to external login +ACCOUNTS_LOGIN_VIEW_FUNCTION = auto_redirect_login # autoredirect to external login + +from invenio_cern_sync.sso.api import confirm_registration_form +OAUTHCLIENT_SIGNUP_FORM = confirm_registration_form + +from invenio_cern_sync.users.profile import CERNUserProfileSchema +ACCOUNTS_USER_PROFILE_SCHEMA = CERNUserProfileSchema() # Invenio-UserProfiles # ==================== -USERPROFILES_READ_ONLY = False # allow users to change profile info (name, email, etc...) -USERPROFILES_EXTEND_SECURITY_FORMS = True +USERPROFILES_READ_ONLY = True # disable change of user profile +USERPROFILES_EXTEND_SECURITY_FORMS = True # automatically use user's email address as account email # OAI-PMH # ======= -# See https://github.com/inveniosoftware/invenio-oaiserver/blob/master/invenio_oaiserver/config.py OAISERVER_ID_PREFIX = "cds-rdm.com" """The prefix that will be applied to the generated OAI-PMH ids.""" @@ -285,35 +248,15 @@ OAISERVER_ID_PREFIX = "cds-rdm.com" # ============== SEARCH_INDEX_PREFIX = "cds-rdm-" -# Celery -# ====== -CELERY_BEAT_SCHEDULE = { - **APP_RDM_CELERY_BEAT_SCHEDULE, - "user-sync": { - "task": "cds_rdm.tasks.sync_users", - "schedule": crontab(minute=0, hour=3), # Every day at 03:00 UTC - }, - "groups-sync": { - "task": "cds_rdm.tasks.sync_groups", - "schedule": crontab(minute=0, hour=2), # Every day at 02:00 UTC - }, -} - ############################################################################### # CDS-RDM configuration ############################################################################### CDS_SERVICE_ELEMENT_URL = "https://cern.service-now.com/service-portal?id=service_element&name=CDS-Service" -# AUTH/LDAP -CERN_LDAP_URL = "ldap://xldap.cern.ch" -CERN_AUTHORIZATION_SERVICE_API = "https://authorization-service-api-qa.web.cern.ch/api/v1.0/" -CERN_AUTHORIZATION_SERVICE_API_GROUP = "Group" - # Permissions: define who can create new communities CDS_EMAILS_ALLOW_CREATE_COMMUNITIES = [] CDS_GROUPS_ALLOW_CREATE_COMMUNITIES = [] - # Invenio-Files-REST # ================== XROOTD_ENABLED = False @@ -355,7 +298,7 @@ RDM_NAMESPACES = { # VocabularyCF( # name="cern:experiment", # vocabulary_id="experiments", -# dump_options=True, +# dump_options=True, # multiple=False, # ), # VocabularyCF( @@ -401,4 +344,12 @@ RDM_NAMESPACES = { # ), # ] # } -# ] \ No newline at end of file +# ] + + +RDM_FILES_DEFAULT_QUOTA_SIZE = 50 * 10**9 # 50GB +RDM_FILES_DEFAULT_MAX_FILE_SIZE = 50 * 10**9 # 50GB + +# Invenio-Jobs +# ============ +JOBS_ADMINISTRATION_ENABLED = True diff --git a/site/cds_rdm/assets/semantic-ui/js/cds_rdm/src/records/detail.js b/site/cds_rdm/assets/semantic-ui/js/cds_rdm/src/records/detail.js deleted file mode 100644 index 3c0381c..0000000 --- a/site/cds_rdm/assets/semantic-ui/js/cds_rdm/src/records/detail.js +++ /dev/null @@ -1,14 +0,0 @@ -/* - * This file is part of Invenio. - * Copyright (C) 2016-2022 CERN. - * - * Invenio is free software; you can redistribute it and/or modify it - * under the terms of the MIT License; see LICENSE file for more details. - */ - -import $ from "jquery"; - -// Initialize conceptdoi modal -$("#record-conceptdoi-badge").on("click", function () { - $("#conceptdoi-modal").modal("show"); -}); diff --git a/site/cds_rdm/cli.py b/site/cds_rdm/cli.py index a871bbb..ffbea29 100644 --- a/site/cds_rdm/cli.py +++ b/site/cds_rdm/cli.py @@ -15,14 +15,12 @@ from invenio_pidstore.models import PersistentIdentifier from invenio_rdm_records.proxies import current_rdm_records_service from invenio_rdm_records.records.api import RDMDraft, RDMRecord -from invenio_rdm_records.records.models import ( - RDMDraftMetadata, - RDMFileDraftMetadata, - RDMFileRecordMetadata, - RDMParentCommunity, - RDMRecordMetadata, - RDMVersionsState, -) +from invenio_rdm_records.records.models import (RDMDraftMetadata, + RDMFileDraftMetadata, + RDMFileRecordMetadata, + RDMParentCommunity, + RDMRecordMetadata, + RDMVersionsState) from invenio_requests.proxies import current_requests_service from invenio_requests.records.api import Request from invenio_requests.records.models import RequestMetadata diff --git a/site/cds_rdm/files.py b/site/cds_rdm/files.py index 061fb95..728e1df 100644 --- a/site/cds_rdm/files.py +++ b/site/cds_rdm/files.py @@ -26,7 +26,8 @@ HTTPKerberosAuth = type("obj", (object,), {}) DISABLED = 3 # use base PyFSFileStorage instead - from invenio_files_rest.storage.pyfs import PyFSFileStorage as BaseFileStorage + from invenio_files_rest.storage.pyfs import \ + PyFSFileStorage as BaseFileStorage class OffloadFileStorage(BaseFileStorage): diff --git a/site/cds_rdm/jobs.py b/site/cds_rdm/jobs.py new file mode 100644 index 0000000..594c8a5 --- /dev/null +++ b/site/cds_rdm/jobs.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Invenio. +# Copyright (C) 2023 CERN. +# +# Invenio is free software; you can redistribute it and/or modify it +# under the terms of the MIT License; see LICENSE file for more details. + +"""Jobs.""" + +from invenio_jobs.jobs import JobType + +from .tasks import sync_groups, sync_users + + +class SyncUsers(JobType): + """Sync users with CERN database.""" + + id = "sync_cern_users" + title = "Sync CERN users" + description = "Sync CERN users with the AuthZ service" + + task = sync_users + + @classmethod + def build_task_arguments(cls, _, since=None, **kwargs): + """Build task arguments.""" + return {"since": since} + + +class SyncGroups(JobType): + """Sync groups with CERN database.""" + + id = "sync_cern_groups" + title = "Sync CERN groups" + description = "Sync CERN groups with the AuthZ service" + + task = sync_groups + + @classmethod + def build_task_arguments(cls, _, since=None, **kwargs): + """Build task arguments.""" + return {"since": since} diff --git a/site/cds_rdm/ldap/__init__.py b/site/cds_rdm/ldap/__init__.py deleted file mode 100644 index d30dac0..0000000 --- a/site/cds_rdm/ldap/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2023 CERN. -# -# CDS-RDM is free software; you can redistribute it and/or modify it under -# the terms of the MIT License; see LICENSE file for more details. - -"""CDS-RDM ldap module.""" diff --git a/site/cds_rdm/ldap/api.py b/site/cds_rdm/ldap/api.py deleted file mode 100644 index becabf1..0000000 --- a/site/cds_rdm/ldap/api.py +++ /dev/null @@ -1,196 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2023 CERN. -# -# CDS-RDM is free software; you can redistribute it and/or modify it under -# the terms of the MIT License; see LICENSE file for more details. - -"""CDS-RDM ldap API.""" - -import json -import time -import uuid -from functools import partial - -from flask import current_app -from invenio_db import db -from invenio_oauthclient.models import RemoteAccount -from invenio_users_resources.services.users.tasks import reindex_users - -from cds_rdm.ldap.client import LdapClient -from cds_rdm.ldap.user_importer import LdapUserImporter -from cds_rdm.ldap.utils import InvenioUser, serialize_ldap_user, user_exists - - -def get_ldap_users(log_func): - """Create and return a map of all LDAP users.""" - ldap_users_emails = set() - ldap_users_map = {} - - # get all CERN users from LDAP - ldap_client = LdapClient() - ldap_users = ldap_client.get_primary_accounts() - ldap_users_count = len(ldap_users) - - log_func("ldap_users_fetched", dict(users_fetched=ldap_users_count)) - - for ldap_user_data in ldap_users: - ldap_user = serialize_ldap_user(ldap_user_data, log_func=log_func) - - if ldap_user and ldap_user["user_email"] not in ldap_users_emails: - ldap_person_id = ldap_user["remote_account_person_id"] - ldap_users_map[ldap_person_id] = ldap_user - ldap_users_emails.add(ldap_user["user_email"]) - - log_func("ldap_users_cached") - return ldap_users_count, ldap_users_map, ldap_users_emails - - -def remap_invenio_users(log_func): - """Create and return a list of all Invenio users.""" - invenio_remote_accounts_list = [] - remote_accounts = RemoteAccount.query.all() - - log_func("invenio_users_fetched", dict(users_fetched=len(remote_accounts))) - - # get all Invenio remote accounts and prepare a list with needed info - for remote_account in remote_accounts: - invenio_remote_accounts_list.append(remote_account) - log_func("invenio_users_cached") - return invenio_remote_accounts_list - - -def _log_info(log_uuid, action, extra=dict(), is_error=False): - name = "ldap_users_synchronization" - structured_msg = dict(name=name, uuid=log_uuid, action=action, **extra) - structured_msg_str = json.dumps(structured_msg, sort_keys=True) - if is_error: - current_app.logger.error(structured_msg_str) - else: - current_app.logger.info(structured_msg_str) - - -def update_users(): - """Sync LDAP users with local users in the DB.""" - - def update_invenio_users_from_ldap(remote_accounts, ldap_users_map, log_func): - """Iterate on all Invenio users to update outdated info from LDAP.""" - updated_count = 0 - user_ids = [] - # Note: cannot iterate on the db query here, because when a user is - # deleted, db session will expire, causing a DetachedInstanceError when - # fetching the user on the next iteration - for remote_account in remote_accounts: - invenio_user = InvenioUser(remote_account) - if not invenio_user.data.get("remote_account_person_id"): - # not a CERN user - continue - - # use `dict.pop` to remove from `ldap_users_map` the users found - # in Invenio, so the remaining will be the ones to be added - # later on - ldap_user = ldap_users_map.pop( - invenio_user.data["remote_account_person_id"], None - ) - if not ldap_user: - continue - - invenio_user.data.pop("remote_account_id") - ldap_user.pop("cern_account_type") - - has_changed = invenio_user.data != ldap_user - - if has_changed: - invenio_user.update(ldap_user) - user_ids.append(invenio_user.user_id) - db.session.commit() - log_func( - "user_updated", - dict(user_id=invenio_user.user_id), - ) - - updated_count += 1 - - db.session.commit() - reindex_users.delay(user_ids) - log_func("invenio_users_updated_from_ldap", dict(count=updated_count)) - - return ldap_users_map, updated_count - - def import_new_ldap_users(new_ldap_users, log_func): - """Import any new LDAP user not in Invenio yet.""" - remote_account_client_id = current_app.config["CERN_APP_CREDENTIALS"][ - "consumer_key" - ] - importer = LdapUserImporter(remote_account_client_id) - added_count = 0 - user_ids = [] - for ldap_user in new_ldap_users: - # Check if email already exists in Invenio. - # Apparently, in some cases, there could be multiple LDAP users - # with different person id but same email. - if not ldap_user: - continue - - if user_exists(ldap_user): - # disabled, too noisy - # log_func( - # "ldap_user_skipped_user_exists", - # dict( - # email=ldap_user["user_email"], - # person_id=ldap_user["remote_account_person_id"], - # ), - # ) - continue - - email = ldap_user["user_email"] - username = ldap_user["user_username"] - employee_id = ldap_user["remote_account_person_id"] - - user_id = importer.import_user(ldap_user) - user_ids.append(user_id) - log_func( - "invenio_user_added", - dict(email=email, username=username, employee_id=employee_id), - ) - - added_count += 1 - - db.session.commit() - reindex_users.delay(user_ids) - log_func("import_new_users_done", dict(count=added_count)) - - return added_count - - log_uuid = str(uuid.uuid4()) - log_func = partial(_log_info, log_uuid) - start_time = time.time() - - ldap_users_count, ldap_users_map, ldap_users_emails = get_ldap_users(log_func) - - if not ldap_users_emails: - return 0, 0, 0 - - remote_accounts = remap_invenio_users(log_func) - - # STEP 1 - update Invenio users with info from LDAP - ldap_users_map, invenio_users_updated = update_invenio_users_from_ldap( - remote_accounts, ldap_users_map, log_func - ) - - # STEP 2 - import any new LDAP user not in Invenio yet - invenio_users_added = 0 - new_ldap_users = ldap_users_map.values() - - if new_ldap_users: - invenio_users_added = import_new_ldap_users(new_ldap_users, log_func) - - total_time = time.time() - start_time - - log_func("task_completed", dict(time=total_time)) - - return ( - ldap_users_count, - invenio_users_updated, - invenio_users_added, - ) diff --git a/site/cds_rdm/ldap/client.py b/site/cds_rdm/ldap/client.py deleted file mode 100644 index 8f40bb2..0000000 --- a/site/cds_rdm/ldap/client.py +++ /dev/null @@ -1,106 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2023 CERN. -# -# CDS-RDM is free software; you can redistribute it and/or modify it under -# the terms of the MIT License; see LICENSE file for more details. - -"""CDS-RDM ldap Client.""" - -import ldap -from flask import current_app - - -class LdapClient(object): - """Ldap client class for user importation/synchronization. - - Response example: - [ - {'displayName': [b'Joe Foe'], - 'department': [b'IT/CDA'], - 'uidNumber': [b'100000'], - 'mail': [b'joe.foe@cern.ch'], - 'cernAccountType': [b'Primary'], - 'employeeID': [b'101010'] - 'cn': [b'joefoe'] - 'name': [b'joefoe'] - },... - ] - """ - - LDAP_BASE = "OU=Users,OU=Organic Units,DC=cern,DC=ch" - - LDAP_CERN_PRIMARY_ACCOUNTS_FILTER = "(&(cernAccountType=Primary))" - - LDAP_USER_RESP_FIELDS = [ - "mail", - "displayName", - "department", - "cernAccountType", - "employeeID", - "uidNumber", - "cn", - "name", - ] - - def __init__(self, ldap_url=None): - """Initialize ldap connection.""" - ldap_url = ldap_url or current_app.config["CERN_LDAP_URL"] - self.ldap = ldap.initialize(ldap_url) - - def _search_paginated_primary_account(self, page_control): - """Execute search to get primary accounts.""" - return self.ldap.search_ext( - self.LDAP_BASE, - ldap.SCOPE_ONELEVEL, - self.LDAP_CERN_PRIMARY_ACCOUNTS_FILTER, - self.LDAP_USER_RESP_FIELDS, - serverctrls=[page_control], - ) - - def get_primary_accounts(self): - """Retrieve all primary accounts from ldap.""" - page_control = ldap.controls.SimplePagedResultsControl( - True, size=1000, cookie="" - ) - - result = [] - while True: - response = self._search_paginated_primary_account(page_control) - rtype, rdata, rmsgid, serverctrls = self.ldap.result3(response) - result.extend([x[1] for x in rdata]) - - ldap_page_control = ldap.controls.SimplePagedResultsControl - ldap_page_control_type = ldap_page_control.controlType - controls = [ - control - for control in serverctrls - if control.controlType == ldap_page_control_type - ] - if not controls: - current_app.logger.exception("The server ignores RFC 2696 control") - break - if not controls[0].cookie: - break - page_control.cookie = controls[0].cookie - - return result - - # Kept as example if needed to fetch a specific user by a field - # def get_user_by_person_id(self, person_id): - # """Query ldap to retrieve user by person id.""" - # self.ldap.search_ext( - # "OU=Users,OU=Organic Units,DC=cern,DC=ch", - # ldap.SCOPE_ONELEVEL, - # "(&(cernAccountType=Primary)(employeeID={}))".format(person_id), - # self.LDAP_USER_RESP_FIELDS, - # serverctrls=[ - # ldap.controls.SimplePagedResultsControl( - # True, size=7, cookie="" - # ) - # ], - # ) - # - # res = self.ldap.result()[1] - # - # return [x[1] for x in res] diff --git a/site/cds_rdm/ldap/errors.py b/site/cds_rdm/ldap/errors.py deleted file mode 100644 index 29dcc6e..0000000 --- a/site/cds_rdm/ldap/errors.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2023 CERN. -# -# CDS-RDM is free software; you can redistribute it and/or modify it under -# the terms of the MIT License; see LICENSE file for more details. - -"""CDS-RDM ldap exceptions.""" - -import sys - -from flask import current_app - - -class InvalidLdapUser(Exception): - """Invalid user exception.""" - - def __init__(self, *args, log_func=None): - """Constructor.""" - super().__init__(*args) - if log_func: - log_func("ldap_user_has_no_email") - else: - current_app.logger.exception(args[0], file=sys.stderr) diff --git a/site/cds_rdm/ldap/user_importer.py b/site/cds_rdm/ldap/user_importer.py deleted file mode 100644 index 485b58a..0000000 --- a/site/cds_rdm/ldap/user_importer.py +++ /dev/null @@ -1,90 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2023 CERN. -# -# CDS-RDM is free software; you can redistribute it and/or modify it under -# the terms of the MIT License; see LICENSE file for more details. - -"""CDS-RDM invenio user importer API.""" - -from flask import current_app -from flask_security.confirmable import confirm_user -from invenio_accounts.models import User -from invenio_db import db -from invenio_oauthclient.models import RemoteAccount, UserIdentity -from invenio_userprofiles.models import UserProfile - - -class LdapUserImporter: - """Import ldap users to Invenio RDM records. - - Expected input format for ldap users: - [ - {'displayName': [b'Joe Foe'], - 'department': [b'IT/CDA'], - 'uidNumber': [b'100000'], - 'mail': [b'joe.foe@cern.ch'], - 'cernAccountType': [b'Primary'], - 'employeeID': [b'101010'] - },... - ] - """ - - def __init__(self, remote_account_client_id): - """Constructor.""" - self.client_id = remote_account_client_id - - def create_invenio_user(self, ldap_user): - """Commit new user in db.""" - email = ldap_user["user_email"] - username = ldap_user["user_username"] - user = User(email=email, username=username, active=True) - db.session.add(user) - db.session.commit() - return user - - def create_invenio_user_identity(self, user_id, ldap_user): - """Return new user identity entry.""" - uid_number = ldap_user["user_identity_id"] - return UserIdentity( - id=uid_number, - method=current_app.config["OAUTH_REMOTE_APP_NAME"], - id_user=user_id, - ) - - def create_invenio_user_profile(self, user, ldap_user): - """Return new user profile.""" - user_profile = UserProfile(user=user) - user_profile.full_name = ldap_user["user_profile_full_name"] - return user_profile - - def create_invenio_remote_account(self, user_id, ldap_user): - """Return new user entry.""" - keycloak_id = ldap_user["user_username"] - employee_id = ldap_user["remote_account_person_id"] - department = ldap_user["remote_account_department"] - return RemoteAccount.create( - client_id=self.client_id, - user_id=user_id, - extra_data=dict( - keycloak_id=keycloak_id, person_id=employee_id, department=department - ), - ) - - def import_user(self, ldap_user): - """Create Invenio users from LDAP export.""" - user = self.create_invenio_user(ldap_user) - user_id = user.id - - identity = self.create_invenio_user_identity(user_id, ldap_user) - db.session.add(identity) - - profile = self.create_invenio_user_profile(user, ldap_user) - db.session.add(profile) - - remote_account = self.create_invenio_remote_account(user_id, ldap_user) - db.session.add(remote_account) - - # Automatically confirm the user - confirm_user(user) - return user_id diff --git a/site/cds_rdm/ldap/utils.py b/site/cds_rdm/ldap/utils.py deleted file mode 100644 index c8e6e86..0000000 --- a/site/cds_rdm/ldap/utils.py +++ /dev/null @@ -1,127 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2023 CERN. -# -# CDS-RDM is free software; you can redistribute it and/or modify it under -# the terms of the MIT License; see LICENSE file for more details. - -"""CDS-RDM ldap serializers.""" - -from functools import partial - -from invenio_accounts.models import User -from invenio_oauthclient.models import UserIdentity -from invenio_userprofiles import UserProfile - -from cds_rdm.ldap.errors import InvalidLdapUser - - -def serialize_ldap_user(ldap_user_data, log_func=None): - """Create ldap user.""" - - def serialize(ldap_user_data): - decoded_data = {} - for key, value in ldap_user_data.items(): - # get first value of each field from LDAP - # decode bytes to UTF-8 - decoded_data[key] = value[0].decode("utf8") - serialized_data = dict( - user_email=decoded_data["mail"].lower(), - user_username=decoded_data["cn"], - user_profile_full_name=decoded_data["displayName"], - user_identity_id=decoded_data["uidNumber"], - cern_account_type=decoded_data["cernAccountType"], - remote_account_person_id=str(decoded_data["employeeID"]), - remote_account_department=decoded_data["department"], - ) - - return serialized_data - - def validate_required(ldap_user_data, employee_id, log_func): - if "mail" not in ldap_user_data: - log_func_missing_email = partial( - log_func, extra=dict(employee_id=employee_id) - ) - raise InvalidLdapUser( - f"LDAP user with employeeID {employee_id}" f"has no email address.", - log_func=log_func_missing_email, - ) - - def validate_user_data(serialized_ldap_user_data, employee_id, log_func): - """Validate user data values.""" - email = serialized_ldap_user_data["user_email"] - # check if email is empty string. - # It happens when the account is not fully created on LDAP - if not email: - log_func_missing_email = partial( - log_func, extra=dict(employee_id=employee_id) - ) - raise InvalidLdapUser( - f"LDAP user with employeeID {employee_id}" f" has no email address.", - log_func=log_func_missing_email, - ) - - employee_id = ldap_user_data["employeeID"][0].decode("utf8") - try: - validate_required(ldap_user_data, employee_id, log_func) - serialized_data = serialize(ldap_user_data) - validate_user_data(serialized_data, employee_id, log_func) - return serialized_data - except InvalidLdapUser: - return - - -def user_exists(ldap_user): - """Check if user exists in the db.""" - if not ldap_user: - return False - - email = ldap_user["user_email"] - user_email_exists = User.query.filter_by(email=email).count() > 0 - - if user_email_exists: - return True - - user_identity_exists = ( - UserIdentity.query.filter_by(id_user=ldap_user["user_identity_id"]).count() > 0 - ) - if user_identity_exists: - return True - - -class InvenioUser: - """Invenio user serializer class.""" - - def __init__(self, remote_account): - """Constructor.""" - self.user_id = remote_account.user_id - self.remote_account = remote_account - self.user_profile = UserProfile.get_by_userid(self.user_id) - self.user_identity = UserIdentity.query.filter_by(id_user=self.user_id).one() - self.user = User.query.filter_by(id=self.user_id).one() - self.data = self._get_full_user_info() - - def _get_full_user_info(self): - """Serialize data from user db models.""" - person_id = self.remote_account.extra_data.get("person_id") - person_id = str(person_id) if person_id else None - - user_info = dict( - user_profile_full_name=self.user_profile.full_name, - user_email=self.user.email, - user_username=self.user.username, - user_identity_id=self.user_identity.id, - remote_account_id=self.remote_account.id, - remote_account_person_id=person_id, - remote_account_department=self.remote_account.extra_data.get("department"), - ) - return user_info - - def update(self, ldap_user): - """Update invenio user with ldap data.""" - ra = self.remote_account - ra.extra_data["keycloak_id"] = ldap_user["user_username"] - ra.extra_data["department"] = ldap_user["remote_account_department"] - self.user.email = ldap_user["user_email"] - self.user.username = ldap_user["user_username"] - self.user_profile.full_name = ldap_user["user_profile_full_name"] diff --git a/site/cds_rdm/oidc.py b/site/cds_rdm/oidc.py deleted file mode 100644 index eb88cc6..0000000 --- a/site/cds_rdm/oidc.py +++ /dev/null @@ -1,114 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Invenio. -# Copyright (C) 2023 CERN. -# -# Invenio is free software; you can redistribute it and/or modify it -# under the terms of the MIT License; see LICENSE file for more details. - -"""OIDC settings.""" - -from flask import current_app, g -from invenio_db import db -from invenio_oauthclient import current_oauthclient, oauth_link_external_id -from invenio_oauthclient.contrib.keycloak.handlers import get_user_info -from invenio_userprofiles.forms import confirm_register_form_preferences_factory -from werkzeug.local import LocalProxy - -_security = LocalProxy(lambda: current_app.extensions["security"]) - - -def confirm_registration_form(*args, **kwargs): - """Confirm form.""" - Form = confirm_register_form_preferences_factory(_security.confirm_register_form) - - class _Form(Form): - password = None - recaptcha = None - submit = None # defined in the template - - return _Form(*args, **kwargs) - - -def cern_groups_serializer(remote, groups, **kwargs): - """Serialize the groups response object.""" - serialized_groups = [] - # E-groups do have unique names and this name cannot be updated, - # therefore the name can act as an ID for invenio - for group_name in groups: - serialized_groups.append({"id": group_name, "name": group_name}) - - return serialized_groups - - -def cern_setup_handler(remote, token, resp): - """Perform additional setup after the user has been logged in.""" - token_user_info, _ = get_user_info(remote, resp) - - with db.session.begin_nested(): - # fetch the user's Keycloak ID and set it in extra_data - keycloak_id = token_user_info["sub"] - token.remote_account.extra_data = {"keycloak_id": keycloak_id} - - # only available to CERN users - cern_person_id = token_user_info.get("cern_person_id", None) - if cern_person_id: - token.remote_account.extra_data["person_id"] = cern_person_id - - user = token.remote_account.user - external_id = {"id": keycloak_id, "method": remote.name} - - # link account with external Keycloak ID - oauth_link_external_id(user, external_id) - - -def cern_groups_handler(remote, resp): - """Retrieves groups from remote account. - - Groups are already part of the response token - """ - groups = g.pop("_cern_groups", []) - handlers = current_oauthclient.signup_handlers[remote.name] - # `remote` param automatically injected via `make_handler` helper - return handlers["groups_serializer"](groups) - - -def cern_info_handler(remote, resp): - """Info handler.""" - token_user_info, user_info = get_user_info(remote, resp) - - # Add the user_info to the request, so it can be used in the groups handler - # to avoid yet another request to the user info endpoint - g._cern_groups = user_info.get("groups", []) - - handlers = current_oauthclient.signup_handlers[remote.name] - return handlers["info_serializer"](resp, token_user_info, user_info) - - -def cern_info_serializer(remote, resp, token_user_info, user_info): - """Info serializer.""" - user_info = user_info or {} - - email = token_user_info["email"] - full_name = token_user_info["name"] - username = token_user_info["preferred_username"] - external_id = token_user_info["cern_upn"] - affiliations = user_info.get("home_institute", "") - return { - "user": { - "active": True, - "email": email, - "profile": { - "full_name": full_name, - "username": username, - "affiliations": affiliations, - }, - "prefs": { - "visibility": "public", - "email_visibility": "restricted", - "locale": "en", - }, - }, - "external_id": external_id, - "external_method": remote.name, - } diff --git a/site/cds_rdm/tasks.py b/site/cds_rdm/tasks.py index 1e447fc..cc2991e 100644 --- a/site/cds_rdm/tasks.py +++ b/site/cds_rdm/tasks.py @@ -8,135 +8,20 @@ """Celery tasks for cds.""" -import requests from celery import shared_task -from flask import current_app -from invenio_db import db -from invenio_oauthclient.handlers.utils import create_or_update_roles +from invenio_cern_sync.groups.sync import sync as groups_sync +from invenio_cern_sync.users.sync import sync as users_sync +from invenio_users_resources.services.users.tasks import reindex_users -from cds_rdm.errors import RequestError -from cds_rdm.ldap.api import update_users - -@shared_task( - bind=True, max_retries=6, default_retry_delay=60 * 10 -) # Retry every 10 min for 1 hour -def sync_groups(self): - """Synchronizes groups in CDS.""" - if current_app.config.get("DEBUG", True): - current_app.logger.warning( - "Groups sync with CERN authorization service disabled, the DEBUG env var is True." - ) - return - - token_url = f"{current_app.config['CERN_KEYCLOAK_BASE_URL']}auth/realms/cern/api-access/token" - token_data = { - "grant_type": "client_credentials", - "client_id": current_app.config["CERN_APP_CREDENTIALS"]["consumer_key"], - "client_secret": current_app.config["CERN_APP_CREDENTIALS"]["consumer_secret"], - "audience": "authorization-service-api", # This is the target api of the token - } - try: - token_response = requests.post(url=token_url, data=token_data) - except Exception as e: - while self.request.retries < self.max_retries: - self.retry() - raise RequestError(token_url, str(e)) - - if not token_response.ok: - while self.request.retries < self.max_retries: - self.retry() - raise RequestError( - token_url, - f"Request failed with status code {token_response.status_code} {token_response.reason}.", - ) - - token = token_response.json()["access_token"] - offset = 0 - limit = 1000 - groups_headers = { - "Authorization": f"Bearer {token}", - "accept": "text/plain", - } - - host = current_app.config["CERN_AUTHORIZATION_SERVICE_API"] - endpoint = current_app.config["CERN_AUTHORIZATION_SERVICE_API_GROUP"] - # We do this to get the total amount of entries, to be able to create as many celery tasks as required - url = f"{host}{endpoint}?offset={offset}&limit={limit}".format(offset=0, limit=1) - try: - groups_response = requests.get(url=url, headers=groups_headers) - except Exception as e: - while self.request.retries < self.max_retries: - self.retry() - raise RequestError(url, str(e)) - - if not groups_response.ok: - while self.request.retries < self.max_retries: - self.retry() - raise RequestError( - url, - f"Request failed with status code {groups_response.status_code}, {groups_response.reason}.", - ) - - total = groups_response.json()["pagination"]["total"] - while offset < total: - update_groups.delay(offset, limit, groups_headers) - offset += limit - - -@shared_task( - bind=True, max_retries=6, default_retry_delay=10 * 60 -) # Retry every 10 min for 1 hour -def update_groups(self, offset, limit, groups_headers): - """Celery task to fetch and update groups. - - :param offset: Offset to be sent in the request. - :param limit: Limit to be sent in the request. - :param groups_headers: Headers of the request. - """ - host = current_app.config["CERN_AUTHORIZATION_SERVICE_API"] - endpoint = current_app.config["CERN_AUTHORIZATION_SERVICE_API_GROUP"] - url = f"{host}{endpoint}?offset={offset}&limit={limit}".format( - offset=offset, limit=limit - ) - try: - groups_response = requests.get(url=url, headers=groups_headers) - except Exception as e: - while self.request.retries < self.max_retries: - self.retry() - raise RequestError(url, e) - - if not groups_response.ok: - while self.request.retries < self.max_retries: - self.retry() - raise RequestError( - url, - f"Request failed with status code {groups_response.status_code}, {groups_response.reason}.", - ) - - serialized_groups = [] - for group in groups_response.json()["data"]: - serialized_groups.append( - { - "id": group["groupIdentifier"], - "name": group["displayName"], - "description": group["description"], - } - ) - create_or_update_roles(serialized_groups) +@shared_task +def sync_users(since=None, **kwargs): + """Task to sync users with CERN database.""" + user_ids = users_sync(identities=dict(since=since)) + reindex_users.delay(user_ids) @shared_task -def sync_users(): - """Run the task to update users from LDAP.""" - if current_app.config.get("DEBUG", True): - current_app.logger.warning( - "Users sync with CERN LDAP disabled, the DEBUG env var is True." - ) - return - - try: - update_users() - except Exception as e: - db.session.rollback() - current_app.logger.exception(e) +def sync_groups(since=None, **kwargs): + """Task to sync groups with CERN database.""" + groups_sync(groups=dict(since=since)) diff --git a/site/cds_rdm/webpack.py b/site/cds_rdm/webpack.py deleted file mode 100644 index 5f40930..0000000 --- a/site/cds_rdm/webpack.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Invenio. -# Copyright (C) 2023 CERN. -# -# Invenio is free software; you can redistribute it and/or modify it -# under the terms of the MIT License; see LICENSE file for more details. - -"""JS/CSS Webpack bundles for CDS-RDM.""" - -from invenio_assets.webpack import WebpackThemeBundle - -theme = WebpackThemeBundle( - __name__, - "assets", - default="semantic-ui", - themes={ - "semantic-ui": dict( - entry={"cds-rdm-detail": "./js/cds_rdm/src/records/detail.js"}, - ), - }, -) diff --git a/site/setup.cfg b/site/setup.cfg index c6b3ba4..e2088c0 100644 --- a/site/setup.cfg +++ b/site/setup.cfg @@ -8,8 +8,6 @@ include_package_data = True packages = find: python_requires = >=3.9 zip_safe = False -install_requires = - python-ldap>=3.4.0,<3.5.0 [options.extras_require] tests = @@ -26,14 +24,14 @@ invenio_base.api_apps = cds_rdm = cds_rdm:CDS_RDM_REST invenio_base.blueprints = cds_rdm_views = cds_rdm.views:create_blueprint -invenio_assets.webpack = - cds_rdm_theme = cds_rdm.webpack:theme invenio_celery.tasks = - cds_rdm = cds_rdm.tasks + cds_rdm_tasks = cds_rdm.tasks +invenio_jobs.jobs = + sync_cern_users = cds_rdm.jobs:SyncUsers + sync_cern_groups = cds_rdm.jobs:SyncGroups invenio_pidstore.minters = legacy = cds_rdm.minters:legacy_recid_minter - [pydocstyle] add_ignore = D401,D403 diff --git a/site/tests/conftest.py b/site/tests/conftest.py index ec57acb..33d88e2 100644 --- a/site/tests/conftest.py +++ b/site/tests/conftest.py @@ -10,6 +10,8 @@ from collections import namedtuple import pytest +from cds_rdm.permissions import (CDSCommunitiesPermissionPolicy, + CDSRDMRecordPermissionPolicy) from invenio_access.models import ActionRoles from invenio_access.permissions import superuser_access, system_identity from invenio_accounts.models import Role @@ -23,11 +25,6 @@ from invenio_vocabularies.proxies import current_service as vocabulary_service from invenio_vocabularies.records.api import Vocabulary -from cds_rdm.permissions import ( - CDSCommunitiesPermissionPolicy, - CDSRDMRecordPermissionPolicy, -) - @pytest.fixture(scope="module") def app_config(app_config): @@ -40,7 +37,6 @@ def app_config(app_config): "consumer_key": "CHANGE ME", "consumer_secret": "CHANGE ME", } - app_config["CERN_LDAP_URL"] = "" # mock app_config["COMMUNITIES_PERMISSION_POLICY"] = CDSCommunitiesPermissionPolicy app_config["RDM_PERMISSION_POLICY"] = CDSRDMRecordPermissionPolicy app_config["COMMUNITIES_ALLOW_RESTRICTED"] = True diff --git a/site/tests/ldap/test_ldap.py b/site/tests/ldap/test_ldap.py deleted file mode 100644 index 2b9fe61..0000000 --- a/site/tests/ldap/test_ldap.py +++ /dev/null @@ -1,263 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2023 CERN. -# -# CDS-RDM is free software; you can redistribute it and/or modify it under -# the terms of the MIT License; see LICENSE file for more details. - -"""Test LDAP functions.""" - -from copy import deepcopy - -import pytest -from flask import current_app -from invenio_access.permissions import system_identity -from invenio_accounts.models import User -from invenio_oauthclient.models import RemoteAccount -from invenio_userprofiles.models import UserProfile -from invenio_users_resources.proxies import current_users_service -from invenio_users_resources.services.users.tasks import reindex_users -from sqlalchemy.exc import IntegrityError - -from cds_rdm.ldap.api import LdapUserImporter, update_users -from cds_rdm.ldap.utils import serialize_ldap_user - - -def test_update_users(app, db, mocker): - """Test update users with LDAP.""" - ldap_users = [ - { - "displayName": [b"New user"], - "cn": [b"newuser"], - "department": [b"A department"], - "uidNumber": [b"111"], - "mail": [b"ldap.user111@cern.ch"], - "cernAccountType": [b"Primary"], - "employeeID": [b"00111"], - "postOfficeBox": [b"M12345"], - }, - { - "displayName": [b"A new name"], - "cn": [b"anewname"], - "department": [b"A new department"], - "uidNumber": [b"222"], - "mail": [b"ldap.user222@cern.ch"], - "cernAccountType": [b"Primary"], - "employeeID": [b"00222"], - "postOfficeBox": [b"M12345"], - }, - { - "displayName": [b"Nothing changed"], - "cn": [b"nothingchanged"], - "department": [b"Same department"], - "uidNumber": [b"333"], - "mail": [b"ldap.user333@cern.ch"], - "cernAccountType": [b"Primary"], - "employeeID": [b"00333"], - "postOfficeBox": [b"M12345"], - }, - { - "displayName": [b"Name 1"], - "cn": [b"name1"], - "department": [b"Department 1"], - "uidNumber": [b"555"], - "mail": [b"ldap.user555@cern.ch"], - "cernAccountType": [b"Primary"], - "employeeID": [b"00555"], - "postOfficeBox": [b"M12345"], - }, - { - "displayName": [b"Name 2"], - "cn": [b"name2"], - "department": [b"Department 2"], - "uidNumber": [b"666"], - "mail": [b"ldap.user555@cern.ch"], # same email as 555 - "cernAccountType": [b"Primary"], - "employeeID": [b"00666"], - "postOfficeBox": [b"M12345"], - }, - { - "displayName": [b"Name"], - "cn": [b"name"], - "department": [b"Department"], - "uidNumber": [b"777"], - # missing email, should be skipped - "cernAccountType": [b"Primary"], - "employeeID": [b"00777"], - "postOfficeBox": [b"M12345"], - }, - { - "displayName": [b"Name"], - "cn": [b"name"], - "department": [b"Department"], - "uidNumber": [b"999"], - # custom emails allowed - "mail": [b"ldap.user999@test.ch"], - "cernAccountType": [b"Primary"], - "employeeID": [b"00999"], - "postOfficeBox": [b"M12345"], - }, - { - "displayName": [b"Nothing changed"], - "cn": [b"nothingchanged"], - "department": [b"Same department"], - "uidNumber": [b"333"], - # same email as 333, different employee ID, should be skipped - "mail": [b"ldap.user333@cern.ch"], - "cernAccountType": [b"Primary"], - "employeeID": [b"9152364"], - "postOfficeBox": [b"M12345"], - }, - { - "displayName": [b"Name"], - "cn": [b"name"], - "department": [b"Department"], - "uidNumber": [b"444"], - # empty email should be skipped - "mail": [b""], - "cernAccountType": [b"Primary"], - "employeeID": [b"00444"], - "postOfficeBox": [b"M12345"], - }, - ] - - def _prepare(): - """Prepare data.""" - remote_account_client_id = current_app.config["CERN_APP_CREDENTIALS"][ - "consumer_key" - ] - importer = LdapUserImporter(remote_account_client_id) - # Prepare users in DB. Use `LdapUserImporter` to make it easy - # create old users - WILL_BE_UPDATED = deepcopy(ldap_users[1]) - WILL_BE_UPDATED["displayName"] = [b"Previous name"] - WILL_BE_UPDATED["cn"] = [b"previousname"] - WILL_BE_UPDATED["department"] = [b"Old department"] - ldap_user = serialize_ldap_user(WILL_BE_UPDATED) - importer.import_user(ldap_user) - - WILL_NOT_CHANGE = deepcopy(ldap_users[2]) - ldap_user = serialize_ldap_user(WILL_NOT_CHANGE) - user_id1 = importer.import_user(ldap_user) - - # create a user that does not exist anymore in LDAP, but will not - # be deleted for safety - COULD_BE_DELETED = { - "displayName": [b"old user left CERN"], - "cn": [b"olduserleftcern"], - "department": [b"Department"], - "uidNumber": [b"444"], - "mail": [b"ldap.user444@cern.ch"], - "cernAccountType": [b"Primary"], - "employeeID": [b"00444"], - "postOfficeBox": [b"M12345"], - } - ldap_user = serialize_ldap_user(COULD_BE_DELETED) - user_id2 = importer.import_user(ldap_user) - db.session.commit() - reindex_users.delay([user_id1, user_id2]) - - def _prepare_duplicate(): - duplicated = { - "displayName": [b"Name 2"], - "cn": [b"name2"], - "department": [b"Department 2"], - # same id as one of the previous, different emails - # should be skipped - "uidNumber": [b"555"], - "mail": [b"other555@cern.ch"], - "cernAccountType": [b"Primary"], - "employeeID": [b"00555"], - "postOfficeBox": [b"M12345"], - } - remote_account_client_id = current_app.config["CERN_APP_CREDENTIALS"][ - "consumer_key" - ] - importer = LdapUserImporter(remote_account_client_id) - ldap_user = serialize_ldap_user(duplicated) - user_id1 = importer.import_user(ldap_user) - db.session.commit() - reindex_users.delay(user_id1) - - _prepare() - - # mock LDAP response - mocker.patch( - "cds_rdm.ldap.client.LdapClient.get_primary_accounts", - return_value=ldap_users, - ) - - n_ldap, n_updated, n_added = update_users() - current_users_service.indexer.process_bulk_queue() - current_users_service.record_cls.index.refresh() - - assert n_ldap == 9 - assert n_updated == 1 # 00222 - assert n_added == 3 # 00111, 00555, 00999 - - invenio_users = User.query.all() - # 4 in the prepared data - # 2 newly added from LDAP - assert len(invenio_users) == 6 - - def check_existence( - expected_email, - expected_username, - expected_name, - expected_department, - expected_person_id, - expected_mailbox, - ): - """Assert exist in DB and ES.""" - # check if saved in DB - user = User.query.filter_by(email=expected_email).one() - up = UserProfile.get_by_userid(user.id) - assert up.full_name == expected_name - ra = RemoteAccount.query.filter_by(user_id=user.id).one() - assert ra.extra_data["keycloak_id"] == expected_username - assert ra.extra_data["department"] == expected_department - assert ra.extra_data["person_id"] == expected_person_id - - # check if indexed correctly - results = current_users_service.search( - system_identity, q=f"username:{user.username}" - ) - assert results.total == 1 - patron_hit = [r for r in results][0] - assert patron_hit["email"] == expected_email - assert patron_hit["username"] == expected_username - - check_existence( - "ldap.user111@cern.ch", "newuser", "New user", "A department", "00111", "M12345" - ) - check_existence( - "ldap.user222@cern.ch", - "anewname", - "A new name", - "A new department", - "00222", - "M12345", - ) - check_existence( - "ldap.user333@cern.ch", - "nothingchanged", - "Nothing changed", - "Same department", - "00333", - "M12345", - ) - check_existence( - "ldap.user444@cern.ch", - "olduserleftcern", - "old user left CERN", - "Department", - "00444", - "M12345", - ) - check_existence( - "ldap.user555@cern.ch", "name1", "Name 1", "Department 1", "00555", "M12345" - ) - - # try ot import duplicated userUID - with pytest.raises(IntegrityError): - _prepare_duplicate()