feat: 노출 식별자를 UUID로 전환 - #47
Merged
Merged
Conversation
Sequential ids in URLs disclose the platform's size and its growth rate: before V78 the number in a VM or domain URL *was* the row count. Each exposed table gains a public_id column; the bigint primary keys, the foreign keys and the id-ordered sorts all stay as they were. Resolution happens at the gates that already own the masking 404 — VmAccessService, AdminVmAccess, the grant service and the few single findById sites where a path id first lands — so nothing below a gate changes and no refusal wording had to be re-picked. A well-formed bigint in a path is no longer a well-formed id, so /vms/999999 now answers 422 where it answered 404. The JWT subject becomes the account's public id and the write-only org_id claim is dropped. audit_logs.target_id records the target's public id; a refresh token has no public identity, so those rows carry the internal id in the detail map instead of in the target column. The organisation slug goes with the numbers (V78 drops the column): it existed to name an org in a URL, which the public id now does. Its unique index was also the only duplicate protection POST /admin/orgs had, so org names may now repeat.
The link path is assembled by string concatenation out of an untyped argument map, so a job still passing the row number compiled unchanged and produced a console link that resolves to nothing. Seven of them did: the expiry notice and auto-stop, the delete-completed notice, the provision result, and three publishing paths.
The admin list scopes take the org filter as a public id now, and an id no org has has to read as a filter matching nothing. Writing that as a conditional mixing long and Long unboxed the null branch, so the user, workspace and audit listings answered 500 instead of an empty page.
Fixtures keep setting rows up through direct SQL, so they still hold internal ids; what changed is that a path, a query filter, a request body and an assertion all name the row by its public id instead. Two shared helpers carry that: one resolves a row's public id for a fixture holding the internal one, the other goes back the other way for a fixture that created its row through the API. The numeric "does not exist" probes have no UUID equivalent, so they share one fixed identifier no row can have. audit_logs.target_id is text holding a public id now, which is what every audit assertion compares against, and the notification payload assertions follow the same move. PublicIdentifierTest is new: it pins the boundary itself — an id the API hands back is a UUID and never the row number, the row number no longer addresses anything, an unknown id and another workspace's real id are refused identically, and the access token names its account by public id and carries no organisation claim.
221 path parameters, query filters and response fields change from int64 to a uuid-formatted string. Nine fields leave their required lists: every one is a history-preserving join whose id used to come from the referring row and now comes from the referenced one, so a deleted workspace, a withdrawn account or a since-removed catalog row leaves nothing to report. The organisation slug is gone from the schemas entirely, as is the org_id claim from the security scheme.
An ORG_ADMIN's org announcement takes its organisation from the actor, not from the request body, so echoing the body's field back reported a null organisation on exactly the send that has one. The response now reads both scope targets off the saved row. The tests that came with it: the org fixtures key on the name the slug used to carry, the audit and payload comparisons take the public id as text, and a fixture that deletes its row reads that row's identifier before deleting it.
The org fixtures that still keyed on the dropped slug, the notification payload and audit comparisons that still passed a row number, and the port-forwarding delete path. The web terminal's force-terminate audit keys on the session instead: the mirror's VM id belongs to no row in that test, so the target column is empty by design there.
The nested record kept the internal bigint while its enclosing response had already moved, so /me and the admin user detail handed out a workspace's sequential id. The admin page builds a /admin/vms?workspaceId= link from it, which the server no longer accepts -- a link that compiles and filters on nothing.
Both operands became Long objects when the lookup moved to the public id, so == stopped comparing values and started comparing references. It succeeds only inside the Long cache, which is why every test passed: a fresh test database issues ids from 1. The dev database has eight keys above 127, each about to become undeletable with its private key undownloadable. The new test pushes the sequence past the cache first, and asserts the refusal the download gives for its own reason rather than the one a failed owner check gives.
The create path already passed public ids; the status-change path 128 lines below still passed internal ones, so the stored link pointed at a route that no longer resolves. Nothing asserted link_path anywhere in the suite, which is how the two drifted apart.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📝 작업내용
⭐️ 검증
💬 리뷰 포인트