Merge branch 'main' into avm99963-monorail
Merged commit 34d8229ae2b51fb1a15bd208e6fe6185c94f6266
GitOrigin-RevId: 7ee0917f93a577e475f8e09526dd144d245593f4
diff --git a/framework/permissions.py b/framework/permissions.py
index ac46af6..37f6cdc 100644
--- a/framework/permissions.py
+++ b/framework/permissions.py
@@ -1,7 +1,6 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file or at
-# https://developers.google.com/open-source/licenses/bsd
+# Copyright 2016 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
"""Classes and functions to implement permission checking.
@@ -32,10 +31,10 @@
import settings
from framework import framework_bizobj
from framework import framework_constants
-from proto import project_pb2
-from proto import site_pb2
-from proto import tracker_pb2
-from proto import usergroup_pb2
+from mrproto import project_pb2
+from mrproto import site_pb2
+from mrproto import tracker_pb2
+from mrproto import usergroup_pb2
from tracker import tracker_bizobj
# Constants that define permissions.
@@ -333,40 +332,53 @@
# Project owners can view and edit artifacts in a LIVE project.
(OWNER_ROLE, project_pb2.ProjectState.LIVE, WILDCARD_ACCESS):
- OWNER_ACTIVE_PERMISSIONSET,
+ OWNER_ACTIVE_PERMISSIONSET,
# Project owners can view, but not edit artifacts in ARCHIVED.
# Note: EDIT_PROJECT is not enough permission to change an ARCHIVED project
# back to LIVE if a delete_time was set.
(OWNER_ROLE, project_pb2.ProjectState.ARCHIVED, WILDCARD_ACCESS):
- OWNER_INACTIVE_PERMISSIONSET,
+ OWNER_INACTIVE_PERMISSIONSET,
# Project members can view their own project, regardless of state.
(COMMITTER_ROLE, project_pb2.ProjectState.LIVE, WILDCARD_ACCESS):
- COMMITTER_ACTIVE_PERMISSIONSET,
+ COMMITTER_ACTIVE_PERMISSIONSET,
(COMMITTER_ROLE, project_pb2.ProjectState.ARCHIVED, WILDCARD_ACCESS):
- COMMITTER_INACTIVE_PERMISSIONSET,
+ COMMITTER_INACTIVE_PERMISSIONSET,
# Project contributors can view their own project, regardless of state.
(CONTRIBUTOR_ROLE, project_pb2.ProjectState.LIVE, WILDCARD_ACCESS):
- CONTRIBUTOR_ACTIVE_PERMISSIONSET,
+ CONTRIBUTOR_ACTIVE_PERMISSIONSET,
(CONTRIBUTOR_ROLE, project_pb2.ProjectState.ARCHIVED, WILDCARD_ACCESS):
- CONTRIBUTOR_INACTIVE_PERMISSIONSET,
+ CONTRIBUTOR_INACTIVE_PERMISSIONSET,
- # Non-members users can read and comment in projects with access == ANYONE
- (USER_ROLE, project_pb2.ProjectState.LIVE,
- project_pb2.ProjectAccess.ANYONE):
- USER_PERMISSIONSET,
+ # Non-members users can read and comment in projects with access == ANYONE.
+ (
+ USER_ROLE, project_pb2.ProjectState.LIVE,
+ project_pb2.ProjectAccess.ANYONE):
+ USER_PERMISSIONSET,
- # Anonymous users can only read projects with access == ANYONE.
- (ANON_ROLE, project_pb2.ProjectState.LIVE,
- project_pb2.ProjectAccess.ANYONE):
- READ_ONLY_PERMISSIONSET,
+ # Non-members users can read archived projects with access == ANYONE.
+ (
+ USER_ROLE, project_pb2.ProjectState.ARCHIVED,
+ project_pb2.ProjectAccess.ANYONE):
+ READ_ONLY_PERMISSIONSET,
+
+ # Anonymous users can only read projects with access == ANYONE,
+ # regardless of state.
+ (
+ ANON_ROLE, project_pb2.ProjectState.LIVE,
+ project_pb2.ProjectAccess.ANYONE):
+ READ_ONLY_PERMISSIONSET,
+ (
+ ANON_ROLE, project_pb2.ProjectState.ARCHIVED,
+ project_pb2.ProjectAccess.ANYONE):
+ READ_ONLY_PERMISSIONSET,
# Permissions for site pages, e.g., creating a new project
(USER_ROLE, UNDEFINED_STATUS, UNDEFINED_ACCESS):
- PermissionSet([CREATE_PROJECT, CREATE_GROUP, CREATE_HOTLIST]),
- }
+ PermissionSet([CREATE_PROJECT, CREATE_GROUP, CREATE_HOTLIST]),
+}
def GetPermissions(user, effective_ids, project):
"""Return a permission set appropriate for the user and project.
@@ -428,7 +440,7 @@
def UpdateIssuePermissions(
perms, project, issue, effective_ids, granted_perms=None, config=None):
- """Update the PermissionSet for an specific issue.
+ """Update the PermissionSet for a specific issue.
Take into account granted permissions and label restrictions to filter the
permissions, and updates the VIEW and EDIT_ISSUE permissions depending on the
@@ -488,7 +500,7 @@
filtered_perms.update(granted_perms)
# The VIEW perm might have been removed due to restrictions, but the issue
- # owner, reporter, cc and approvers can always be an issue.
+ # owner, reporter, cc and approvers can always view an issue.
allowed_ids = set(
tracker_bizobj.GetCcIds(issue)
+ tracker_bizobj.GetApproverIds(issue)
@@ -507,7 +519,8 @@
# The EDIT_ISSUE permission might have been removed due to restrictions, but
# the owner always has permission to edit it.
- if effective_ids and tracker_bizobj.GetOwnerId(issue) in effective_ids:
+ if (effective_ids and tracker_bizobj.GetOwnerId(issue) in effective_ids and
+ project and project.state != project_pb2.ProjectState.ARCHIVED):
filtered_perms.add(EDIT_ISSUE.lower())
return PermissionSet(filtered_perms, perms.consider_restrictions)
@@ -1113,6 +1126,36 @@
return perms.CanUsePerm(EDIT_ISSUE_APPROVAL, effective_ids, project, [])
+def CanEditProjectConfig(mr, services):
+ """ Special function to check if a user can edit a project config.
+
+ This function accounts for special edge cases pertaining only to project
+ configuration editing permissions, such as checking if a project is frozen
+ for config edits or if a user is in the allowlist of users who can override
+ a config freeze.
+
+ Args:
+ mr: MonorailRequest object.
+ services: reference to database layer.
+
+ Returns:
+ True if the user can edit the project.
+ """
+ if mr.project.project_id not in settings.config_freeze_project_ids:
+ return mr.perms.CanUsePerm(
+ EDIT_PROJECT, mr.auth.effective_ids, mr.project, [])
+
+ effective_users = services.user.GetUsersByIDs(
+ mr.cnxn, list(mr.auth.effective_ids))
+
+ for _, user in effective_users.items():
+ if user.email in settings.config_freeze_override_users.get(
+ mr.project.project_id, {}):
+ return True
+
+ return False
+
+
def CanViewComponentDef(effective_ids, perms, project, component_def):
"""Return True if a user can view the given component definition."""
if not effective_ids.isdisjoint(component_def.admin_ids):
@@ -1122,8 +1165,53 @@
return perms.CanUsePerm(VIEW, effective_ids, project, [])
-def CanEditComponentDef(effective_ids, perms, project, component_def, config):
- """Return True if a user can edit the given component definition."""
+def CanEditComponentDef(mr, services, component_def, config):
+ """ Checks if the currently logged in user can edit a component.
+
+ Args:
+ mr: MonorailRequest object.
+ services: reference to database layer.
+ component_def: the component to check permissions for.
+ config: project config of the project the component is in.
+
+ Returns:
+ True if a user can edit the given component definition."""
+ if mr.project.project_id in settings.config_freeze_project_ids:
+ return CanEditProjectConfig(mr, services)
+
+ if not mr.auth.effective_ids.isdisjoint(component_def.admin_ids):
+ return True # Component admins can edit that component.
+
+ # Check to see if user is admin of any parent component.
+ parent_components = tracker_bizobj.FindAncestorComponents(
+ config, component_def)
+ for parent in parent_components:
+ if not mr.auth.effective_ids.isdisjoint(parent.admin_ids):
+ return True
+
+ return CanEditProjectConfig(mr, services)
+
+
+def CanEditComponentDefLegacy(
+ effective_ids, perms, project, component_def, config):
+ """ Legacy version of CanEditComponentDef for codepaths without access to mr.
+ This function is entirely used in API clients.
+
+ Args:
+ effective_ids: Set containing IDs for the user and their groups
+ linked accounts, etc.
+ perms: PermissionSet for current user.
+ project: the project the component is in.
+ component_def: the component to check permissions for.
+ config: project config of the project the component is in.
+
+ Returns:
+ True if a user can edit the given component definition."""
+ # Do not bother checking if API client users are allowlisted to override
+ # the config freeze. Only human users are currently being allowlisted.
+ if project and project.project_id in settings.config_freeze_project_ids:
+ return False
+
if not effective_ids.isdisjoint(component_def.admin_ids):
return True # Component admins can edit that component.