-
Notifications
You must be signed in to change notification settings - Fork 149
Add account delegation preparation #3076
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
… main, update existing code
…s as well as tests
…te-account merge get_accounts return result
…te-account Update error handling
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.
Looking good!
// this should only happen if a named account was created, and then both it and the | ||
// default account references were moved/deleted. | ||
if acc_ref_vec.is_empty() { | ||
return Some(Account::new( |
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 they were moved or deleted, shouldn't we return None
?
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.
Normally yes, but we do have to guarantee that a user can always log in. If we don't return a synthetic default account here, hypothetically a user could have hit the maximum after deleting a renamed default account. Maybe we should talk about this more though, basically transferring a default account and also having a synthetic default account here means the original identity retains access to the transferred default account.
In any case, this should not happen for now because we cannot currently move or delete accounts.
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.
That makes sense.
Let's add it in the comment as well. It's not clear at first sight why we should return the synthetic account.
I agree we should think this more. We should have a WARNING in the comment, to review this at some point and the edge cases we have with the current code.
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.
Adding this.
src/internet_identity/src/storage.rs
Outdated
@@ -735,21 +735,21 @@ impl<M: Memory + Clone> Storage<M> { | |||
.map(|list| list.into()) | |||
} | |||
|
|||
pub fn has_account_reference( | |||
fn has_account_reference( |
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.
Should we rename this method to find_account_reference
? It returns an AccountReference while the name has_...
indicates a boolean.
I probably wrote this method, but I think it's confusing.
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.
Sure!
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.
Done ✅
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.
Can we add two more tests for read_account
for the two new use cases?
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.
done ✅
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.
Looking good thanks!
Already approving, but please extend the comments. There are some edge cases that we should solve sooner rather than later.
|
||
if stored_accounts >= MAX_ANCHOR_ACCOUNTS as u64 { | ||
return Err(UpdateAccountError::AccountLimitReached); | ||
storage_borrow_mut(|storage| { |
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 realized now, but waas this necessary for the prepare delegation?
No need to do it in another PR, just trying to understand the changes.
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.
You and @sea-snake had asked to put anchor_has_account
functionality into read_account
, so I removed it here. Not sure why storage_borrow_mut
got moved, moving it back now (no need to borrow storage if we're not using it).
@@ -150,6 +119,21 @@ fn calculate_seed(anchor_number: AnchorNumber, frontend: &FrontendHostname) -> H | |||
hash_bytes(blob) | |||
} | |||
|
|||
pub fn calculate_account_seed(account_number: AccountNumber) -> Hash { |
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.
Can we add a comment on why we don't need the frontend nor the anchor number?
It's not obvious unless you know everything about the accounts.
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.
done ✅
timestamp, | ||
}| (user_key, timestamp), | ||
) | ||
.unwrap_or_else(|err| trap(&format!("{:?}", err))) |
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.
Are we planning on use the AccountDelegationError
returned by prepare_account_delegation
here?
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.
In the spirit of backwards compatibility, I have not updated legacy endpoints. We can do that, but would also need to update the frontend.
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 guess that's fine for now. I was wondering whether you discussed it already.
} | ||
None => { | ||
// If this is an added account, we derive from the account number. | ||
delegation::calculate_account_seed(self.account_number.unwrap()) |
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.
To be safer, you could match both:
match (self.get_seed_anchor(), self.account_number)
This way we don't need to rely in unwrapping and we can trap with a nicer error message. Wdyt?
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 this is about the error message, we can just .expect()
with a nicer message. Matching both means we need four branches, two of which are basically identical, this would feel a bit superfluous.
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.
You don't need four branches. Three should be enough and it's maybe clearer the error.
(Some(seed_from_anchor), _) => ...
(None, Some(account_number) => delegation::calculate_account_seed(account_number)
(None, None) => trap("Missing both seed anchor and account number in this account")
Just a nitpick though. No need to implement it. I like pattern matching personally 😄
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.
done ✅
// this should only happen if a named account was created, and then both it and the | ||
// default account references were moved/deleted. | ||
if acc_ref_vec.is_empty() { | ||
return Some(Account::new( |
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.
That makes sense.
Let's add it in the comment as well. It's not clear at first sight why we should return the synthetic account.
I agree we should think this more. We should have a WARNING in the comment, to review this at some point and the edge cases we have with the current code.
@@ -498,3 +496,68 @@ fn should_count_accounts_different_anchors() { | |||
"Total counters after anchor 2 additional account mismatch" | |||
); | |||
} | |||
|
|||
#[test] | |||
fn should_read_default_account_with_empty_reference_list() { |
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.
Replicate part of the comment on why we return a default account in this case here, please.
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.
done ✅
src/internet_identity/src/storage.rs
Outdated
/// Returns the requested account. | ||
/// If the anchor doesn't own this account, returns None. | ||
/// If the account is default but has been moved/deleted, returns None. |
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.
Is this comment accurate? Aren't we returning a default account if the list is empty?
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.
It is correct but incomplete - if the default has been moved or deleted but there are other accounts, we return None. If there are no accounts left (all accounts moved/deleted), we return a synthetic default account. Updating comments.
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 do agree that this is not a great solution - we currently cannot actually get to this state, but we do need to think about this at some point)
} | ||
|
||
// if there is a default account in the list, we return it | ||
// else we return None, account has been moved or deleted |
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.
Shuold we mention here that the user can still log in with the other account?
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.
Adding...
* add get_accounts_for_origin to account_management and get_accounts to main, update existing code * add authorization fn * clippy * add tests for get and create * clippy * add account updating endpoint and related account management functions as well as tests * cwippy * add seed_from_anchor to Account * add seed_from_anchor to account struct * add calculate_account_delegation_seed function * make get_accounts return a result * add principal to GetAccountsError::Unauthorized * add const * add global acc limit and unit test * add max acc limit to update function * add authorization, add checking if anchor has account (for later) * clippy * fix did * add per-acc auth check in account management * add tests, restructure per-acc auth check in update_account * change variant name, update comment * cleanup function inputs * cwippy * cwippy again * minor changes * add account delegation preparation function and update endpoint * fix did * fix did * cwippy * fix did again * cwippy fix * fix did? * fix typing * clippyyy * cleanup * clippy * fix read_account method to account for seed_from_anchor * rename * move seed generation function into account impl * clippy * cleanup delegation generation, handle default account in has_account_reference, move anchor_has_account * return account from anchor_has_account * cleanupt * restructure has_account_reference and anchor_has_account * cwippy * return default acc if no application number * cwippy * cleanup * restructure storage borrowing * make nicer * update list_accounts to return accounts and use read_account * remove anchor_has_account, pack everything into read_account * clippy * clippy * add comment * clippy * change error type * fix function inputs * fix tests * 🤖 cargo-fmt auto-update * add tests, rename some things * remove comment * update comments * add expect message, add comments * clippy * update match statement in calculate_seed --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
* add get_accounts_for_origin to account_management and get_accounts to main, update existing code * add authorization fn * clippy * add tests for get and create * clippy * add account updating endpoint and related account management functions as well as tests * cwippy * add seed_from_anchor to Account * add seed_from_anchor to account struct * add calculate_account_delegation_seed function * make get_accounts return a result * add principal to GetAccountsError::Unauthorized * add const * add global acc limit and unit test * add max acc limit to update function * add authorization, add checking if anchor has account (for later) * clippy * fix did * add per-acc auth check in account management * add tests, restructure per-acc auth check in update_account * change variant name, update comment * cleanup function inputs * cwippy * cwippy again * minor changes * add account delegation preparation function and update endpoint * fix did * fix did * cwippy * fix did again * cwippy fix * fix did? * fix typing * clippyyy * cleanup * clippy * fix read_account method to account for seed_from_anchor * start updating api * rename * move seed generation function into account impl * start adding from * clippy * add get_account_delegation method in account_management * cwippy * cleanup delegation generation, handle default account in has_account_reference, move anchor_has_account * return account from anchor_has_account * merge updates * cleanupt * restructure has_account_reference and anchor_has_account * cwippy * return default acc if no application number * cwippy * cleanup * restructure storage borrowing * replaced existing get_delegation with new and more general get_account_delegation * cwippy * cwippy again * update did * update did * update did * make nicer * update list_accounts to return accounts and use read_account * remove anchor_has_account, pack everything into read_account * clippy * clippy * add comment * clippy * change error type * fix function inputs * fix tests * 🤖 cargo-fmt auto-update * clippy * clippy * add tests, rename some things * minor adjustments * Add account delegation preparation (#3076) * add get_accounts_for_origin to account_management and get_accounts to main, update existing code * add authorization fn * clippy * add tests for get and create * clippy * add account updating endpoint and related account management functions as well as tests * cwippy * add seed_from_anchor to Account * add seed_from_anchor to account struct * add calculate_account_delegation_seed function * make get_accounts return a result * add principal to GetAccountsError::Unauthorized * add const * add global acc limit and unit test * add max acc limit to update function * add authorization, add checking if anchor has account (for later) * clippy * fix did * add per-acc auth check in account management * add tests, restructure per-acc auth check in update_account * change variant name, update comment * cleanup function inputs * cwippy * cwippy again * minor changes * add account delegation preparation function and update endpoint * fix did * fix did * cwippy * fix did again * cwippy fix * fix did? * fix typing * clippyyy * cleanup * clippy * fix read_account method to account for seed_from_anchor * rename * move seed generation function into account impl * clippy * cleanup delegation generation, handle default account in has_account_reference, move anchor_has_account * return account from anchor_has_account * cleanupt * restructure has_account_reference and anchor_has_account * cwippy * return default acc if no application number * cwippy * cleanup * restructure storage borrowing * make nicer * update list_accounts to return accounts and use read_account * remove anchor_has_account, pack everything into read_account * clippy * clippy * add comment * clippy * change error type * fix function inputs * fix tests * 🤖 cargo-fmt auto-update * add tests, rename some things * remove comment * update comments * add expect message, add comments * clippy * update match statement in calculate_seed --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> * Improve copy in buttons (#3082) * Improve copy in buttons * Revert changes in use another account * small fixes * merge main and small fixes * run generate * cleanup --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Llorenç Muntaner <[email protected]>
Motivation
In order to be able to authenticate with accounts, we need to first prepare the delegation.
Changes
Add necessary functions -
calculate_account_delegation seed
(which resolves to a regular anchor number based seed for default accounts and former default accounts),prepare_account_delegation
which closely mirrorsprepare_delegation
but uses the different seed and checks whether the anchor has access to the account, as well as theprepare_account_delegation
endpoint in main.rs which checks auth, records activity and returns the delegation, wrapped in a Result for frontend convenience. Changing legacyget_delegation
method to also use new functions. Also, related structs/methods etc.Tests
No additional tests at this time, created Jira tickets for the integration tests. However, since legacy endpoint uses new functions, there is also some amount of test coverage.