releases/openstack_releases/cmds/find_gerrit_acl_issues.py
Doug Hellmann a12d59edd1 add a script to examine the gerrit acls for issues
This is based on mnaser's work in https://review.openstack.org/533780/

Change-Id: Ie6f94db0dce2f5331c3d21a1192533ffc648329b
Signed-off-by: Doug Hellmann <doug@doughellmann.com>
2018-02-23 11:52:07 -05:00

187 lines
5.8 KiB
Python

# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Look for governed projects that have their tagging ACLs misconfigured.
"""
import argparse
import configparser
import json
import os.path
import urllib
import appdirs
import requests
from requests.packages import urllib3
import openstack_releases
from openstack_releases import defaults
from openstack_releases import deliverable
from openstack_releases import governance
# Disable warnings about insecure connections.
urllib3.disable_warnings()
IGNORED_TEAMS = [
'Infrastructure',
'OpenStack Charms',
]
ALLOWED = [
'library-release',
'Release Managers',
'openstack-chef-release',
'xstatic-release',
'release-tools-core',
'releases-core',
]
class GerritClient(object):
BASE = 'https://review.openstack.org:443/a/'
def __init__(self, user, password):
self._user = user
self._password = password
self._auth = requests.auth.HTTPDigestAuth(
self._user,
self._password,
)
self._groups = {}
def _mk_url(self, api, *args):
encoded = [
urllib.parse.quote_plus(a)
for a in args
]
return self.BASE + api.format(*encoded)
def _get(self, url):
response = requests.get(url, auth=self._auth)
if response.status_code == 404:
raise ValueError(404)
# strip off first few chars because 'the JSON response body starts with
# a magic prefix line that must be stripped before feeding the rest of
# the response body to a JSON parser'
# https://review.openstack.org/Documentation/rest-api.html
# print(response.text)
return json.loads(response.text[5:])
def get_access(self, repo):
url = self._mk_url('projects/{}/access', repo)
return self._get(url)
def get_group(self, group_id):
if group_id in self._groups:
return self._groups[group_id]
url = self._mk_url('groups/{}', group_id)
data = self._get(url)
self._groups[group_id] = data
return data
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'--verbose', '-v',
action='store_true',
default=False,
)
parser.add_argument(
'--deliverables-dir',
default=openstack_releases.deliverable_dir,
help='location of deliverable files',
)
args = parser.parse_args()
config_filename = os.path.join(
appdirs.user_config_dir('openstack-release', 'openstack'),
'gerrit.ini',
)
config = configparser.ConfigParser()
config.read(config_filename, encoding='utf-8')
if not config.has_option('DEFAULT', 'username'):
parser.error('No username set in {}'.format(config_filename))
if not config.has_option('DEFAULT', 'password'):
parser.error('No password set in {}'.format(config_filename))
team_data = governance.get_team_data()
# Some deliverables were independent at one time but might not be
# any more, so compare the independent list with the current
# release series.
all_independent_deliverables = set(
name
for team, series, name, deliv in deliverable.Deliverables(
root_dir=args.deliverables_dir,
collapse_history=True,
).get_deliverables(None, None)
)
current_deliverables = set(
name
for team, series, name, deliv in deliverable.Deliverables(
root_dir=args.deliverables_dir,
collapse_history=True,
).get_deliverables(None, defaults.RELEASE)
)
independent_deliverables = all_independent_deliverables.difference(
current_deliverables)
gerrit = GerritClient(
config['DEFAULT']['username'],
config['DEFAULT']['password'],
)
for repo in governance.get_repositories(team_data, code_only=True):
if repo.deliverable.team.name in IGNORED_TEAMS:
if args.verbose:
print('{}: ignoring {} team'.format(
repo.name, repo.deliverable.team.name))
continue
if repo.deliverable.name in independent_deliverables:
if args.verbose:
print('{}: ignoring independent deliverable'.format(
repo.name))
continue
acls = gerrit.get_access(repo.name)
local_tag_acls = acls.get('local', {}).get('refs/tags/*', {})
if local_tag_acls:
rules = local_tag_acls.get('permissions', {}).get(
'pushSignedTag', {}).get('rules', {})
if not rules and args.verbose:
print('{}: OK'.format(repo.name))
for group_id, permissions in rules.items():
group_details = gerrit.get_group(group_id)
group_name = group_details['name']
if group_name in ALLOWED:
if args.verbose:
print('{}: {} pushSignedTag OK'.format(
repo.name, group_name))
continue
if args.verbose:
print('{}: {} pushSignedTag WARNING'.format(
repo.name, group_name))
else:
print('{}: {} pushSignedTag'.format(
repo.name, group_name))