Skip to content

[services] Switch to role based auth#15200

Open
cjllanwarne wants to merge 42 commits intohail-is:mainfrom
cjllanwarne:cjl_role_based_auth
Open

[services] Switch to role based auth#15200
cjllanwarne wants to merge 42 commits intohail-is:mainfrom
cjllanwarne:cjl_role_based_auth

Conversation

@cjllanwarne
Copy link
Collaborator

Change Description

Updates UI and backend to update to use the system_roles table for system-level UI and functionality auth.

NB this leaves the previous is_developer field in the database, in case we need to revert back, but hopefully we can remove that too in an upcoming PR.

Security Assessment

  • This change potentially impacts the Hail Batch instance as deployed by Broad Institute in GCP

Impact Rating

  • This change has a high security impact

Impact Description

Refactors the simple is_developer auth model into one based on roles and permissions. Allows intermediate roles between "everything" and "end user", eg for accessing billing pages or developer namespaces without being a full system admin.

Appsec Review

  • Required: The impact has been assessed and approved by appsec

@cjllanwarne cjllanwarne requested a review from a team as a code owner November 20, 2025 23:02

This comment was marked as outdated.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the authentication and authorization system from a simple is_developer boolean flag to a comprehensive role-based access control (RBAC) system using system roles and permissions. This enables more granular access control with intermediate roles between full system admin and end user.

Key Changes:

  • Introduces new database tables for system_roles, system_permissions, system_role_permissions, and users_system_roles
  • Replaces is_developer checks throughout the codebase with permission-based checks
  • Updates API endpoints to use new permission decorators (e.g., @auth.authenticated_users_with_permission())
  • Migrates UI templates to conditionally render elements based on user permissions

Reviewed changes

Copilot reviewed 36 out of 36 changed files in this pull request and generated 17 comments.

Show a summary per file
File Description
auth/sql/011-refactor-system-permissions.sql Adds new CI and deployed system state permissions, creates sysadmin-readonly role, and migrates existing users to new role system
auth/sql/estimated-current.sql Updates schema to include new system_roles, system_permissions, and relationship tables
auth/auth/auth.py Refactors user creation and management to use system roles instead of is_developer flag; adds permission checking endpoints
auth/auth/driver/driver.py Updates user creation/deletion logic to check for access_developer_environments permission
auth/auth/templates/users.html Updates user management UI to support multiple system roles via checkboxes
auth/auth/templates/roles.html Adds conditional rendering for role assignment buttons based on assign_system_roles permission
auth/auth/templates/openapi.yaml Updates API spec to reflect system_roles array replacing is_developer boolean
auth/auth/exceptions.py Adds InvalidRole exception for unknown role validation
gear/gear/auth.py Replaces authenticated_developers_only decorator with authenticated_users_with_permission; updates UserData type
gear/gear/init.py Exports SystemPermission (duplicate entry)
gear/gear/system_permissions.py Adds new permission types for CI, deployed system state, and developer activities
web_common/web_common/web_common.py Updates Jinja context retrieval to check READ_PRERENDERED_JINJA2_CONTEXT permission
web_common/web_common/templates/new_header.html Updates navigation menu to show/hide sections based on user permissions
web_common/web_common/templates/header.html Updates legacy header to use permission-based visibility
batch/batch/front_end/front_end.py Replaces is_developer checks with permission-based authorization for billing operations
batch/batch/front_end/templates/billing*.html Conditionally renders billing management UI based on user permissions
batch/batch/driver/main.py Updates driver endpoints to require specific system permissions
batch/batch/driver/templates/*.html Adds permission guards for system state modification UI elements
ci/ci/ci.py Replaces authenticated_developers_only with permission-based decorators for CI operations
ci/ci/templates/*.html Conditionally renders CI management controls based on manage_ci permission
ci/bootstrap_create_accounts.py Updates bootstrap user creation to use system_roles instead of is_developer
monitoring/monitoring/monitoring.py Requires VIEW_MONITORING_DASHBOARDS permission for billing endpoints
gateway/envoy.yaml Updates authorization path to verify access_developer_environments permission
grafana/deployment.yaml Updates authorization path to verify view_monitoring_dashboards permission
prometheus/prometheus.yaml Updates authorization path to verify view_monitoring_dashboards permission
hail/python/hailtop/auth/auth.py Updates async_create_user to accept system_roles list instead of is_developer boolean
hail/python/hailtop/hailctl/auth/*.py Updates CLI commands to support system_roles parameter
hail/python/hailtop/hail_logging.py Logs system_permissions instead of is_developer
devbin/dev_proxy.py Updates dev proxy to set system_roles instead of is_developer
build.yaml Adds migration step for refactor-system-permissions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

"""
INSERT INTO users (state, username, login_id, is_developer, is_service_account, hail_identity, hail_credentials_secret_name)
INSERT INTO users (state, username, login_id, is_service_account, hail_identity, hail_credentials_secret_name)
VALUES (%s, %s, %s, %s, %s, %s, %s);

This comment was marked as resolved.

ci/ci/ci.py Outdated
)
app[AppKeys.DEVELOPERS] = [u for u in users if u['is_developer'] == 1 and u['state'] == 'active']
app[AppKeys.DEVELOPERS] = [
u for u in users if SystemPermission.MANAGE_CI in u['system_permissions'] and u['state'] == 'active'

This comment was marked as resolved.

{% endif %}

{% if userdata['is_developer'] == 1 %}
{% if userdata['system_permissions']['view_monitoring_dashboards'] %}

This comment was marked as resolved.

db = request.app[AppKeys.DB]
users = [x async for x in db.select_and_fetchall('SELECT * FROM users;')]
users = await _get_users(db)
page_context = {'users': users}

This comment was marked as resolved.

Comment on lines 649 to 655
system_roles_raw = post.get('system_roles', [])
if isinstance(system_roles_raw, list):
system_roles = [str(role) for role in system_roles_raw]
elif system_roles_raw:
raise web.HTTPBadRequest(text='Invalid system_roles value. Must be a list of role name strings.')
else:
system_roles = []

This comment was marked as resolved.

ident_token = f'{username}-{token}'

if user['is_developer'] == 1 or user['is_service_account'] == 1 or username == 'test':
if 'access_developer_environments' in user['system_roles'] or user['is_service_account'] == 1 or username == 'test':

This comment was marked as resolved.

# auth services in test namespaces cannot/should not be creating and deleting namespaces
if namespace_name is not None and namespace_name != DEFAULT_NAMESPACE and not is_test_deployment:
assert user['is_developer'] == 1
if 'access_developer_environments' not in user['system_roles']:

This comment was marked as resolved.

) -> web.Response:
if request.headers.get('x-hail-return-jinja-context'):
if userdata and userdata['is_developer']:
if userdata and userdata['system_permissions'][SystemPermission.READ_PRERENDERED_JINJA2_CONTEXT]:

This comment was marked as resolved.

</a>

{% if userdata['is_developer'] == 1 %}
{% if userdata['system_permissions']['read_system_roles'] or userdata['system_permissions']['read_users'] %}

This comment was marked as resolved.

<div class="header-dropdown-menu">
<div class="batch-caret header-dropdown-menu-caret"></div>
{% if userdata['is_developer'] == 1 %}
{% if userdata['system_permissions']['read_deployed_system_state'] %}

This comment was marked as resolved.

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.

1 participant