예제 #1
0
 def issueMacaroon(self, issuer_name, context_type, context):
     """See `IAuthServer.issueMacaroon`."""
     try:
         issuer = getUtility(IMacaroonIssuer, issuer_name)
     except ComponentLookupError:
         return faults.PermissionDenied()
     # Only permit issuers that have been specifically designed for use
     # with the authserver: they must need to be issued by parts of
     # Launchpad other than appservers but be verified by appservers,
     # they must take parameters that can be passed over XML-RPC, and
     # they must issue macaroons with carefully-designed constraints to
     # minimise privilege-escalation attacks.
     if not issuer.issuable_via_authserver:
         return faults.PermissionDenied()
     # The context is plain data, since we can't pass general objects over
     # the XML-RPC interface.  Look it up so that we can verify it.
     context = self._resolveContext(context_type, context)
     if context is None:
         return faults.PermissionDenied()
     try:
         # issueMacaroon isn't normally public, but we clearly need it
         # here.
         macaroon = removeSecurityProxy(issuer).issueMacaroon(context)
     except BadMacaroonContext:
         return faults.PermissionDenied()
     return macaroon.serialize()
예제 #2
0
        def create_branch(requester):
            if not branch_path.startswith('/'):
                return faults.InvalidPath(branch_path)
            escaped_path = unescape(branch_path.strip('/'))
            try:
                namespace, branch_name, link_func, path = (
                    self._getBranchNamespaceExtras(escaped_path, requester))
            except ValueError:
                return faults.PermissionDenied("Cannot create branch at '%s'" %
                                               branch_path)
            except InvalidNamespace:
                return faults.PermissionDenied("Cannot create branch at '%s'" %
                                               branch_path)
            except NoSuchPerson as e:
                return faults.NotFound("User/team '%s' does not exist." %
                                       e.name)
            except NoSuchProduct as e:
                return faults.NotFound("Project '%s' does not exist." % e.name)
            except InvalidProductName as e:
                return faults.InvalidProductName(escape(e.name))
            except NoSuchSourcePackageName as e:
                try:
                    getUtility(ISourcePackageNameSet).new(e.name)
                except InvalidName:
                    return faults.InvalidSourcePackageName(e.name)
                return self.createBranch(login_id, branch_path)
            except NameLookupFailed as e:
                return faults.NotFound(str(e))
            try:
                branch = namespace.createBranch(BranchType.HOSTED, branch_name,
                                                requester)
            except LaunchpadValidationError as e:
                msg = e.args[0]
                if isinstance(msg, unicode):
                    msg = msg.encode('utf-8')
                return faults.PermissionDenied(msg)
            except BranchCreationException as e:
                return faults.PermissionDenied(str(e))

            if link_func:
                try:
                    link_func(branch)
                except Unauthorized:
                    # We don't want to keep the branch we created.
                    transaction.abort()
                    return faults.PermissionDenied(
                        "Cannot create linked branch at '%s'." % path)

            return branch.id
 def test_issueMacaroon_not_via_authserver(self):
     build = self.factory.makeBinaryPackageBuild(
         archive=self.factory.makeArchive(private=True))
     private_root = getUtility(IPrivateApplication)
     authserver = AuthServerAPIView(private_root.authserver, TestRequest())
     self.assertEqual(
         faults.PermissionDenied(),
         authserver.issueMacaroon(
             "binary-package-build", "BinaryPackageBuild", build))
예제 #4
0
 def _parseUniqueName(self, branch_path):
     """Return a dict of the parsed information and the branch name."""
     try:
         namespace_path, branch_name = branch_path.rsplit('/', 1)
     except ValueError:
         raise faults.PermissionDenied(
             "Cannot create branch at '/%s'" % branch_path)
     data = BranchNamespaceSet().parse(namespace_path)
     return data, branch_name
예제 #5
0
 def createBranch(self, requester_id, branch_path):
     if not branch_path.startswith('/'):
         return faults.InvalidPath(branch_path)
     escaped_path = unescape(branch_path.strip('/'))
     registrant = self._person_set.get(requester_id)
     try:
         return self._createBranch(registrant, escaped_path)
     except LaunchpadFault as e:
         return e
     except LaunchpadValidationError as e:
         return faults.PermissionDenied(six.ensure_binary(e.args[0]))
예제 #6
0
 def _serializeBranch(self, requester_id, branch, trailing_path,
                      force_readonly=False):
     if not self._canRead(requester_id, branch):
         return faults.PermissionDenied()
     elif branch.branch_type == BranchType.REMOTE:
         return None
     if force_readonly:
         writable = False
     else:
         writable = self._canWrite(requester_id, branch)
     return (
         BRANCH_TRANSPORT,
         {'id': branch.id, 'writable': writable},
         trailing_path)
