-
Notifications
You must be signed in to change notification settings - Fork 63
[2/n] Silo-level max device token TTL #8214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| // and differently for quotas. Maybe the best thing would be to make them all | ||
| // non-nullable on SiloQuotasUpdate. I vaguely remember the latter being the | ||
| // direction we wanted to go in general anyway. Can't find the issue where it | ||
| // was discussed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
something to think about
| // TODO: modify seems fine, but look into why policy has its own | ||
| // separate permission | ||
| let (.., authz_silo) = | ||
| silo_lookup.lookup_for(authz::Action::Modify).await?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was imitating the policy endpoint, but I don't see why we would need to do an auth check here if the datastore function does the same check.
| # List of authentication schemes to support. | ||
| [authn] | ||
| schemes_external = ["spoof", "session_cookie"] | ||
| schemes_external = ["spoof", "session_cookie", "access_token"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is necessary to make tokens actually work in the integration tests, so we can test that they work and that they expire.
ahl
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, a silo administrator can modify the token expiration for their own silo, but a central admin could not modify the policy for another silo. And there's no way to set a policy that would apply (e.g. by default) to all silos (existing and new). This seems like a decision worth validating with prospective users. For example, if I were operating the control plane, responsible for the product at large, I would want to be able to set a policy that applied to all silos.
The name "settings" is very general. I would suggest far too general. I'd suggest more specific, discoverable naming.
What configuration happens at the silo-level today? What do we imagine happening in the future? With the proposed API, it seems very easy to accidentally remove token expiration: oxide settings --some-other-setting 7 -- since device_token_max_ttl_seconds is optional, I think that accidentally remove that setting.
As an administrator, I would expect to be able to create a silo with an expiration policy (i.e. atomically). That doesn't seem to be possible.
|
The solution we've discussed for fleet admins setting silo-level TTLs (not yet incorporated into the RFD) is setting a fleet-level setting and then allowing or disallowing each silo's admins to override it. That is unlikely to make it into v15 (though it's possible) but it seems reasonable for v16. Will work on the other stuff. |
|
Here's what I'm thinking.
|
6fbe688 to
a4d0d74
Compare
f68b227 to
3c36e26
Compare
I think we need something that indicates the entity to which the settings apply i.e. the silo.
I should have asked this as we were figuring the
Have we validated how customers want to use this? |
a4d0d74 to
6f16cfe
Compare
3c36e26 to
26d25ab
Compare
|
auth-settings is a helpful clarification
Agreed. I'm not sure what progenitor does with this. Certainly it's not going to do the right thing in the SDK... sadly. I think I should make the
Understood. |
d2fc15e to
14c544c
Compare
14c544c to
4dd9321
Compare
| pub device_token_max_ttl_seconds: Option<i64>, | ||
| /// Maximum lifetime of a device token in seconds. If set to null, users | ||
| /// will be able to create tokens that do not expire. | ||
| pub device_token_max_ttl_seconds: Option<u32>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Went with u32 here because it feels maybe overkill to have to validate that this is NonZero when it comes out of the DB.
|
Got everything in df0fae8 |
Built on top of #8137 and #8214. This is only for a user to list and delete their own tokens. It doesn't quite match RFD 570, which says `/v1/device-tokens` instead of `/v1/me/access-tokens`, but it feels good under `/v1/me`, and after trying to make the UI too, I think "access tokens" is much more intuitive. If I stick with this, I will update RFD 570 to match. ~~I'm not sure about the path `/v1/device-tokens` — in the API we call them `Device Access Tokens`. I think `/v1/access-tokens` might be more intuitive because the `device` is sort of an implementation detail, it refers to the OAuth device auth flow, which we are using. In practice, the user just gets a token with the CLI and pastes a code into the web UI and they don't have to think too much about it, so exposing that detail in the name might not be worth it.~~ Went with `/v1/me/access-tokens`. - [x] Basic token list and delete - [x] Basic integration tests - [x] Finalize endpoint paths - [x] Figure out authz story - Went with restricting datastore functions to current actor for now
Closes #8147. Built on #8137, #8214, and #8227. This is pretty straightforward, I think. The user gives us a TTL in seconds at token request time. We store it on the request row. When they come back in the later step to confirm the code and generate the token, we retrieve the TTL, validate that it is less than the silo max (if one is set), and we use it to generate the `time_expires` timestamp, which cannot be changed later. One slightly surprising bit is that we can't validate the TTL against the silo max at initial request time because we don't have any idea what silo the user is associated with until the confirm step. So probably want to make sure we are handling TTL validation errors nicely in the web console, because I think that's where they will show up.
…e required to be present (#9046) Optional fields on a request body can be left out completely from the JSON. In the case of instance update (and probably many other endpoints) this is a problem because passing `null` and passing nothing have the same effect, namely unsetting that field if it is already set in the DB. And because the fields are optional, the generated client types do not enforce that they are present (at least when creating the JSON by hand and also in TypeScript, which distinguishes between optional and nullable at the type level). It is easy to do this by accident: while working on [this](oxidecomputer/console#2900), I [discovered a bug](oxidecomputer/console#2900 (comment)) in the console where we accidentally unset the auto restart policy on an instance when you resize an instance. This PR changes `Option` to `Nullable`, which I added in #8214 to fix this problem: `null` remains a valid value, but the client is no longer allowed to leave that field out of the body. If we don't want to do this, I can live with it because in the console, I have [made a helper](oxidecomputer/console@60ca75e) that enforces that all fields are explicitly present. But I do think the status quo is very error-prone. https://github.com/oxidecomputer/omicron/blob/c6989117c72ecf88d4e3dc8a597d9fe703152a93/common/src/api/external/mod.rs#L3564-L3570
…e required to be present (#9046) Optional fields on a request body can be left out completely from the JSON. In the case of instance update (and probably many other endpoints) this is a problem because passing `null` and passing nothing have the same effect, namely unsetting that field if it is already set in the DB. And because the fields are optional, the generated client types do not enforce that they are present (at least when creating the JSON by hand and also in TypeScript, which distinguishes between optional and nullable at the type level). It is easy to do this by accident: while working on [this](oxidecomputer/console#2900), I [discovered a bug](oxidecomputer/console#2900 (comment)) in the console where we accidentally unset the auto restart policy on an instance when you resize an instance. This PR changes `Option` to `Nullable`, which I added in #8214 to fix this problem: `null` remains a valid value, but the client is no longer allowed to leave that field out of the body. If we don't want to do this, I can live with it because in the console, I have [made a helper](oxidecomputer/console@60ca75e) that enforces that all fields are explicitly present. But I do think the status quo is very error-prone. https://github.com/oxidecomputer/omicron/blob/c6989117c72ecf88d4e3dc8a597d9fe703152a93/common/src/api/external/mod.rs#L3564-L3570

Closes #2302. Built on top of #8137, but doesn't really rely on much in there. Implementing RFD 570.
silo_settingstable with nullable device token expiration (seconds) column. Null setting means no max/v1/settingswith all necessary plumbing