From 7b7c2388510c68277fc0069685fa2cf48f1d5d45 Mon Sep 17 00:00:00 2001 From: Patrick Uiterwijk Date: Sep 24 2018 19:03:15 +0000 Subject: Add a small ACLChecker script This script can be used as ssh command if gitolite is not used to make sure that the user is able to perform any actions on the repo, including cloning. Signed-off-by: Patrick Uiterwijk --- diff --git a/files/aclchecker.py b/files/aclchecker.py new file mode 100644 index 0000000..db3b0b0 --- /dev/null +++ b/files/aclchecker.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" + (c) 2014-2018 - Copyright Red Hat Inc + + Authors: + Patrick Uiterwijk + +""" + +from __future__ import unicode_literals, print_function + +import subprocess +import sys +import os + +# Since this is run by sshd, we don't have a way to set environment +# variables ahead of time +if "PAGURE_CONFIG" not in os.environ and os.path.exists( + "/etc/pagure/pagure.cfg" +): + os.environ["PAGURE_CONFIG"] = "/etc/pagure/pagure.cfg" + +# Here starts the code +import pagure +import pagure.lib +from pagure.utils import is_repo_user +from pagure.config import config as pagure_config + + +# Get the arguments +if len(sys.argv) != 2: + print("Invalid call, too few arguments", file=sys.stderr) + sys.exit(1) +remoteuser = sys.argv[1] + +args = os.environ["SSH_ORIGINAL_COMMAND"].split(" ") +# Expects: +if len(args) != 2: + print("Invalid call, too few inner arguments", file=sys.stderr) + sys.exit(1) + + +cmd = args[0] +path = args[1] +if cmd not in ("git-receive-pack", "git-upload-pack"): + print("Invalid call, invalid operation", file=sys.stderr) + sys.exit(1) + +# Git will encode the file path argument within single quotes +if path[0] != "'" or path[-1] != "'": + print("Invalid call: invalid path", file=sys.stderr) + sys.exit(1) +path = path[1:-1] + +if os.path.isabs(path): + print("Non-full path expected, not %s" % path, file=sys.stderr) + sys.exit(1) + +if not path.endswith(".git"): + path = path + ".git" + +session = pagure.lib.create_session(pagure_config["DB_URL"]) +if not session: + raise Exception("Unable to initialize db session") + +gitdir = os.path.join(pagure_config["GIT_FOLDER"], path) +(repotype, username, namespace, repo) = pagure.lib.git.get_repo_info_from_path( + gitdir, hide_notfound=True +) + +if repo is None: + print("Repo not found", file=sys.stderr) + sys.exit(1) + +project = pagure.lib.get_authorized_project( + session, repo, user=username, namespace=namespace, asuser=remoteuser +) + +if not project: + print("Repo not found", file=sys.stderr) + sys.exit(1) + +if repotype != "main" and not is_repo_user(project, remoteuser): + print("Repo not found", file=sys.stderr) + sys.exit(1) + +# Now go run git +# We verified that cmd is either "git-receive-pack" or "git-send-pack" +# and "gitdir" is a full, absolute, path within GIT_FOLDER that points to +# the canonical location for this git repo. +os.execvp(cmd, [cmd, gitdir]) diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py index 89a27f4..2a9bd61 100644 --- a/pagure/lib/__init__.py +++ b/pagure/lib/__init__.py @@ -5435,7 +5435,7 @@ def issues_history_stats(session, project): return output -def get_authorized_project(session, project_name, user=None, namespace=None): +def get_authorized_project(session, project_name, user=None, namespace=None, asuser=None): """ Retrieving the project with user permission constraint :arg session: The SQLAlchemy session to use @@ -5446,6 +5446,8 @@ def get_authorized_project(session, project_name, user=None, namespace=None): :type user: String :arg namespace: Pagure namespace :type namespace: String + :arg asuser: Username to check for access + :type asuser: String :return: The project object if project is public or user has permissions for the project else it returns None :rtype: Project @@ -5453,7 +5455,7 @@ def get_authorized_project(session, project_name, user=None, namespace=None): """ repo = pagure.lib._get_project(session, project_name, user, namespace) - if repo and repo.private and not pagure.utils.is_repo_user(repo): + if repo and repo.private and not pagure.utils.is_repo_user(repo, asuser): return None return repo diff --git a/pagure/lib/git.py b/pagure/lib/git.py index e9ac914..08b3f80 100644 --- a/pagure/lib/git.py +++ b/pagure/lib/git.py @@ -1261,7 +1261,7 @@ def get_commit_subject(commit, abspath): return subject -def get_repo_info_from_path(gitdir): +def get_repo_info_from_path(gitdir, hide_notfound=False): """ Returns the name, username, namespace and type of a git directory This gets computed based on the *_FOLDER's in the config file, @@ -1269,6 +1269,10 @@ def get_repo_info_from_path(gitdir): Args: gitdir (string): Path of the canonical git repository + hide_notfound (bool): Whether to return a tuple with None's instead of + raising an error if the regenerated repo didn't exist. + Can be used to hide the difference between no project access vs not + existing when looking up private repos. Return: (tuple): Tuple with (repotype, username, namespace, repo) Some of these elements may be None if not applicable. """ @@ -1356,7 +1360,10 @@ def get_repo_info_from_path(gitdir): % (rebuiltpath, gitdir) ) if not os.path.exists(rebuiltpath): - raise ValueError("Splitting gitdir %s failed" % gitdir) + if hide_notfound: + return (None, None, None, None) + else: + raise ValueError("Splitting gitdir %s failed" % gitdir) return (repotype, username, namespace, repo) diff --git a/pagure/utils.py b/pagure/utils.py index 25165ca..0ab261d 100644 --- a/pagure/utils.py +++ b/pagure/utils.py @@ -161,12 +161,11 @@ def is_repo_committer(repo_obj, username=None): def is_repo_user(repo_obj, username=None): """ Return whether the user has some access in the provided repo. """ - if not authenticated(): - return False - if username: user = username else: + if not authenticated(): + return False user = flask.g.fas_user.username if is_admin():