예제 #7
0
 def createBranch(self, requester_id, branch_path):
     if not branch_path.startswith('/'):
         return faults.InvalidPath(branch_path)
     escaped_path = unescape(branch_path.strip('/'))
     registrant = self._person_set.get(requester_id)
     try:
         return self._createBranch(registrant, escaped_path)
     except LaunchpadFault as e:
         return e
     except LaunchpadValidationError as e:
         msg = e.args[0]
         if isinstance(msg, unicode):
             msg = msg.encode('utf-8')
         return faults.PermissionDenied(msg)
예제 #8
0
 def _serializeBranch(self, requester, branch, trailing_path,
                      force_readonly=False):
     if requester == LAUNCHPAD_SERVICES:
         branch = removeSecurityProxy(branch)
     try:
         branch_id = branch.id
     except Unauthorized:
         raise faults.PermissionDenied()
     if branch.branch_type == BranchType.REMOTE:
         return None
     if force_readonly:
         writable = False
     else:
         writable = self._canWriteToBranch(requester, branch)
     return (
         BRANCH_TRANSPORT,
         {'id': branch_id, 'writable': writable, 'private': branch.private},
         trailing_path)
예제 #9
0
 def _translatePath(self, requester, path, permission, auth_params):
     if requester == LAUNCHPAD_ANONYMOUS:
         requester = None
     try:
         result = self._performLookup(requester, path, auth_params)
         if (result is None and requester is not None and
             permission == "write"):
             self._createRepository(requester, path)
             result = self._performLookup(requester, path, auth_params)
         if result is None:
             raise faults.GitRepositoryNotFound(path)
         if permission != "read" and not result["writable"]:
             raise faults.PermissionDenied()
         return result
     except (faults.PermissionDenied, faults.GitRepositoryNotFound):
         # Turn lookup errors for anonymous HTTP requests into
         # "authorisation required", so that the user-agent has a
         # chance to try HTTP basic auth.
         can_authenticate = auth_params.get("can-authenticate", False)
         if can_authenticate and requester is None:
             raise faults.Unauthorized()
         else:
             raise
예제 #10
0
    def _createBranch(self, registrant, branch_path):
        """The guts of the create branch method.

        Raises exceptions on error conditions.
        """
        to_link = None
        if branch_path.startswith(BRANCH_ALIAS_PREFIX + '/'):
            branch_path = branch_path[len(BRANCH_ALIAS_PREFIX) + 1:]
            if branch_path.startswith('~'):
                data, branch_name = self._parseUniqueName(branch_path)
            else:
                tokens = branch_path.split('/')
                data = {
                    'person': registrant.name,
                    'product': tokens[0],
                    }
                branch_name = 'trunk'
                # check the series
                product = self._product_set.getByName(data['product'])
                if product is not None:
                    if len(tokens) > 1:
                        series = product.getSeries(tokens[1])
                        if series is None:
                            raise faults.NotFound(
                                "No such product series: '%s'." % tokens[1])
                        else:
                            to_link = ICanHasLinkedBranch(series)
                    else:
                        to_link = ICanHasLinkedBranch(product)
                # don't forget the link.
        else:
            data, branch_name = self._parseUniqueName(branch_path)

        owner = self._person_set.getByName(data['person'])
        if owner is None:
            raise faults.NotFound(
                "User/team '%s' does not exist." % (data['person'],))
        # The real code consults the branch creation policy of the product. We
        # don't need to do so here, since the tests above this layer never
        # encounter that behaviour. If they *do* change to rely on the branch
        # creation policy, the observed behaviour will be failure to raise
        # exceptions.
        if not registrant.inTeam(owner):
            raise faults.PermissionDenied(
                ('%s cannot create branches owned by %s'
                 % (registrant.displayname, owner.displayname)))
        product = sourcepackage = None
        if data['product'] == '+junk':
            product = None
        elif data['product'] is not None:
            if not valid_name(data['product']):
                raise faults.InvalidProductName(escape(data['product']))
            product = self._product_set.getByName(data['product'])
            if product is None:
                raise faults.NotFound(
                    "Project '%s' does not exist." % (data['product'],))
        elif data['distribution'] is not None:
            distro = self._distribution_set.getByName(data['distribution'])
            if distro is None:
                raise faults.NotFound(
                    "No such distribution: '%s'." % (data['distribution'],))
            distroseries = self._distroseries_set.getByName(
                data['distroseries'])
            if distroseries is None:
                raise faults.NotFound(
                    "No such distribution series: '%s'."
                    % (data['distroseries'],))
            sourcepackagename = self._sourcepackagename_set.getByName(
                data['sourcepackagename'])
            if sourcepackagename is None:
                try:
                    sourcepackagename = self._sourcepackagename_set.new(
                        data['sourcepackagename'])
                except InvalidName:
                    raise faults.InvalidSourcePackageName(
                        data['sourcepackagename'])
            sourcepackage = self._factory.makeSourcePackage(
                distroseries, sourcepackagename)
        else:
            raise faults.PermissionDenied(
                "Cannot create branch at '%s'" % branch_path)
        branch = self._factory.makeBranch(
            owner=owner, name=branch_name, product=product,
            sourcepackage=sourcepackage, registrant=registrant,
            branch_type=BranchType.HOSTED)
        if to_link is not None:
            if registrant.inTeam(to_link.product.owner):
                to_link.branch = branch
            else:
                self._branch_set._delete(branch)
                raise faults.PermissionDenied(
                    "Cannot create linked branch at '%s'." % branch_path)
        return branch.id
