From 24ca1375502a58bcab5ad8b3aee0d936e96fd6c6 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Fri, 12 Aug 2022 14:30:06 -0700 Subject: [PATCH 1/2] Add GID support Allow explicitly specifying the GID, either per user or with a spec. Default to setting the GID to the same as the UID, which was the behavior previously forced in nublado2, if the UID was specified. If it was not specified, leave the behavior to Gafaelfawr, but document that synthetic user private groups need to be enabled. --- src/mobu/flock.py | 6 ++++- src/mobu/models/user.py | 34 +++++++++++++++++++++++- tests/autostart_test.py | 10 ++++++- tests/business/jupyterpythonloop_test.py | 5 +++- tests/handlers/flock_test.py | 3 +++ tests/support/gafaelfawr.py | 4 +++ tests/user_test.py | 2 +- 7 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/mobu/flock.py b/src/mobu/flock.py index 322075ad..7ea4a9e2 100644 --- a/src/mobu/flock.py +++ b/src/mobu/flock.py @@ -137,6 +137,10 @@ def _users_from_spec(self, spec: UserSpec, count: int) -> List[User]: uid = spec.uid_start + i - 1 else: uid = None - user = User(username=username, uidnumber=uid) + if spec.gid_start is not None: + gid = spec.gid_start + i - 1 + else: + gid = None + user = User(username=username, uidnumber=uid, gidnumber=gid) users.append(user) return users diff --git a/src/mobu/models/user.py b/src/mobu/models/user.py index 23c99338..fc47073d 100644 --- a/src/mobu/models/user.py +++ b/src/mobu/models/user.py @@ -26,6 +26,18 @@ class User(BaseModel): example=60001, ) + gidnumber: Optional[int] = Field( + None, + title="Primary GID", + description=( + "If omitted but a UID was specified, use a GID equal to the UID." + " If both are omitted, Gafaelfawr will assign a UID and GID." + " (Gafaelfawr UID and GID assignment requires Firestore and" + " synthetic user private groups to be configured.)" + ), + example=60001, + ) + class UserSpec(BaseModel): """Configuration to generate a set of users.""" @@ -48,6 +60,19 @@ class UserSpec(BaseModel): example=60000, ) + gid_start: Optional[int] = Field( + None, + title="Starting GID", + description=( + "Users will be given consecutive primary GIDs starting with this." + " If omitted but UIDs were given, the GIDs will be equal to the" + " UIDs. If both are omitted, Gafaelfawr will assign UIDs and GIDs" + " (which requires Firestore and synthetic user private groups to" + " be configured)." + ), + example=60000, + ) + class AuthenticatedUser(User): """Represents an authenticated user with a token.""" @@ -79,6 +104,12 @@ async def create( } if user.uidnumber is not None: data["uid"] = user.uidnumber + if user.gidnumber is not None: + data["gid"] = user.gidnumber + else: + data["gid"] = user.uidnumber + elif user.gidnumber is not None: + data["gid"] = user.gidnumber r = await session.post( token_url, headers={"Authorization": f"Bearer {config.gafaelfawr_token}"}, @@ -88,7 +119,8 @@ async def create( body = await r.json() return cls( username=user.username, - uidnumber=user.uidnumber, + uidnumber=data["uid"] if "uid" in data else None, + gidnumber=data["gid"] if "gid" in data else None, token=body["token"], scopes=scopes, ) diff --git a/tests/autostart_test.py b/tests/autostart_test.py index 5438acb2..e5ae2210 100644 --- a/tests/autostart_test.py +++ b/tests/autostart_test.py @@ -19,6 +19,7 @@ user_spec: username_prefix: testuser uid_start: 1000 + gid_start: 2000 scopes: ["exec:notebook"] business: Business - name: python @@ -71,6 +72,7 @@ async def test_autostart(client: AsyncClient) -> None: "scopes": ["exec:notebook"], "token": ANY, "uidnumber": 1000 + i - 1, + "gidnumber": 2000 + i - 1, "username": f"testuser{i:02d}", }, } @@ -81,7 +83,11 @@ async def test_autostart(client: AsyncClient) -> None: "config": { "name": "basic", "count": 10, - "user_spec": {"username_prefix": "testuser", "uid_start": 1000}, + "user_spec": { + "username_prefix": "testuser", + "uid_start": 1000, + "gid_start": 2000, + }, "scopes": ["exec:notebook"], "business": "Business", }, @@ -140,6 +146,7 @@ async def test_autostart(client: AsyncClient) -> None: "token": ANY, "username": "python", "uidnumber": 60000, + "gidnumber": 60000, }, }, { @@ -165,6 +172,7 @@ async def test_autostart(client: AsyncClient) -> None: "token": ANY, "username": "otherpython", "uidnumber": 70000, + "gidnumber": 70000, }, }, ], diff --git a/tests/business/jupyterpythonloop_test.py b/tests/business/jupyterpythonloop_test.py index 1744ee2e..bd9d3226 100644 --- a/tests/business/jupyterpythonloop_test.py +++ b/tests/business/jupyterpythonloop_test.py @@ -18,7 +18,9 @@ async def test_run( client: AsyncClient, mock_aioresponses: aioresponses ) -> None: - mock_gafaelfawr(mock_aioresponses, username="testuser1", uid=1000) + mock_gafaelfawr( + mock_aioresponses, username="testuser1", uid=1000, gid=1000 + ) r = await client.put( "/mobu/flocks", @@ -53,6 +55,7 @@ async def test_run( "scopes": ["exec:notebook"], "token": ANY, "uidnumber": 1000, + "gidnumber": 1000, "username": "testuser1", }, } diff --git a/tests/handlers/flock_test.py b/tests/handlers/flock_test.py index 4b022eb5..dfe5ea77 100644 --- a/tests/handlers/flock_test.py +++ b/tests/handlers/flock_test.py @@ -140,6 +140,7 @@ async def test_user_list( { "username": "testuser", "uidnumber": 1000, + "gidnumber": 1056, }, { "username": "otheruser", @@ -169,6 +170,7 @@ async def test_user_list( "scopes": ["exec:notebook"], "token": ANY, "uidnumber": 1000, + "gidnumber": 1056, "username": "testuser", }, }, @@ -186,6 +188,7 @@ async def test_user_list( "scopes": ["exec:notebook"], "token": ANY, "uidnumber": 60000, + "gidnumber": 60000, "username": "otheruser", }, }, diff --git a/tests/support/gafaelfawr.py b/tests/support/gafaelfawr.py index 8aa3e1c9..59ef04ec 100644 --- a/tests/support/gafaelfawr.py +++ b/tests/support/gafaelfawr.py @@ -35,6 +35,7 @@ def mock_gafaelfawr( mocked: aioresponses, username: Optional[str] = None, uid: Optional[int] = None, + gid: Optional[int] = None, *, any_uid: bool = False, ) -> None: @@ -59,8 +60,11 @@ def handler(url: str, **kwargs: Any) -> CallbackResult: } if uid: expected["uid"] = uid + if gid: + expected["gid"] = gid elif any_uid: expected["uid"] = ANY + expected["gid"] = ANY assert kwargs["json"] == expected assert kwargs["json"]["token_name"].startswith("mobu ") assert kwargs["json"]["expires"] > time.time() diff --git a/tests/user_test.py b/tests/user_test.py index ff77e1c3..0c706dfb 100644 --- a/tests/user_test.py +++ b/tests/user_test.py @@ -12,7 +12,7 @@ @pytest.mark.asyncio async def test_generate_token(mock_aioresponses: aioresponses) -> None: - mock_gafaelfawr(mock_aioresponses, "someuser", 1234) + mock_gafaelfawr(mock_aioresponses, "someuser", 1234, 1234) config = User(username="someuser", uidnumber=1234) scopes = ["exec:notebook"] From 76ae23d9abd907fdff8c35df16106a3dc03fbc58 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Fri, 19 Aug 2022 14:20:04 -0700 Subject: [PATCH 2/2] Update dependencies --- requirements/dev.txt | 105 +++++++++++++++++++++++------------------- requirements/main.txt | 6 +-- 2 files changed, 60 insertions(+), 51 deletions(-) diff --git a/requirements/dev.txt b/requirements/dev.txt index ad97101f..5eb86447 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -130,48 +130,57 @@ charset-normalizer==2.1.0 \ # via # -c requirements/main.txt # aiohttp -coverage[toml]==6.4.3 \ - --hash=sha256:04010af3c06ce2bfeb3b1e4e05d136f88d88c25f76cd4faff5d1fd84d11581ea \ - --hash=sha256:05de0762c1caed4a162b3e305f36cf20a548ff4da0be6766ad5c870704be3660 \ - --hash=sha256:068d6f2a893af838291b8809c876973d885543411ea460f3e6886ac0ee941732 \ - --hash=sha256:0a84376e4fd13cebce2c0ef8c2f037929c8307fb94af1e5dbe50272a1c651b5d \ - --hash=sha256:0e34247274bde982bbc613894d33f9e36358179db2ed231dd101c48dd298e7b0 \ - --hash=sha256:0e3a41aad5919613483aad9ebd53336905cab1bd6788afd3995c2a972d89d795 \ - --hash=sha256:306788fd019bb90e9cbb83d3f3c6becad1c048dd432af24f8320cf38ac085684 \ - --hash=sha256:39ebd8e120cb77a06ee3d5fc26f9732670d1c397d7cd3acf02f6f62693b89b80 \ - --hash=sha256:411fdd9f4203afd93b056c0868c8f9e5e16813e765de962f27e4e5798356a052 \ - --hash=sha256:4822327b35cb032ff16af3bec27f73985448f08e874146b5b101e0e558b613dd \ - --hash=sha256:52f8b9fcf3c5e427d51bbab1fb92b575a9a9235d516f175b24712bcd4b5be917 \ - --hash=sha256:53c8edd3b83a4ddba3d8c506f1359401e7770b30f2188f15c17a338adf5a14db \ - --hash=sha256:555a498999c44f5287cc95500486cd0d4f021af9162982cbe504d4cb388f73b5 \ - --hash=sha256:59fc88bc13e30f25167e807b8cad3c41b7218ef4473a20c86fd98a7968733083 \ - --hash=sha256:5a559aab40c716de80c7212295d0dc96bc1b6c719371c20dd18c5187c3155518 \ - --hash=sha256:5de1e9335e2569974e20df0ce31493d315a830d7987e71a24a2a335a8d8459d3 \ - --hash=sha256:6630d8d943644ea62132789940ca97d05fac83f73186eaf0930ffa715fbdab6b \ - --hash=sha256:73a10939dc345460ca0655356a470dd3de9759919186a82383c87b6eb315faf2 \ - --hash=sha256:7856ea39059d75f822ff0df3a51ea6d76307c897048bdec3aad1377e4e9dca20 \ - --hash=sha256:877ee5478fd78e100362aed56db47ccc5f23f6e7bb035a8896855f4c3e49bc9b \ - --hash=sha256:920a734fe3d311ca01883b4a19aa386c97b82b69fbc023458899cff0a0d621b9 \ - --hash=sha256:923f9084d7e1d31b5f74c92396b05b18921ed01ee5350402b561a79dce3ea48d \ - --hash=sha256:a0d2df4227f645a879010461df2cea6b7e3fb5a97d7eafa210f7fb60345af9e8 \ - --hash=sha256:a2738ba1ee544d6f294278cfb6de2dc1f9a737a780469b5366e662a218f806c3 \ - --hash=sha256:a42eaaae772f14a5194f181740a67bfd48e8806394b8c67aa4399e09d0d6b5db \ - --hash=sha256:ab2b1a89d2bc7647622e9eaf06128a5b5451dccf7c242deaa31420b055716481 \ - --hash=sha256:ab9ef0187d6c62b09dec83a84a3b94f71f9690784c84fd762fb3cf2d2b44c914 \ - --hash=sha256:adf1a0d272633b21d645dd6e02e3293429c1141c7d65a58e4cbcd592d53b8e01 \ - --hash=sha256:b104b6b1827d6a22483c469e3983a204bcf9c6bf7544bf90362c4654ebc2edf3 \ - --hash=sha256:bc698580216050b5f4a34d2cdd2838b429c53314f1c4835fab7338200a8396f2 \ - --hash=sha256:cdf7b83f04a313a21afb1f8730fe4dd09577fefc53bbdfececf78b2006f4268e \ - --hash=sha256:d5191d53afbe5b6059895fa7f58223d3751c42b8101fb3ce767e1a0b1a1d8f87 \ - --hash=sha256:d75314b00825d70e1e34b07396e23f47ed1d4feedc0122748f9f6bd31a544840 \ - --hash=sha256:e4d64304acf79766e650f7acb81d263a3ea6e2d0d04c5172b7189180ff2c023c \ - --hash=sha256:ec2ae1f398e5aca655b7084392d23e80efb31f7a660d2eecf569fb9f79b3fb94 \ - --hash=sha256:eff095a5aac7011fdb51a2c82a8fae9ec5211577f4b764e1e59cfa27ceeb1b59 \ - --hash=sha256:f1eda5cae434282712e40b42aaf590b773382afc3642786ac3ed39053973f61f \ - --hash=sha256:f217850ac0e046ede611312703423767ca032a7b952b5257efac963942c055de \ - --hash=sha256:f50d3a822947572496ea922ee7825becd8e3ae6fbd2400cd8236b7d64b17f285 \ - --hash=sha256:fc294de50941d3da66a09dca06e206297709332050973eca17040278cb0918ff \ - --hash=sha256:ff9832434a9193fbd716fbe05f9276484e18d26cc4cf850853594bb322807ac3 +coverage[toml]==6.4.4 \ + --hash=sha256:01778769097dbd705a24e221f42be885c544bb91251747a8a3efdec6eb4788f2 \ + --hash=sha256:08002f9251f51afdcc5e3adf5d5d66bb490ae893d9e21359b085f0e03390a820 \ + --hash=sha256:1238b08f3576201ebf41f7c20bf59baa0d05da941b123c6656e42cdb668e9827 \ + --hash=sha256:14a32ec68d721c3d714d9b105c7acf8e0f8a4f4734c811eda75ff3718570b5e3 \ + --hash=sha256:15e38d853ee224e92ccc9a851457fb1e1f12d7a5df5ae44544ce7863691c7a0d \ + --hash=sha256:354df19fefd03b9a13132fa6643527ef7905712109d9c1c1903f2133d3a4e145 \ + --hash=sha256:35ef1f8d8a7a275aa7410d2f2c60fa6443f4a64fae9be671ec0696a68525b875 \ + --hash=sha256:4179502f210ebed3ccfe2f78bf8e2d59e50b297b598b100d6c6e3341053066a2 \ + --hash=sha256:42c499c14efd858b98c4e03595bf914089b98400d30789511577aa44607a1b74 \ + --hash=sha256:4b7101938584d67e6f45f0015b60e24a95bf8dea19836b1709a80342e01b472f \ + --hash=sha256:564cd0f5b5470094df06fab676c6d77547abfdcb09b6c29c8a97c41ad03b103c \ + --hash=sha256:5f444627b3664b80d078c05fe6a850dd711beeb90d26731f11d492dcbadb6973 \ + --hash=sha256:6113e4df2fa73b80f77663445be6d567913fb3b82a86ceb64e44ae0e4b695de1 \ + --hash=sha256:61b993f3998ee384935ee423c3d40894e93277f12482f6e777642a0141f55782 \ + --hash=sha256:66e6df3ac4659a435677d8cd40e8eb1ac7219345d27c41145991ee9bf4b806a0 \ + --hash=sha256:67f9346aeebea54e845d29b487eb38ec95f2ecf3558a3cffb26ee3f0dcc3e760 \ + --hash=sha256:6913dddee2deff8ab2512639c5168c3e80b3ebb0f818fed22048ee46f735351a \ + --hash=sha256:6a864733b22d3081749450466ac80698fe39c91cb6849b2ef8752fd7482011f3 \ + --hash=sha256:7026f5afe0d1a933685d8f2169d7c2d2e624f6255fb584ca99ccca8c0e966fd7 \ + --hash=sha256:783bc7c4ee524039ca13b6d9b4186a67f8e63d91342c713e88c1865a38d0892a \ + --hash=sha256:7a98d6bf6d4ca5c07a600c7b4e0c5350cd483c85c736c522b786be90ea5bac4f \ + --hash=sha256:8d032bfc562a52318ae05047a6eb801ff31ccee172dc0d2504614e911d8fa83e \ + --hash=sha256:98c0b9e9b572893cdb0a00e66cf961a238f8d870d4e1dc8e679eb8bdc2eb1b86 \ + --hash=sha256:9c7b9b498eb0c0d48b4c2abc0e10c2d78912203f972e0e63e3c9dc21f15abdaa \ + --hash=sha256:9cc4f107009bca5a81caef2fca843dbec4215c05e917a59dec0c8db5cff1d2aa \ + --hash=sha256:9d6e1f3185cbfd3d91ac77ea065d85d5215d3dfa45b191d14ddfcd952fa53796 \ + --hash=sha256:a095aa0a996ea08b10580908e88fbaf81ecf798e923bbe64fb98d1807db3d68a \ + --hash=sha256:a3b2752de32c455f2521a51bd3ffb53c5b3ae92736afde67ce83477f5c1dd928 \ + --hash=sha256:ab066f5ab67059d1f1000b5e1aa8bbd75b6ed1fc0014559aea41a9eb66fc2ce0 \ + --hash=sha256:c1328d0c2f194ffda30a45f11058c02410e679456276bfa0bbe0b0ee87225fac \ + --hash=sha256:c35cca192ba700979d20ac43024a82b9b32a60da2f983bec6c0f5b84aead635c \ + --hash=sha256:cbbb0e4cd8ddcd5ef47641cfac97d8473ab6b132dd9a46bacb18872828031685 \ + --hash=sha256:cdbb0d89923c80dbd435b9cf8bba0ff55585a3cdb28cbec65f376c041472c60d \ + --hash=sha256:cf2afe83a53f77aec067033199797832617890e15bed42f4a1a93ea24794ae3e \ + --hash=sha256:d5dd4b8e9cd0deb60e6fcc7b0647cbc1da6c33b9e786f9c79721fd303994832f \ + --hash=sha256:dfa0b97eb904255e2ab24166071b27408f1f69c8fbda58e9c0972804851e0558 \ + --hash=sha256:e16c45b726acb780e1e6f88b286d3c10b3914ab03438f32117c4aa52d7f30d58 \ + --hash=sha256:e1fabd473566fce2cf18ea41171d92814e4ef1495e04471786cbc943b89a3781 \ + --hash=sha256:e3d3c4cc38b2882f9a15bafd30aec079582b819bec1b8afdbde8f7797008108a \ + --hash=sha256:e431e305a1f3126477abe9a184624a85308da8edf8486a863601d58419d26ffa \ + --hash=sha256:e7b4da9bafad21ea45a714d3ea6f3e1679099e420c8741c74905b92ee9bfa7cc \ + --hash=sha256:ee2b2fb6eb4ace35805f434e0f6409444e1466a47f620d1d5763a22600f0f892 \ + --hash=sha256:ee6ae6bbcac0786807295e9687169fba80cb0617852b2fa118a99667e8e6815d \ + --hash=sha256:ef6f44409ab02e202b31a05dd6666797f9de2aa2b4b3534e9d450e42dea5e817 \ + --hash=sha256:f67cf9f406cf0d2f08a3515ce2db5b82625a7257f88aad87904674def6ddaec1 \ + --hash=sha256:f855b39e4f75abd0dfbcf74a82e84ae3fc260d523fcb3532786bcbbcb158322c \ + --hash=sha256:fc600f6ec19b273da1d85817eda339fb46ce9eef3e89f220055d8696e0a06908 \ + --hash=sha256:fcbe3d9a53e013f8ab88734d7e517eb2cd06b7e689bedf22c0eb68db5e4a0a19 \ + --hash=sha256:fde17bc42e0716c94bf19d92e4c9f5a00c5feb401f5bc01101fdf2a8b7cacf60 \ + --hash=sha256:ff934ced84054b9018665ca3967fc48e1ac99e811f6cc99ea65978e1d384454b # via # -r requirements/dev.in # pytest-cov @@ -495,13 +504,13 @@ types-pyyaml==6.0.11 \ --hash=sha256:7f7da2fd11e9bc1e5e9eb3ea1be84f4849747017a59fc2eee0ea34ed1147c2e0 \ --hash=sha256:8f890028123607379c63550179ddaec4517dc751f4c527a52bb61934bf495989 # via -r requirements/dev.in -types-requests==2.28.8 \ - --hash=sha256:7a9f7b152d594a1c18dd4932cdd2596b8efbeedfd73caa4e4abb3755805b4685 \ - --hash=sha256:b0421f9f2d0dd0f8df2c75f974686517ca67473f05b466232d4c6384d765ad7a +types-requests==2.28.9 \ + --hash=sha256:86cb66d3de2f53eac5c09adc42cf6547eefbd0c7e1210beca1ee751c35d96083 \ + --hash=sha256:feaf581bd580497a47fe845d506fa3b91b484cf706ff27774e87659837de9962 # via -r requirements/dev.in -types-urllib3==1.26.22 \ - --hash=sha256:09a8783e1002472e8d1e1f3792d4c5cca1fffebb9b48ee1512aae6d16fe186bc \ - --hash=sha256:b05af90e73889e688094008a97ca95788db8bf3736e2776fd43fb6b171485d94 +types-urllib3==1.26.23 \ + --hash=sha256:333e675b188a1c1fd980b4b352f9e40572413a4c1ac689c23cd546e96310070a \ + --hash=sha256:b78e819f0e350221d0689a5666162e467ba3910737bafda14b5c2c85e9bb1e56 # via types-requests typing-extensions==4.3.0 \ --hash=sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02 \ diff --git a/requirements/main.txt b/requirements/main.txt index d8193167..a67aaaa4 100644 --- a/requirements/main.txt +++ b/requirements/main.txt @@ -146,9 +146,9 @@ click==8.1.3 \ # via # -r requirements/main.in # uvicorn -fastapi==0.79.0 \ - --hash=sha256:cf0ff6db25b91d321050c4112baab0908c90f19b40bf257f9591d2f9780d1f22 \ - --hash=sha256:d337563424ceada23857f73d5abe8dae0c28e4cccb53b2af06e78b7bb4a1c7d7 +fastapi==0.79.1 \ + --hash=sha256:006862dec0f0f5683ac21fb0864af2ff12a931e7ba18920f28cc8eceed51896b \ + --hash=sha256:3c584179c64e265749e88221c860520fc512ea37e253282dab378cc503dfd7fd # via # -r requirements/main.in # safir