Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion linodecli/configuration/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,14 @@ def _check_full_access(base_url: str, token: str) -> bool:
verify=API_CA_PATH,
)

_handle_response_status(result, exit_on_error=True)
# IAM-enrolled users receive a 403 from /profile/grants since that
# endpoint is not accessible to them. Treat 403 as a valid response
# (i.e. not full access) rather than a fatal error.
_handle_response_status(
result,
exit_on_error=True,
status_validator=lambda status: status == 403,
)
Comment on lines +179 to +186
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Treating a 403 from /profile/grants as a non-fatal response makes _check_full_access return False for IAM-enrolled users. In CLIConfig.configure() this value gates the /account/users lookup, so IAM users will never be offered authorized_users selection even if they otherwise have the needed account permissions. If the intent is to still support configuring authorized users for IAM tokens, consider falling back to a different capability check (e.g. attempt /account/users with a status_validator for 401/403) or decouple the authorized-users prompt from _check_full_access when the grants endpoint is inaccessible.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jriddle-linode Thoughts on this? I can't seem to reproduce the issue that requires this change but I might not be properly enrolled in IAM


return result.status_code == 204

Expand Down
39 changes: 39 additions & 0 deletions tests/unit/test_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from linodecli import configuration
from linodecli.configuration import (
_bool_input,
_check_full_access,
_default_text_input,
_default_thing_input,
)
Expand Down Expand Up @@ -676,3 +677,41 @@ def test_custom_config_path(self, monkeypatch, tmp_path):

for i, _ in enumerate(expected_configs):
assert expected_configs[i] == configs[i]


class TestCheckFullAccess:
"""
Unit tests for _check_full_access
"""

base_url = "https://linode-test.com"
test_token = "cli-dev-token"

def test_full_access_returns_true(self):
"""
204 No Content means the token has full (unrestricted) access.
"""
with requests_mock.Mocker() as m:
m.get(f"{self.base_url}/profile/grants", status_code=204)
assert _check_full_access(self.base_url, self.test_token) is True

def test_restricted_access_returns_false(self):
"""
200 with a grants body means the token has restricted access.
"""
with requests_mock.Mocker() as m:
m.get(
f"{self.base_url}/profile/grants",
status_code=200,
json={"linode": []},
)
assert _check_full_access(self.base_url, self.test_token) is False

def test_iam_user_403_returns_false(self):
"""
IAM-enrolled users receive a 403 from /profile/grants.
This should be treated as "not full access" rather than a fatal error.
"""
with requests_mock.Mocker() as m:
m.get(f"{self.base_url}/profile/grants", status_code=403)
assert _check_full_access(self.base_url, self.test_token) is False
Loading