Skip to content

headless_api: gate request on new Repo.automation_permission (bug 1971103)#1044

Closed
shtrom wants to merge 1 commit intomainfrom
bug1971103/headless-repo-scope
Closed

headless_api: gate request on new Repo.automation_permission (bug 1971103)#1044
shtrom wants to merge 1 commit intomainfrom
bug1971103/headless-repo-scope

Conversation

@shtrom
Copy link
Copy Markdown
Member

@shtrom shtrom commented Mar 31, 2026

  • repo: add automation_permission and user_can_use_automation()
  • headless_api: gate API request to user having the correct repo's required_automation_permission
  • test: test headless_api gating on repo required_automation_permission
  • repo: fail closed on empty permissions strings
  • utils: add stub data migration command (bug 2013991)

@github-actions
Copy link
Copy Markdown

View this pull request in Lando to land it once approved.

@shtrom shtrom marked this pull request as ready for review March 31, 2026 07:56
@shtrom shtrom requested a review from a team as a code owner March 31, 2026 07:56
@shtrom shtrom force-pushed the bug1971103/headless-repo-scope branch 2 times, most recently from b5850ff to 09a737f Compare March 31, 2026 08:00
Comment on lines +431 to +437
if not repo.user_can_use_automation(request.user):
error = f"User {request.user.email} is not allowed to use this API. Missing permission: {repo.automation_permission}."
logger.info(
error,
extra={"user": request.user.email, "token": request.auth.token_prefix},
)
return 403, {"details": error}
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@cgsheeh With this in place, I wonder if

if not api_key.user.has_perm("headless_api.add_automationjob"):
raise APIPermissionDenied(
f"User {api_key.user.email} is not permitted to make automation changes."
)
is still necessary. What do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think it can be removed. That permission is currently used for "does this user have automation API access". With the more granular control, we can simply remove their automation-level permissions instead.

Copy link
Copy Markdown
Member Author

@shtrom shtrom Apr 7, 2026

Choose a reason for hiding this comment

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

Ok, I'll file a bug to remove later, so we can confirm that the new system works adequately first.

https://bugzilla.mozilla.org/show_bug.cgi?id=2029863

cgsheeh
cgsheeh previously requested changes Apr 2, 2026
Copy link
Copy Markdown
Member

@cgsheeh cgsheeh left a comment

Choose a reason for hiding this comment

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

One more thing that isn't clear to me from this is whether we intend to use scm levels as the automation_permission. I guess these will be the SCM_ALLOW_DIRECT_PUSH permission in most cases? I think we could make an argument that we could manage these permissions without SCM levels, for example our own custom create_automation_firefox. This would allow us to use Django groups to manage access to teams (sheriffs, thunderbird, etc) without having to use the manual checks we have for SCM levels.