예제 #11
0
    def _createRepository(self, requester, path, clone_from=None):
        try:
            namespace, repository_name, default_func = (
                self._getGitNamespaceExtras(path, requester))
        except InvalidNamespace:
            raise faults.PermissionDenied(
                "'%s' is not a valid Git repository path." % path)
        except NoSuchPerson as e:
            raise faults.NotFound("User/team '%s' does not exist." % e.name)
        except (NoSuchProduct, InvalidProductName) as e:
            raise faults.NotFound("Project '%s' does not exist." % e.name)
        except NoSuchSourcePackageName as e:
            try:
                getUtility(ISourcePackageNameSet).new(e.name)
            except InvalidName:
                raise faults.InvalidSourcePackageName(e.name)
            return self._createRepository(requester, path)
        except NameLookupFailed as e:
            raise faults.NotFound(unicode(e))
        except GitRepositoryCreationForbidden as e:
            raise faults.PermissionDenied(unicode(e))

        try:
            repository = namespace.createRepository(
                GitRepositoryType.HOSTED, requester, repository_name)
        except LaunchpadValidationError as e:
            # Despite the fault name, this just passes through the exception
            # text so there's no need for a new Git-specific fault.
            raise faults.InvalidBranchName(e)
        except GitRepositoryExists as e:
            # We should never get here, as we just tried to translate the
            # path and found nothing (not even an inaccessible private
            # repository).  Log an OOPS for investigation.
            self._reportError(path, e)
        except GitRepositoryCreationException as e:
            raise faults.PermissionDenied(unicode(e))

        try:
            if default_func:
                try:
                    default_func(repository)
                except Unauthorized:
                    raise faults.PermissionDenied(
                        "You cannot set the default Git repository for '%s'." %
                        path)

            # Flush to make sure that repository.id is populated.
            Store.of(repository).flush()
            assert repository.id is not None

            # If repository has target_default, clone from default.
            target_path = None
            try:
                default = self.repository_set.getDefaultRepository(
                    repository.target)
                if default is not None and default.visibleByUser(requester):
                    target_path = default.getInternalPath()
                else:
                    default = self.repository_set.getDefaultRepositoryForOwner(
                        repository.owner, repository.target)
                    if (default is not None and
                            default.visibleByUser(requester)):
                        target_path = default.getInternalPath()
            except GitTargetError:
                pass  # Ignore Personal repositories.

            hosting_path = repository.getInternalPath()
            try:
                getUtility(IGitHostingClient).create(
                    hosting_path, clone_from=target_path)
            except GitRepositoryCreationFault as e:
                # The hosting service failed.  Log an OOPS for investigation.
                self._reportError(path, e, hosting_path=hosting_path)
        except Exception:
            # We don't want to keep the repository we created.
            transaction.abort()
            raise
예제 #12
0
 def test_issue_bad_context(self):
     build = self.factory.makeSnapBuild()
     self.assertEqual(
         faults.PermissionDenied(),
         self.authserver.issueMacaroon('test', 'SnapBuild', build.id))
예제 #13
0
 def test_issue_not_issuable_via_authserver(self):
     self.issuer.issuable_via_authserver = False
     self.assertEqual(
         faults.PermissionDenied(),
         self.authserver.issueMacaroon('test', 'LibraryFileAlias', 1))
예제 #14
0
 def test_issue_wrong_context_type(self):
     self.assertEqual(
         faults.PermissionDenied(),
         self.authserver.issueMacaroon('unknown-issuer', 'nonsense', 1))
예제 #15
0
 def test_issue_unknown_issuer(self):
     self.assertEqual(
         faults.PermissionDenied(),
         self.authserver.issueMacaroon('unknown-issuer', 'LibraryFileAlias',
                                       1))