Repository navigation
Conversation
GET /api/items/exchange returns the items as an item exchange file of the MuMain client, which its editor downloads with "Update from server". - The file has the format version 2 and a "fields" list: the item fields it carries. The client compares and applies only these and keeps its own values of the others, so the fields can be added one at a time. Items are sorted by group and number; items outside the client's groups and numbers are left out. No field is listed yet. - The endpoint needs an API key with the Viewer role (the login cookie isn't accepted) and refuses requests while the admin panel has no user, because anybody can create a key in that setup mode. - It reads the saved configuration in a context of its own.
The page said that only the item exchange file needs a key, but changing an account's password or state and disconnecting a player don't accept the login cookie either. These rows now say "key only" as well.
The controller repeated the scheme which the policy already names. With two places, changing the attribute to the schemes of the other controllers would quietly accept the login cookie again.
OpenMU doesn't keep the group and number of an item unique, and the order in which the configuration is loaded can differ. Items which share them now follow their id, so the same configuration always gives the same file (which a later ETag depends on).
The export loaded the whole configuration for each request in a new context, and the repository attached all of it to a change tracker. It now reads through ConfigurationReadScope, like the configuration reads of the agents: the configuration which the admin panel has already loaded, or a copy of the stored one while the admin panel has unsaved changes. Unsaved edits still aren't part of the file. When an edit changes the items while they are read, the file is written again, at most three times.
The tests read a configuration which only had stored data, so nothing showed that edits the admin panel hasn't saved are left out. Now: - with unsaved changes, the stored configuration is written; - without, the configuration the admin panel has loaded is written; - AddAdminReadOperations registers the export, which the controller needs (the API tests register it by hand, because their host doesn't have the services of the other operations).
…e file The file always said "season6", so the client accepted the items of a 0.75 or 0.95d server as Season 6 data. The file now names the key of the data initialization which created the configuration, as the configuration updates find it: the stored update state, or for an older configuration its Season 6 client. The client then refuses the file of another version. When neither tells the version, the export is refused (503).
Items outside the client's groups and numbers were left out without a word, so neither the server operator nor the user of the client editor learned that the file is missing them. The export now logs a warning with their group, number and name.
While the admin panel has no user, anybody who reaches it can open the API keys page. A key created then kept working after the first user existed, so refusing the item exchange file in that setup mode didn't protect it: the key only had to wait. The same held for every other key endpoint. The API keys page now refuses to create a key until the first user exists, which covers all endpoints. The item exchange file's own setup-mode requirement is gone with it. In setup mode the whole panel is open anyway, and the requirement also refused valid keys while its cached "no user" answer was stale, with a 403 which the client editor reads as a missing role.
An export reads the whole item configuration, and loads it again while the admin panel has unsaved changes. Any Viewer key could start as many of them at once as it liked. A request while an export is running now gets 429 Too Many Requests with Retry-After, which the client editor already reports. Rate limits per key and a cached export with an ETag are still to come.
The test refused the item exchange file to a signed-in browser, but never showed that its cookie signed in at all, so it would also pass when the cookie got lost. It now checks the sign-in on an endpoint which accepts the cookie first.
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 92 |
| Duplication | 17 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
…operties Codacy (Sonar S2339) asks for static read-only properties instead of public constants, as other public values of OpenMU are written. The check of the client's numbers compares the number instead of matching it in a pattern, which needs a constant.
…ation reads The retry loop had no stop condition of its own (Sonar S1994): it ended through the exception filter. It now tries as ConfigurationReadOperations does: the attempts but the last catch a change during the read, and the last one lets it through.
…I tests Codacy's Sonar doesn't see the global usings and took the fixture for a class which doesn't dispose its HTTP client (S2931), as it did for the account administration API tests (e813622).
markdownlint (MD004) wants one list marker per page; the page's other lists use asterisks.
sven-n
left a comment
There was a problem hiding this comment.
Changes requested: src/Web/AdminPanel/Services/ApiKeyManagementService.cs:63-69 blocks creation of new API keys before initial administrator setup, but previously issued setup-mode keys remain valid and can access endpoints including src/Web/AdminPanel/API/ItemExchangeController.cs:17-21 if sufficiently privileged. Please provide a way to identify/revoke or rotate potentially exposed legacy keys, with administrator guidance. Also, the check in ApiKeyManagementService.cs:65-100 occurs before an asynchronous modal interaction; recheck at persistence time to prevent creating a key after the last administrator is deleted. Add a concurrency regression test.
The API keys page checked for a user only when the dialog of a new key opened, from the cached answer of the request authorization. The dialog can stay open for long; a key confirmed after the last user was gone was still stored. AdminPanelSetupMode asks the storage whether there is a user (or a bootstrap user). The key service asks it when the dialog opens and again right before the key is stored. A test deletes the last user while the dialog is open and confirms it afterwards: no key, an error. It fails without the second check.
Keys created while the admin panel had no user kept working: anybody who reached the panel then could have created one. They are disabled once, so that an administrator enables only the keys they created: - the migration DisableApiKeysCreatedBeforeTheFirstUser disables every key which is older than the oldest admin user; a migration runs once, so keys enabled again afterwards stay enabled, - creating the first user in the admin panel disables every key which exists then, for installations which update while still in setup mode. The new user gets a warning with the number of keys. The API keys page marks the keys created before the first user and says what to do with them; the docs explain it, also why nothing is disabled with only a bootstrap user. The texts are in English, German and Chinese.
Codacy's Sonar can't tell that the test dialogs implement IModalService and took a parameter of each for unused (S1172). The user dialog checks that it fills in a dialog which creates something, and the pending dialog keeps the parameters it was opened with, which the test checks instead of a flag.
Codacy's Sonar names one unused parameter per method at a time (S1172), so using one parameter of each test dialog only moved the warning to the next. Both test dialogs now keep each dialog they show, with all its parameters, and the tests check that exactly one dialog was shown. The check of the user dialog's component type is gone again.
|
Thanks, both points are addressed:
|
Items phase 7, step 3: the admin panel serves the items as an item exchange file of the MuMain client. MuMain's editor (MuEditor) downloads it with "Update from server" and shows what differs from the client's items before anything changes.
This PR is the endpoint without item fields yet: the fields follow in their own PRs, a group at a time, so each mapping can be reviewed with the differences it shows.
The endpoint
GET /api/items/exchangereturns the items of the saved configuration as the client's exchange file.AdminPolicies.ApiKeyViewer); the login cookie isn't accepted.429 Too Many RequestswithRetry-After, which MuEditor already reports. Rate limits per key and a cached export with an ETag follow later.ConfigurationReadScope, like the configuration reads of the agents: the configuration which the admin panel has already loaded, or a copy of the stored one while the admin panel has unsaved changes. So unsaved changes aren't part of the file, and the configuration usually isn't loaded again for each request.[NotExposedToAgents]).API keys need a user
The API keys page creates a key only once the admin panel has its first user. While there is no user, anybody who reaches the admin panel can open that page, and a key created then would keep working after the first user exists. This covers every endpoint which takes a key, not only this one.
DisableApiKeysCreatedBeforeTheFirstUserdisables every key which is older than the oldest admin user. A migration runs once, so keys enabled again afterwards stay enabled.An earlier version of this PR refused only the export while the admin panel has no user. That didn't protect the file: a key created in that setup mode just had to wait for the first user. That check is gone.
The texts are in English, German and Chinese.
The file
fieldslist: the item fields the file carries. The client compares and applies only these and keeps its own values of every other field, and an older client refuses the file instead of reading the missing fields as zero.gameVersionis the key of the data initialization which created the configuration (season6,0.75or0.95d), found like the configuration updates find it: the stored update state, or for an older configuration its Season 6 client. The client only readsseason6, so it refuses the file of a 0.75 or 0.95d server. When neither tells the version, the export is refused (503).Docs
docs-website/docs/admin-panel/authentication.md: the item exchange file, keys only after the first user, what happens to the keys from before the first user and what to do with them, and the endpoints which accept only a key are marked "key only" in the table (changing an account's password or state and disconnecting a player already were).Testing
Built with
-p:ci=true.MUnique.OpenMU.Web.Tests: 531 pass. The 16 failures are the network analyzer tests, which expect English texts and get German ones on this machine; they pass withDOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1.ItemExchangeApiTests(own host): no key, the login cookie (with a check that it does sign in), a cash shop key, a viewer key, one export at a time.ItemExchangeExportTests: the header, every Season 6 item sorted, repeated items by id, only listed fields, UTF-8 with LF, the operation on a stored configuration, unsaved changes left out, the loaded configuration reused, the registration inAddAdminReadOperations, the game version (update state, Season 6 client, unknown refused), the left-out items and their warning.ApiKeyManagementServiceTests: no key without a user, a key with one, no key when the last user is gone while the dialog is open (it fails without the second check).AdminPanelSetupModeTests: the setup mode without a user, with a stored user and with a bootstrap user; the oldest user; disabling the enabled keys; the first user created in the admin panel disables the keys, a later one doesn't.