Comment thread src/lando/main/models/repo.py Outdated
def _user_has_direct_permission(self, user: User, required_permission: Permission):
"""
Test that the user has permission to push to this repo.
Test that the user has a specific permission directly rather than via a role.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Checking that a user has a specific permission from LDAP/mirrored groups makes sense for scm levels. I would imagine granting permissions to all users in a specific group would be useful for us in the future (for example, creating a sheriff group and allowing all sheriffs access to the automation API).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is for Django roles, rather than LDAP groups. I'm not sure we have a mapping for groups to Django roles at the moment. It would certainly be useful.

FWIW, this _user_has_direct_permission is there avoid granting super-users SCM permissions they don't have outright. We could loosen the check and allow role-inheritane if needed, but I don't think that's the case at the moment.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thinking more about this, in a way, if we grant sheriff_firefox or scm_sheriff_firefox we'd create an implicit group of users who have this permission. Unless we want to associate more than just that permission to them (maybe scm3 comes with at the same time), I think we are already here.

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.

FWIW I think this will be resolved in a much cleaner way once we stop using superusers in Lando.

Comment thread src/lando/main/models/repo.py Outdated
# Use this field to enable/disable access to this repo via the automation API.
automation_enabled = models.BooleanField(default=False)
automation_permission = models.CharField(
default="main.scm_direct_push",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

See my other comment about changing this to a ForeignKey.

However if we keep this as a CharField, the default should just be None/blank, which would be a proxy for "there is no permission which can grant you access". We could potentially remove the automation_enabled flag as well, though there's probably an argument to be made that enabling the flag without wiping the permission would be ideal.

Copy link
Copy Markdown
Member Author

@shtrom shtrom Apr 7, 2026

Choose a reason for hiding this comment

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

None by default would be good, but this means that firefox repos will find themselves without direct push access when we deploy that change. Not a big deal, but something to remember to fix manually at the time of deploy.

We can also add a dedicated data-migration step that would set it correctly for fx repos on migrate, but we don't have a very clean way to do this at the moment https://bugzilla.mozilla.org/show_bug.cgi?id=2013991 @zzzeid maybe it's an opportunity to work on this pattern?

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.

It would be good to set up the data migration "framework" so to speak, even if it's as simple as a management command that calls other management commands (the latter being the actual migrations). They can probably all sit in their own app.

However, for this particular migration I would say it's probably fine to do the changes manually in the admin, while Lando is in maintenance.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

🌮 I added a stub migration script, that we can run manually during the upgrade.

Comment on lines +431 to +437
if not repo.user_can_use_automation(request.user):
error = f"User {request.user.email} is not allowed to use this API. Missing permission: {repo.automation_permission}."
logger.info(
error,
extra={"user": request.user.email, "token": request.auth.token_prefix},
)
return 403, {"details": error}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think it can be removed. That permission is currently used for "does this user have automation API access". With the more granular control, we can simply remove their automation-level permissions instead.

Comment thread src/lando/main/models/repo.py Outdated
Comment thread src/lando/main/models/repo.py Outdated
@shtrom
Copy link
Copy Markdown
Member Author

shtrom commented Apr 7, 2026

One more thing that isn't clear to me from this is whether we intend to use scm levels as the automation_permission. I guess these will be the SCM_ALLOW_DIRECT_PUSH permission in most cases? I think we could make an argument that we could manage these permissions without SCM levels, for example our own custom create_automation_firefox. This would allow us to use Django groups to manage access to teams (sheriffs, thunderbird, etc) without having to use the manual checks we have for SCM levels.

With the change, we're aiming to continue have that link to SCM levels in LDAP. My reasoning is that doing so lines up with current access practices and processes, which means we don't need to manage it in an ad hoc manner in Lando.

With the coming comm and nss migration, this will likely require the creation of dedicated LDAP groups similar to SCM_ALLOW_DIRECT_PUSH for those projects, but this is a one-off change.

Happy to discuss more, though (:

@shtrom shtrom force-pushed the bug1971103/headless-repo-scope branch from 72af2a4 to 87209ae Compare April 7, 2026 06:32
Comment thread src/lando/main/models/repo.py Outdated
# Use this field to enable/disable access to this repo via the automation API.
automation_enabled = models.BooleanField(default=False)
automation_permission = models.CharField(
default="main.scm_direct_push",
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.

It would be good to set up the data migration "framework" so to speak, even if it's as simple as a management command that calls other management commands (the latter being the actual migrations). They can probably all sit in their own app.

However, for this particular migration I would say it's probably fine to do the changes manually in the admin, while Lando is in maintenance.

Comment thread src/lando/main/models/repo.py Outdated
Comment thread src/lando/main/models/repo.py
@shtrom shtrom force-pushed the bug1971103/headless-repo-scope branch from e14ccbd to 4324fef Compare April 9, 2026 03:56
@shtrom shtrom requested a review from zzzeid April 9, 2026 03:58
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@zzzeid here's a very stubby data-migration script built for immediate purpose, and we can iterate on it when working on bug 2013991 for real.

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.

Sounds good. I have some comments but I can save them to a different discussion when we are working on bug 2013991 for real.

@shtrom shtrom force-pushed the bug1971103/headless-repo-scope branch 2 times, most recently from 86c23c7 to b168ea8 Compare April 9, 2026 06:40
@shtrom shtrom requested a review from cgsheeh April 9, 2026 06:48
@shtrom shtrom dismissed cgsheeh’s stale review April 9, 2026 06:48

addressed, I think

Comment thread src/lando/utils/management/commands/migrate_data.py Outdated
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.

Sounds good. I have some comments but I can save them to a different discussion when we are working on bug 2013991 for real.

Comment thread src/lando/utils/management/commands/migrate_data.py Outdated
Comment thread src/lando/utils/management/commands/migrate_data.py Outdated
Comment thread src/lando/conftest.py
Comment thread src/lando/main/models/repo.py
Comment thread src/lando/main/models/repo.py Outdated
def _user_has_direct_permission(self, user: User, required_permission: Permission):
"""
Test that the user has permission to push to this repo.
Test that the user has a specific permission directly rather than via a role.
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.

FWIW I think this will be resolved in a much cleaner way once we stop using superusers in Lando.

Comment thread src/lando/utils/management/commands/migrate_data.py Outdated
@shtrom shtrom force-pushed the bug1971103/headless-repo-scope branch from d679156 to d6e0938 Compare April 14, 2026 03:04
@shtrom
Copy link
Copy Markdown
Member Author

shtrom commented Apr 14, 2026

Rebase and force-push to work around landing problems https://lando.moz.tools/landings/35783/

@shtrom shtrom force-pushed the bug1971103/headless-repo-scope branch from d6e0938 to 05cc56e Compare April 14, 2026 05:07
@shtrom
Copy link
Copy Markdown
Member Author

shtrom commented Apr 14, 2026

Rebase and squash fixups... https://lando.moz.tools/landings/35790/

headless_api: gate API request to user having the correct repo's automation_permission

test: test automation_api gating on repo automation_permission

migrations: renumber after merge

repo: fail closed on empty permissions strings

tests: better coverage of Repo._user_has_direct_permissions

repo: rename automation_permission to required_automation_permission

utils: add stub data migration command (bug 2013991)

tests: fix permission issues

Update src/lando/main/models/repo.py

Co-authored-by: Zeid <2043828+zzzeid@users.noreply.github.com>

pr comments

test: no need to create the scm_allow_direct_push permission
@shtrom shtrom force-pushed the bug1971103/headless-repo-scope branch from 05cc56e to af050af Compare April 14, 2026 05:25
@shtrom
Copy link
Copy Markdown
Member Author

shtrom commented Apr 14, 2026

Squish squash 🙄 https://lando.moz.tools/landings/35793/

lando-worker bot pushed a commit that referenced this pull request Apr 14, 2026
…1103) r=sheehan,zeid

 * repo: add `automation_permission` and `user_can_use_automation()`
 * headless_api: gate API request to user having the correct repo's `required_automation_permission`
 * test: test headless_api gating on repo `required_automation_permission`
 * repo: fail closed on empty permissions strings
 * utils: add stub data migration command (bug 2013991)

Pull request: #1044
@lando-worker
Copy link
Copy Markdown

lando-worker bot commented Apr 14, 2026

Pull request closed by commit 9e90472

@lando-worker lando-worker bot closed this Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants