Skip to content

fix(console): serve an IAM user its own user page and access keys only - #844

Open
LeeroyHannigan wants to merge 4 commits into
mainfrom
fix/console-iam-user-own-pages
Open

LeeroyHannigan wants to merge 4 commits into
mainfrom
fix/console-iam-user-own-pages

Conversation

@LeeroyHannigan

Copy link
Copy Markdown
Collaborator

What

An IAM-user console session now reads only its own user page and its own access keys in its account. Every other page and form under /console/accounts redirects the session to /console. Admin sessions see every page as before.

  • The account page, the group pages, the role pages and the account list at /console/accounts are for admin sessions only.
  • A user page opens only when the account ID and the user name in the path are the session's own. The new require_user_session in account_scope.rs makes this check. The management API uses the same rule for the access keys of an IAM user. Another user's name and a name that no user has get the same answer.
  • The user's own page lists its access keys, with the forms that create and delete them. It leaves out the user's inline policies, groups and tags. The management API does not give the user these reads either: GET .../users/{self}/policies and GET .../users/{self}/tags answer 403, and no route lists the groups of a user.
  • The admin-only forms and POST handlers redirect an IAM user to /console. These are the forms that change users, groups, roles, members and policies, the delete account form and the new account form. Before, some of them sent the user to the account page, the group page or /console/accounts.
  • Each handler makes its check before it reads the account, user, group or role in the path.
  • The navigation bar shows the Accounts link to admin sessions only. The dashboard card of an IAM user links to its own user page, and its breadcrumbs are Dashboard and its own user. The pages, navigation bar and breadcrumbs of an admin session are the same as before, byte for byte.
  • Docs: getting-started section 8, the architecture guide, the admin guide, the security model, and a v0.1.15 entry in the upgrade manual. The earlier v0.1.15 entry for console account scoping no longer says that the home page links an IAM user to its own account.

Why

Closes #731.

An IAM-user console session read every page of its own account. It saw every user, group and role name. It also saw the access key IDs, inline policy names, groups and tags of every user. Group pages showed the members and policy names, and role pages showed the trust policy and policy names. The management API refuses the same reads to the same user with HTTP 403. docs/getting-started.md section 8 said that IAM users view their own details and manage their own access keys.

Testing done

  • Rust: 34 console unit tests pass. They cover the own-user check, the user page, the breadcrumbs and the dashboard card. The new account_scope_reads_tests.rs uses a store that counts calls. For an IAM-user session, the account page, a group page, a role page and another user's page answer 303 to /console with 0 store calls. The user's own page and each admin page make 1 call.
  • Python: tests/test_console_iam_user_pages.py (53 tests) logs in as an IAM user that holds only the self-service policy that every user with a password gets. It checks these things:
    • 15 refused pages and 21 refused forms answer 303 to /console and change nothing.
    • No page that the user opens names another user, group or role, or links to a page that redirects it. The user's own group and tag do not show on its page.
    • The management API refuses the same reads.
    • The user creates and deletes its own keys and logs out. The two key forms answer 403 to an empty or wrong CSRF token, and the keys do not change.
    • An admin still reads every page.
  • tests/test_console_*.py and tests/test_cross_account_isolation.py: 168 passed on a SQLite build.
  • cargo fmt --check and cargo clippy -p extenddb-server --all-targets -- -D warnings pass.
  • Negative controls: I disabled each new check, one at a time, and a test failed each time. The 18 controls cover the account, group, role, user and account list checks, and the read order of four pages. They also cover both CSRF checks, the redirect targets, the dashboard card, the breadcrumbs and the Accounts link.

Merging with other PRs

Checklist

  • I have read CONTRIBUTING.md
  • All tests pass (cargo test --workspace)
  • Code is formatted (cargo fmt --check)
  • Clippy is clean (cargo clippy -- -W clippy::pedantic)
  • I have added or updated tests for new functionality
  • I have updated documentation if behavior changed
  • Breaking changes are noted below (if any)
  • If this changes the wire protocol, Storage trait, auth model, on-disk
    format, or public CLI surface, an RFC has been accepted or is linked
    below. Otherwise, an ADR captures the decision (link below).

ADR / RFC: n/a. The change limits what an IAM-user console session reads to what the management API already gives that user. The API, the storage and the policy evaluation do not change.

