From patchwork Mon Aug 2 15:27:28 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Axtens X-Patchwork-Id: 1512497 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.ozlabs.org (client-ip=2404:9400:2:0:216:3eff:fee1:b9f1; helo=lists.ozlabs.org; envelope-from=patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=axtens.net header.i=@axtens.net header.a=rsa-sha256 header.s=google header.b=rZ/KrUcJ; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2404:9400:2:0:216:3eff:fee1:b9f1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Gdhhm2kjsz9sRN for ; Tue, 3 Aug 2021 01:27:52 +1000 (AEST) Received: from boromir.ozlabs.org (localhost [IPv6:::1]) by lists.ozlabs.org (Postfix) with ESMTP id 4Gdhhm1dZ0z3bXm for ; Tue, 3 Aug 2021 01:27:52 +1000 (AEST) Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=axtens.net header.i=@axtens.net header.a=rsa-sha256 header.s=google header.b=rZ/KrUcJ; dkim-atps=neutral X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=axtens.net (client-ip=2607:f8b0:4864:20::631; helo=mail-pl1-x631.google.com; envelope-from=dja@axtens.net; receiver=) Authentication-Results: lists.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=axtens.net header.i=@axtens.net header.a=rsa-sha256 header.s=google header.b=rZ/KrUcJ; dkim-atps=neutral Received: from mail-pl1-x631.google.com (mail-pl1-x631.google.com [IPv6:2607:f8b0:4864:20::631]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 4Gdhhf3l0xz30NX for ; Tue, 3 Aug 2021 01:27:46 +1000 (AEST) Received: by mail-pl1-x631.google.com with SMTP id c16so20012332plh.7 for ; Mon, 02 Aug 2021 08:27:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=axtens.net; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=FyMxBF8abrZxfPmc7o+Zo2QOuJRrYEQs4mEjim78oPA=; b=rZ/KrUcJTNphsgGJimDsFlA4drBQYuew3iW9X7wPH4pXsR7VkF4WU5gEEzOin635k6 zaTk+PUwxBg4kF2J4aJWJXFMc3ScS+Xlo14taBQOCmc6gef0Gr0aoLx8j5MizjysMK5V fCNeidRVqCO0U6vcudxq2sXVprTPUhXcbThdc= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=FyMxBF8abrZxfPmc7o+Zo2QOuJRrYEQs4mEjim78oPA=; b=riGFf7dFo8IcXtL2cxA6vHMn5LIxKSTkkdxBSVgutaAUy5pkN0v7+7oU/46023b2c/ VARyXLKYZV8V/7gzTxVnvBRVhV5Gq4iwIPObFFNs9TRYe79FPFrb9IX3nNskAqNo0+b4 rWMC04b3S/QIXON/3pdxrB1+5fydJS4LrRNG4Hv5kM5UEosoqtvEX+T68nBIVC0Tty2+ VKuCEVdBjMixTujUBvxX3konDrLa6r1xFuIKoy10t5zg9ksDerZ17hORTxzzvKZ9cE8s alpt+mH+l2IGjDWPxKV9am2Df0r/vCtfE8e1M3kGpSeEA/sh5AF0cWMdxlrwj/YrkETr QgmQ== X-Gm-Message-State: AOAM5304R6DlF2LP7OF6QRrMKShXW/M7WtLfIdNF4d6GjPW/svIi5hIp qqLuafD9WtsNCkGS19vWLtESO6LnKw/DXg== X-Google-Smtp-Source: ABdhPJxZv+yLYPMRTNYavtajQcl6TOAvb1W2swrwGNq+jLDDk0h+cCT9JnQ9x3oiObvO/aDQrQxcjw== X-Received: by 2002:a17:90a:ca93:: with SMTP id y19mr18450101pjt.142.1627918063880; Mon, 02 Aug 2021 08:27:43 -0700 (PDT) Received: from localhost (ip-145-57.yless4u.com.au. [103.22.145.57]) by smtp.gmail.com with ESMTPSA id j185sm12415058pfb.86.2021.08.02.08.27.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Aug 2021 08:27:43 -0700 (PDT) From: Daniel Axtens To: patchwork@lists.ozlabs.org Subject: [PATCH 2/3] xmlrpc: Allow a project to restrict submitter state changes Date: Tue, 3 Aug 2021 01:27:28 +1000 Message-Id: <20210802152729.2110734-3-dja@axtens.net> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210802152729.2110734-1-dja@axtens.net> References: <20210802152729.2110734-1-dja@axtens.net> MIME-Version: 1.0 X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" As with the UI. Signed-off-by: Daniel Axtens --- patchwork/tests/test_xmlrpc.py | 90 ++++++++++++++++++++++++++++++++++ patchwork/views/xmlrpc.py | 4 ++ 2 files changed, 94 insertions(+) diff --git a/patchwork/tests/test_xmlrpc.py b/patchwork/tests/test_xmlrpc.py index 4726fdffa5d5..eea0b4eaf560 100644 --- a/patchwork/tests/test_xmlrpc.py +++ b/patchwork/tests/test_xmlrpc.py @@ -10,6 +10,9 @@ from django.conf import settings from django.test import LiveServerTestCase from django.urls import reverse +from patchwork.models import Person +from patchwork.models import Project +from patchwork.models import State from patchwork.tests import utils @@ -81,6 +84,93 @@ class XMLRPCAuthenticatedTest(LiveServerTestCase): self.assertTrue(result['archived']) +@unittest.skipUnless(settings.ENABLE_XMLRPC, + 'requires xmlrpc interface (use the ENABLE_XMLRPC ' + 'setting)') +class XMLRPCStateSettingTest(LiveServerTestCase): + + def url_for_user(self, user): + return ('http://%s:%s@' + self.url[7:]) % \ + (user.username, user.username) + + def setUp(self): + self.url = self.live_server_url + reverse('xmlrpc') + # url is of the form http://localhost:PORT/PATH + # strip the http and replace it with the username/passwd of a user. + self.projects = {} + self.maintainers = {} + self.delegates = {} + self.submitters = {} + self.patches = {} + self.rpcs = {} + + for project_type in (Project.SUBMITTER_NO_STATE_CHANGES, + Project.SUBMITTER_ALL_STATE_CHANGES): + project = utils.create_project( + submitter_state_change_rules=project_type) + self.projects[project_type] = project + self.maintainers[project_type] = utils.create_maintainer(project) + submitter = utils.create_user(project) + self.submitters[project_type] = submitter + delegate = utils.create_user(project) + self.delegates[project_type] = delegate + + self.rpcs[project_type] = { + 'maintainer': ServerProxy(self.url_for_user( + self.maintainers[project_type])), + 'delegate': ServerProxy(self.url_for_user(delegate)), + 'submitter': ServerProxy(self.url_for_user(submitter)), + } + + patch = utils.create_patch(project=project, + submitter=Person.objects.get( + user=submitter), + delegate=delegate) + self.patches[project_type] = patch + + utils.create_state(name="New") + utils.create_state(name="RFC") + + def tearDown(self): + for project_type in self.rpcs: + rpc_dict = self.rpcs[project_type] + for user in rpc_dict: + rpc_dict[user].close() + + def can_set_state(self, patch, rpc): + new_state = State.objects.get(name="New") + rfc_state = State.objects.get(name="RFC") + patch.state = new_state + patch.save() + + result = rpc.patch_get(patch.id) + self.assertEqual(result['state_id'], new_state.id) + + try: + rpc.patch_set(patch.id, {'state': rfc_state.id}) + except xmlrpc_client.Fault: + return False + + # reload the patch + result = rpc.patch_get(patch.id) + self.assertEqual(result['state_id'], rfc_state.id) + return True + + def test_allset(self): + rpc_dict = self.rpcs[Project.SUBMITTER_ALL_STATE_CHANGES] + patch = self.patches[Project.SUBMITTER_ALL_STATE_CHANGES] + self.assertTrue(self.can_set_state(patch, rpc_dict['maintainer'])) + self.assertTrue(self.can_set_state(patch, rpc_dict['delegate'])) + self.assertTrue(self.can_set_state(patch, rpc_dict['submitter'])) + + def test_noset(self): + rpc_dict = self.rpcs[Project.SUBMITTER_NO_STATE_CHANGES] + patch = self.patches[Project.SUBMITTER_NO_STATE_CHANGES] + self.assertTrue(self.can_set_state(patch, rpc_dict['maintainer'])) + self.assertTrue(self.can_set_state(patch, rpc_dict['delegate'])) + self.assertFalse(self.can_set_state(patch, rpc_dict['submitter'])) + + class XMLRPCModelTestMixin(object): def create_multiple(self, count): diff --git a/patchwork/views/xmlrpc.py b/patchwork/views/xmlrpc.py index 6701bf20f386..d73cfa7a8441 100644 --- a/patchwork/views/xmlrpc.py +++ b/patchwork/views/xmlrpc.py @@ -713,6 +713,10 @@ def patch_set(user, patch_id, params): if not patch.is_editable(user): raise Exception('No permissions to edit this patch') + if 'state' in params: + if not patch.can_set_state(user): + raise Exception('No permissions to set patch state') + for (k, v) in params.items(): if k not in ok_params: continue