Skip to content

Commit

Permalink
Add getting groups and users with given access rights for Pagure (#834)
Browse files Browse the repository at this point in the history
Add getting groups and users with given access rights for Pagure

Needed for packit/packit#2088
RELEASE NOTES BEGIN
For Pagure there are 2 new methods available: one for getting users with specified access rights and one for getting members of a group.
RELEASE NOTES END

Reviewed-by: Nikola Forró
Reviewed-by: Laura Barcziová
  • Loading branch information
softwarefactory-project-zuul[bot] authored Jan 25, 2024
2 parents d183a6c + c8bc591 commit 1767349
Show file tree
Hide file tree
Showing 11 changed files with 1,279 additions and 156 deletions.
29 changes: 18 additions & 11 deletions COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,18 @@ In case you find any error, please [create a new issue](https://github.com/packi

## Project

| | GitHub | GitLab | Pagure |
| --------------------------- | :----: | :----: | :---------------------: |
| `change_token` ||||
| `get_release` ||||
| `get_latest_release` ||||
| `is_private` ||| ✘ (may not be accurate) |
| `remove_user` ||||
| `add_group` ||||
| `remove_group` ||||
| `which_groups_can_merge_pr` ||||
| `get_pr_files_diff` ||||
| | GitHub | GitLab | Pagure |
| ----------------------------- | :----: | :----: | :---------------------: |
| `change_token` ||||
| `get_release` ||||
| `get_latest_release` ||||
| `is_private` ||| ✘ (may not be accurate) |
| `remove_user` ||||
| `add_group` ||||
| `remove_group` ||||
| `which_groups_can_merge_pr` ||||
| `get_pr_files_diff` ||||
| `get_users_with_given_access` ||||

## User

Expand All @@ -76,3 +77,9 @@ In case you find any error, please [create a new issue](https://github.com/packi
| | GitHub | GitLab | Pagure |
| -------- | :----: | :----: | :----: |
| `delete` ||||

## Service

| | GitHub | GitLab | Pagure |
| ----------- | :----: | :----: | :----: |
| `get_group` ||||
16 changes: 16 additions & 0 deletions ogr/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -1369,6 +1369,12 @@ def list_projects(
"""
raise NotImplementedError

def get_group(self, group_name: str):
"""
Get a group by name.
"""
raise NotImplementedError


class GitProject(OgrAbstractClass):
def __init__(self, repo: str, service: GitService, namespace: str) -> None:
Expand Down Expand Up @@ -1528,6 +1534,16 @@ def can_merge_pr(self, username: str) -> bool:
"""
raise NotImplementedError()

def get_users_with_given_access(self, access_levels: list[AccessLevel]) -> set[str]:
"""
Args:
access_levels: list of access levels
Returns:
set of users with given access levels
"""
raise NotImplementedError()

def add_user(self, user: str, access_level: AccessLevel) -> None:
"""
Add user to project.
Expand Down
24 changes: 24 additions & 0 deletions ogr/services/pagure/group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT

import logging

from ogr.services import pagure as ogr_pagure

logger = logging.getLogger(__name__)


class PagureGroup:
service: "ogr_pagure.PagureService"

def __init__(self, name: str, raw_group: dict) -> None:
self.name = name
# see https://pagure.io/api/0/#groups-tab
self._raw_group = raw_group

def __str__(self) -> str:
return f'PagureGroup(name="{self.name}")'

@property
def members(self) -> list[str]:
return self._raw_group["members"]
106 changes: 92 additions & 14 deletions ogr/services/pagure/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import logging
from collections.abc import Iterable
from typing import Optional
from typing import ClassVar, Optional
from urllib.parse import urlparse

from ogr.abstract import (
Expand Down Expand Up @@ -37,6 +37,14 @@

class PagureProject(BaseGitProject):
service: "ogr_pagure.PagureService"
access_dict: ClassVar[dict] = {
AccessLevel.pull: "ticket",
AccessLevel.triage: "ticket",
AccessLevel.push: "commit",
AccessLevel.admin: "commit",
AccessLevel.maintain: "admin",
None: "",
}

def __init__(
self,
Expand Down Expand Up @@ -229,7 +237,19 @@ def which_groups_can_merge_pr(self) -> set[str]:
return groups

def can_merge_pr(self, username) -> bool:
return username in self.who_can_merge_pr()
accounts_that_can_merge_pr = self.who_can_merge_pr()

groups_that_can_merge_pr = self.which_groups_can_merge_pr()
accounts_that_can_merge_pr.update(
member
for group in groups_that_can_merge_pr
for member in self.service.get_group(group).members
)

logger.info(
f"All users (considering groups) that can merge PR: {accounts_that_can_merge_pr}",
)
return username in accounts_that_can_merge_pr

def request_access(self):
raise OperationNotSupported("Not possible on Pagure")
Expand Down Expand Up @@ -405,22 +425,14 @@ def add_user_or_group(
access_level: Optional[AccessLevel],
user_type: str,
) -> None:
access_dict = {
AccessLevel.pull: "ticket",
AccessLevel.triage: "ticket",
AccessLevel.push: "commit",
AccessLevel.admin: "commit",
AccessLevel.maintain: "admin",
None: "",
}
response = self._call_project_api_raw(
"git",
"modifyacls",
method="POST",
data={
"user_type": user_type,
"name": user,
"acl": access_dict[access_level],
"acl": self.access_dict[access_level],
},
)

Expand Down Expand Up @@ -592,9 +604,75 @@ def get_contributors(self) -> set[str]:
raise OperationNotSupported("Pagure doesn't provide list of contributors")

def users_with_write_access(self) -> set[str]:
users_with_access = self.get_project_info()["access_users"]
return self._get_users_with_given_access(["commit", "admin", "owner"])

def get_users_with_given_access(self, access_levels: list[AccessLevel]) -> set[str]:
access_levels_pagure = [
self.access_dict[access_level] for access_level in access_levels
]

# for AccessLevel.maintain get the maintainer as well
if AccessLevel.maintain in access_levels:
access_levels_pagure.append("owner")

return self._get_users_with_given_access(access_levels_pagure)

def _get_users_with_given_access(self, access_levels: list[str]) -> set[str]:
"""
Get all users (considering groups) with the access levels given by list.
Arguments:
access_levels: list of access levels, e.g. ['commit', 'admin']
"""
users = self._get_user_accounts_with_access(access_levels)

# group cannot have owner access
group_accounts = self._get_group_accounts_with_access(
list(set(access_levels) - {"owner"}),
)

users.update(
member
for group in group_accounts
for member in self.service.get_group(group).members
)

logger.info(
f"All users (considering groups) with given access levels: {users}",
)
return users

def _get_entity_accounts_with_access(
self,
access_levels: list[str],
entity_type: str,
) -> set[str]:
"""
Get the entity account names (users or groups) with the access levels given by the set.
Arguments:
access_levels: list of access levels, e.g. ['commit', 'admin']
entity_type: 'users' or 'groups'
"""
if entity_type not in ("users", "groups"):
raise OgrException(
f"Unsupported entity type {entity_type}: only 'users' and 'groups' are allowed.",
)
entity_info = self.get_project_info()["access_" + entity_type]
result = set()
for access_level in ["commit", "admin", "owner"]:
result.update(users_with_access[access_level])
for access_level in access_levels:
result.update(entity_info[access_level])

return result

def _get_user_accounts_with_access(self, access_levels: list[str]) -> set[str]:
"""
Get the users with the access levels given by the set.
"""
return self._get_entity_accounts_with_access(access_levels, "users")

def _get_group_accounts_with_access(self, access_levels: list[str]) -> set[str]:
"""
Get the groups with the access levels given by list.
"""
return self._get_entity_accounts_with_access(access_levels, "groups")
8 changes: 8 additions & 0 deletions ogr/services/pagure/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from ogr.factory import use_for_service
from ogr.parsing import parse_git_repo
from ogr.services.base import BaseGitService, GitProject
from ogr.services.pagure.group import PagureGroup
from ogr.services.pagure.project import PagureProject
from ogr.services.pagure.user import PagureUser
from ogr.utils import RequestResponse
Expand Down Expand Up @@ -373,3 +374,10 @@ def list_projects(
language: Optional[str] = None,
) -> list[GitProject]:
raise OperationNotSupported

def get_group(self, group_name: str) -> PagureGroup:
"""
Get a Pagure group by name.
"""
url = self.get_api_url("group", group_name)
return PagureGroup(group_name, self.call_api(url))
Loading

0 comments on commit 1767349

Please sign in to comment.