Breaking changes

In the console, an IAM user can no longer open its account page or the pages of other users. It can no longer open the group and role pages or the account list either. Its own user page no longer shows its inline policies, groups and tags. It still creates and deletes its own access keys there.


By submitting this pull request, I confirm that my contribution is made under
the terms of the Apache License 2.0 and I agree to the Developer Certificate of
Origin (DCO). See CONTRIBUTING.md for details.

An IAM-user console session could open every page of its own account. The
account page named every user, group and role. A user page showed that
user's access key IDs, inline policy names, groups and tags. A group page
showed its members and inline policy names, and a role page showed its trust
policy, inline policy names and tags. The handlers checked only that the
account in the path was the session's own. The management API answers the
same reads to the same user with HTTP 403.

An IAM-user session now opens only its own user page and the forms that
create and delete its own access keys. These are the access key reads and
writes the management API gives the same user. The console has no form to
change the password or import a key. require_user_session lets an IAM-user
session through when the account ID and the user name in the path are its
own. The management API applies the same rule to an IAM user's access keys.
Any other user's pages redirect it to /console before the handler reads
anything about that user. The account page, the group and role pages and
the account list are for admins. Their handlers redirect an IAM-user session
to /console before they read what the page shows. The admin-only forms under
the account also redirect it to /console. Before, some of them redirected to
the account page, the group page or the account list.

The user's own page lists its access keys and leaves out its inline
policies, groups and tags. The management API does not give the user those
either. The dashboard links an IAM-user session to its own user page, and
the navigation bar shows the Accounts link to admins only. The breadcrumbs
of the user page and the new access key page leave out the pages the
session may not open. Admin sessions get the same pages as before.

The tests that read an IAM user's own account page, its dashboard link and
its account list now expect the redirects.
…d keys

tests/test_console_iam_user_pages.py logs in as an IAM user that holds only
the self-service policy every user created with a console password gets. The
account also holds two other users, a group and a role, each with an inline
policy, and one user with an access key and a tag.

The tests check that the account list, the account forms and every page and
form under the user's account other than its own user page and access key
forms redirect it to /console, and that no refused form changes the catalog.
They check that no page it opens names another user, group or role of the
account or links to a page that redirects it, and that the management API
refuses the user the same reads. The user still reads, creates and
deletes its own access keys and logs out, and an admin session still reads
every page.
The getting started guide, the admin guide, the architecture guide and the
security model now say that an IAM-user session opens its own user page and
manages its own access keys, and that every other page under its account
redirects it to the dashboard. The upgrade manual has a v0.1.15 entry for
the change.

The earlier v0.1.15 entry for console account scoping no longer says
that the home page links an IAM user to its own account, or that the
pages of the user's own account are unchanged.
account_scope_reads_tests.rs runs the account, group, role and user page
handlers against a catalog store that holds no data and counts every call
to it. An IAM-user session that requests the account page, a group page, a
role page or another user's page gets a redirect to /console (HTTP 303).
The handler makes no call to the store. An admin request and the IAM
user's own user page each make one call.

The integration test posts the create and delete access key forms as the
IAM user. It posts each form twice, once with an empty CSRF token and once
with a wrong token of the same length. Each answer is HTTP 403, and the
user's access keys, as the management API lists them, do not change.

The IAM user in the integration test now belongs to a second group and has
a tag. The test checks that its own page leaves out that group and tag,
and that the admin's view of the same page shows them.
user_scope(&iam_user(), account, user).expect_err("the check let the session in");
let (status, location, body) = answer(refused).await;

assert_eq!(status, StatusCode::SEE_OTHER, "{account}/{user}");
let (status, location, body) = answer(refused).await;

assert_eq!(status, StatusCode::SEE_OTHER, "{account}/{user}");
assert_eq!(location.as_deref(), Some("/console"), "{account}/{user}");
] {
assert!(
user_scope(&admin(), account, user).is_ok(),
"admin refused {account}/{user}"
.into_iter()
.filter(|link| link.starts_with("/console/accounts"))
.collect();
assert!(to_accounts.is_empty(), "{to_accounts:?}: {body}");

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v1 Needed for the 1.0 release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Console: an IAM user reads every user, group and role page of its own account, which the management API refuses it

2 participants