Merge lp:~mvo/aptdaemon/support-for-whitelisted-repositories into lp:aptdaemon

Proposed by Michael Vogt
Status: Merged
Merged at revision: 858
Proposed branch: lp:~mvo/aptdaemon/support-for-whitelisted-repositories
Merge into: lp:aptdaemon
Diff against target: 821 lines (+506/-36)
14 files modified
aptdaemon/core.py (+31/-1)
aptdaemon/policykit1.py (+4/-1)
aptdaemon/test.py (+15/-5)
aptdaemon/utils.py (+1/-0)
aptdaemon/worker.py (+112/-16)
data/org.debian.apt.policy.in (+24/-0)
tests/data/high-trust-repository-whitelist-broken.cfg (+10/-0)
tests/data/high-trust-repository-whitelist.cfg (+10/-0)
tests/repo/whitelisted/Packages (+34/-0)
tests/repo/whitelisted/Packages.gpg (+34/-0)
tests/repo/whitelisted/Release (+17/-0)
tests/test_client.py (+1/-0)
tests/test_high_trust_repository_whitelist.py (+201/-0)
tests/test_worker.py (+12/-13)
To merge this branch: bzr merge lp:~mvo/aptdaemon/support-for-whitelisted-repositories
Reviewer Review Type Date Requested Status
Michael Vogt Needs Resubmitting
Martin Pitt (community) Approve
Review via email: mp+121200@code.launchpad.net

Description of the change

This branch implements the whitelist feature discussed in #1035207

It adds a new type of policykit priv "org.debian.apt.install-packages-from-whitelisted-repo"
that can be overriden by to allow passwordless installs. If its not
overriden it has the same requirements as install-remove-packages

The whitelist is defined via /etc/aptdaemon/repository-whitelist.cfg

[webapps test]
origin = Ubuntu
component = universe
pkgnames = 2vcard

[more whitelist]
origin = Ubuntu
component = main
pkgnames = ^unity.*

To post a comment you must log in.
861. By Michael Vogt

aptdaemon/worker.py: more paranoia

Revision history for this message
Martin Pitt (pitti) wrote :

As a general nitpick, I'd prefer to rename "whitelisted" in any public API/properties etc. to "passwordless" or something similarly meaningful, to point out what this list actually does. This especially applies to the conffile, as renaming it later is painful.

The test case does not actually verify that the expected PK privilege is being used, as it only goes until the simulation stage (or did I get that wrong?). You could tell the mock polkitd to permit org.debian.apt.install-packages-from-whitelisted-repo and nothing else, and verify that installation of silly-* works, but other-pkg fails due to a permission error. It would also be nice to then try to remove the package again and verify that for this case (i. e. any but "install" it requires the "full" privilege.

I also think read_repository_whitelist() should become more robust against errors in the conffile. ConfigParser defaults to "strict" and thus excepts out if there a duplicate keys, etc. We don't want aptdaemon to crash on a ConfigParser.* exception; I think it should just be logged and it should return an empty set then. (Also easy to testcase).

Otherwise this looks good to me. Thanks!

review: Needs Fixing
862. By Michael Vogt

make config parser more robust against errors in the config file and add tests

863. By Michael Vogt

small cleanup/comment updates

864. By Michael Vogt

move the whitelsit releated tests into a single file

865. By Michael Vogt

test that the right polkit action is used for whitelisted packages

866. By Michael Vogt

test that non-whitelisted package fails to install with aptdaemon.policykit1.PK_ACTION_INSTALL_PACKAGES_FROM_WHITELISTED_REPO

867. By Michael Vogt

aptdaemon/test.py: make the debug in start_session_aptd option to have less noise

868. By Michael Vogt

documation updates and ensure that the is install-from-whitelist only applies to install

Revision history for this message
Michael Vogt (mvo) wrote :

On Mon, Aug 27, 2012 at 01:55:22PM -0000, Martin Pitt wrote:
> Review: Needs Fixing

Thanks for the review.

> As a general nitpick, I'd prefer to rename "whitelisted" in any public API/properties etc. to "passwordless" or something similarly meaningful, to point out what this list actually does. This especially applies to the conffile, as renaming it later is painful.

Right. I tend to agree but its not neseccarily passwordless, i.e. the
default implementation of this has auth_admin. Its just easier to
override this via e.g. the policykit-desktop-privileges or a special
webapps packages that does the same. Maybe a better name would be
"potentially_passordless" instead of "whitelisted"? But that gets a
bit long :/ Any hints appreciated.

> The test case does not actually verify that the expected PK privilege is being used, as it only goes until the simulation stage (or did I get that wrong?). You could tell the mock polkitd to permit org.debian.apt.install-packages-from-whitelisted-repo and nothing else, and verify that installation of silly-* works, but other-pkg fails due to a permission error. It would also be nice to then try to remove the package again and verify that for this case (i. e. any but "install" it requires the "full" privilege.

Indeed good points, I added tests for that now too and also
consolidated the whitelist test stuff into a single file.

> I also think read_repository_whitelist() should become more robust against errors in the conffile. ConfigParser defaults to "strict" and thus excepts out if there a duplicate keys, etc. We don't want aptdaemon to crash on a ConfigParser.* exception; I think it should just be logged and it should return an empty set then. (Also easy to testcase).

Thanks, good point. This is more robust now I added tests and logging
(and test for the logging).

This should be ready for re-review, except that we need to find a
better name for whitelisted.

Thanks,
 Michael

Revision history for this message
Martin Pitt (pitti) wrote :

The code/tests look good to me now, many thanks for the changes!

Some other ideas about the privilege name:

 - org.debian.apt.install-packages.relaxed-auth: connotes the "how"
 - org.debian.apt.install-packages.lightweight, org.debian.apt.install-packages.high-trust: connotes the "what for"

Perhaps you can also extend the comment to say what kind of packages this privilege is meant to be used for, i. e. webapps wrappers.

review: Approve
869. By Michael Vogt

rename read_repository_whitelist to read_high_trust_repository_whitelist

870. By Michael Vogt

rename transaction_only_installs_packages_from_whitelisted_repos to transaction_only_installs_packages_from_high_trust_repos

871. By Michael Vogt

rename self._whitelisted_packages to self._high_trust_whitelisted_packages

872. By Michael Vogt

rename PK_ACTION_INSTALL_PACKAGES_FROM_WHITELISTED_REPO to PK_ACTION_INSTALL_PACKAGES_FROM_HIGH_TRUST_WHITELISTED_REPO

873. By Michael Vogt

rename /etc/aptdaemon/repository-whitelist.cfg to /etc/aptdaemon/high-trust-repository-whitelist.cfg

874. By Michael Vogt

read the high-trust repository whitelist from /etc/aptdaemon/high-trust-repository-whitelist.d by default and make it a .d directory to ensure packages like e.g. webapps can drop there whitelist in

Revision history for this message
Michael Vogt (mvo) wrote :

Thanks Martin! I like the "high-trust" one best, the current code calls it:
 "org.debian.apt.install-packages.high-trust-whitelisted-repo"

I also tried to make the naming clearer and improve the examples. A quick
double check would be very cool

review: Needs Resubmitting
Revision history for this message
Martin Pitt (pitti) wrote :

There are still a few places where this hasn't been renamed consistently:

+ def _get_whitelisted_packages(self):

+class WhiteListedRepositoryIntegrationTestCase(BaseWhitelistTestCase):
+ """ Test the whitelist feature inside the chroot """

and a few more.

Do we really need both "high trust" and "whitelisted" in the name? But anyway, that's bikeshedding at some point, at least it more self-explaining now.

Thanks!

875. By Michael Vogt

rename AptWorker._whitelisted_repositories to _high_trust_repositories

Revision history for this message
Michael Vogt (mvo) wrote :

On Fri, Aug 31, 2012 at 08:38:20AM -0000, Martin Pitt wrote:
> There are still a few places where this hasn't been renamed consistently:
>
> + def _get_whitelisted_packages(self):
>
> +class WhiteListedRepositoryIntegrationTestCase(BaseWhitelistTestCase):
> + """ Test the whitelist feature inside the chroot """
>
> and a few more.

Thanks, I look over the diff once more and eliminiate the remaining
ones.

> Do we really need both "high trust" and "whitelisted" in the name? But anyway, that's bikeshedding at some point, at least it more self-explaining now.

I guess you are right about this, its a bit too much, so I will get
rid of more of the "whitelisted" strings.

876. By Michael Vogt

-PK_ACTION_INSTALL_PACKAGES_FROM_HIGH_TRUST_WHITELISTED_REPO -> +PK_ACTION_INSTALL_PACKAGES_FROM_HIGH_TRUST_REPO

877. By Michael Vogt

trans.whitelisted_packages, -> trans.high_trust_packages

878. By Michael Vogt

more renaming

879. By Michael Vogt

data/org.debian.apt.policy.in: fix name to match org.debian.apt.install-packages.high-trust-repo

880. By Michael Vogt

some more renames

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'aptdaemon/core.py'
--- aptdaemon/core.py 2012-06-06 13:53:40 +0000
+++ aptdaemon/core.py 2012-09-03 09:53:19 +0000
@@ -63,7 +63,11 @@
63from defer import inline_callbacks, return_value, Deferred63from defer import inline_callbacks, return_value, Deferred
64from defer.utils import dbus_deferred_method64from defer.utils import dbus_deferred_method
65from . import policykit165from . import policykit1
66from .worker import AptWorker, DummyWorker66from .worker import (
67 AptWorker,
68 DummyWorker,
69 transaction_only_installs_packages_from_high_trust_repos,
70 )
67from .loop import mainloop71from .loop import mainloop
6872
69# Setup i18n73# Setup i18n
@@ -407,6 +411,8 @@
407 for pkgs in packages],411 for pkgs in packages],
408 signature="asasasasasas")412 signature="asasasasasas")
409 self._unauthenticated = dbus.Array([], signature=dbus.Signature('s'))413 self._unauthenticated = dbus.Array([], signature=dbus.Signature('s'))
414 self._high_trust_packages = dbus.Array(
415 [], signature=dbus.Signature('s'))
410 # Add a timeout which removes the transaction from the bus if it416 # Add a timeout which removes the transaction from the bus if it
411 # hasn't been setup and run for the TRANSACTION_IDLE_TIMEOUT period417 # hasn't been setup and run for the TRANSACTION_IDLE_TIMEOUT period
412 self._idle_watch = GObject.timeout_add_seconds(418 self._idle_watch = GObject.timeout_add_seconds(
@@ -607,6 +613,24 @@
607 doc="Unauthenticated packages in this "613 doc="Unauthenticated packages in this "
608 "transaction")614 "transaction")
609615
616 # package that can have a different auth schema, useful for e.g.
617 # lightweight packages like unity-webapps or packages comming from
618 # a high trust repository (e.g. a internal company repo)
619 def _get_high_trust_packages(self):
620 return self._high_trust_packages
621
622 def _set_high_trust_packages(self, whitelisted_packages):
623 self._high_trust_packages = dbus.Array(
624 whitelisted_packages, signature="s")
625 self.PropertyChanged(
626 "HighTrustWhitelistedPackages",
627 self._high_trust_packages)
628
629 high_trust_packages = property(
630 _get_high_trust_packages,
631 _set_high_trust_packages,
632 doc="High trust packages in this transaction")
633
610 def _get_depends(self):634 def _get_depends(self):
611 return self._depends635 return self._depends
612636
@@ -911,6 +935,12 @@
911 action = self.ROLE_ACTION_MAP[self.role]935 action = self.ROLE_ACTION_MAP[self.role]
912 if action is None:936 if action is None:
913 raise StopIteration937 raise StopIteration
938 # Special case if InstallPackages only touches stuff from the
939 # high trust whitelist
940 if (self.role in (enums.ROLE_INSTALL_PACKAGES,
941 enums.ROLE_COMMIT_PACKAGES) and
942 transaction_only_installs_packages_from_high_trust_repos(self)):
943 action = policykit1.PK_ACTION_INSTALL_PACKAGES_FROM_HIGH_TRUST_REPO
914 # Special case if CommitPackages only upgrades944 # Special case if CommitPackages only upgrades
915 if self.role == enums.ROLE_COMMIT_PACKAGES and \945 if self.role == enums.ROLE_COMMIT_PACKAGES and \
916 not self.packages[enums.PKGS_INSTALL] and \946 not self.packages[enums.PKGS_INSTALL] and \
917947
=== modified file 'aptdaemon/policykit1.py'
--- aptdaemon/policykit1.py 2012-05-08 18:50:57 +0000
+++ aptdaemon/policykit1.py 2012-09-03 09:53:19 +0000
@@ -28,7 +28,8 @@
28 "PK_ACTION_GET_TRUSTED_VENDOR_KEYS",28 "PK_ACTION_GET_TRUSTED_VENDOR_KEYS",
29 "PK_ACTION_INSTALL_FILE",29 "PK_ACTION_INSTALL_FILE",
30 "PK_ACTION_INSTALL_OR_REMOVE_PACKAGES",30 "PK_ACTION_INSTALL_OR_REMOVE_PACKAGES",
31 "PK_ACTION_INSTALL_PACKAGES_FROM_NRE_REPO",31 "PK_ACTION_INSTALL_PACKAGES_FROM_NEW_REPO",
32 "PK_ACTION_INSTALL_PACKAGES_FROM_HIGH_TRUST_REPO",
32 "PK_ACTION_INSTALL_PURCHASED_PACKAGES",33 "PK_ACTION_INSTALL_PURCHASED_PACKAGES",
33 "PK_ACTION_UPDATE_CACHE", "PK_ACTION_UPGRADE_PACKAGES",34 "PK_ACTION_UPDATE_CACHE", "PK_ACTION_UPGRADE_PACKAGES",
34 "PK_ACTION_SET_PROXY", "PK_ACTION_CLEAN")35 "PK_ACTION_SET_PROXY", "PK_ACTION_CLEAN")
@@ -44,6 +45,8 @@
44 "org.debian.apt.install-purchased-packages"45 "org.debian.apt.install-purchased-packages"
45PK_ACTION_INSTALL_PACKAGES_FROM_NEW_REPO = \46PK_ACTION_INSTALL_PACKAGES_FROM_NEW_REPO = \
46 "org.debian.apt.install-packages-from-new-repo"47 "org.debian.apt.install-packages-from-new-repo"
48PK_ACTION_INSTALL_PACKAGES_FROM_HIGH_TRUST_REPO = \
49 "org.debian.apt.install-packages.high-trust-repo"
47PK_ACTION_INSTALL_FILE = "org.debian.apt.install-file"50PK_ACTION_INSTALL_FILE = "org.debian.apt.install-file"
48PK_ACTION_UPGRADE_PACKAGES = "org.debian.apt.upgrade-packages"51PK_ACTION_UPGRADE_PACKAGES = "org.debian.apt.upgrade-packages"
49PK_ACTION_UPDATE_CACHE = "org.debian.apt.update-cache"52PK_ACTION_UPDATE_CACHE = "org.debian.apt.update-cache"
5053
=== modified file 'aptdaemon/test.py'
--- aptdaemon/test.py 2012-06-13 07:57:12 +0000
+++ aptdaemon/test.py 2012-09-03 09:53:19 +0000
@@ -45,6 +45,14 @@
45PY3K = sys.version_info.major > 245PY3K = sys.version_info.major > 2
4646
4747
48class MockQueue(object):
49
50 """A fake TransactionQueue which only provides a limbo attribute."""
51
52 def __init__(self):
53 self.limbo = {}
54
55
48class Chroot(object):56class Chroot(object):
4957
50 """Provides a chroot which can be used by APT."""58 """Provides a chroot which can be used by APT."""
@@ -195,7 +203,7 @@
195 self.addCleanup(self._kill_process, proc)203 self.addCleanup(self._kill_process, proc)
196 return proc204 return proc
197205
198 def start_session_aptd(self, chroot="/"):206 def start_session_aptd(self, chroot="/", debug=False):
199 """Start an aptdaemon which listens on the session D-Bus.207 """Start an aptdaemon which listens on the session D-Bus.
200208
201 :param chroot: path to the chroot209 :param chroot: path to the chroot
@@ -210,10 +218,12 @@
210 path = "/usr/sbin/aptd"218 path = "/usr/sbin/aptd"
211 else:219 else:
212 path = os.path.join(dir, "../aptd")220 path = os.path.join(dir, "../aptd")
213 proc = subprocess.Popen(["python3", path, "--debug",221 cmd = ["python3", path,
214 "--disable-timeout", "--disable-plugins",222 "--disable-timeout", "--disable-plugins",
215 "--chroot", chroot],223 "--chroot", chroot]
216 env=env)224 if debug:
225 cmd.append("--debug")
226 proc = subprocess.Popen(cmd, env=env)
217 self.addCleanup(self._kill_process, proc)227 self.addCleanup(self._kill_process, proc)
218 return proc228 return proc
219229
220230
=== modified file 'aptdaemon/utils.py'
--- aptdaemon/utils.py 2012-05-09 00:49:57 +0000
+++ aptdaemon/utils.py 2012-09-03 09:53:19 +0000
@@ -29,6 +29,7 @@
29import warnings29import warnings
30from xml.etree import ElementTree30from xml.etree import ElementTree
3131
32
32def deprecated(func):33def deprecated(func):
33 """This is a decorator which can be used to mark functions34 """This is a decorator which can be used to mark functions
34 as deprecated. It will result in a warning being emitted35 as deprecated. It will result in a warning being emitted
3536
=== modified file 'aptdaemon/worker.py'
--- aptdaemon/worker.py 2012-06-13 19:31:39 +0000
+++ aptdaemon/worker.py 2012-09-03 09:53:19 +0000
@@ -22,6 +22,7 @@
22__all__ = ("AptWorker", "DummyWorker")22__all__ = ("AptWorker", "DummyWorker")
2323
24import contextlib24import contextlib
25import glob
25import logging26import logging
26import os27import os
27import platform28import platform
@@ -32,9 +33,14 @@
32import time33import time
33import traceback34import traceback
34try:35try:
35 from urllib.parse import urlsplit, urlunsplit36 from urllib.parse import urlsplit, urlunsplit
36except ImportError:37except ImportError:
37 from urlparse import urlsplit, urlunsplit38 from urlparse import urlsplit, urlunsplit
39
40try:
41 from configparser import ConfigParser
42except ImportError:
43 from ConfigParser import ConfigParser
3844
39import apt45import apt
40import apt.auth46import apt.auth
@@ -52,15 +58,17 @@
52from .errors import *58from .errors import *
53from . import lock59from . import lock
54from .loop import mainloop60from .loop import mainloop
55from .progress import DaemonOpenProgress, \61from .progress import (
56 DaemonInstallProgress, \62 DaemonOpenProgress,
57 DaemonAcquireProgress, \63 DaemonInstallProgress,
58 DaemonAcquireRepoProgress, \64 DaemonAcquireProgress,
59 DaemonDpkgInstallProgress, \65 DaemonAcquireRepoProgress,
60 DaemonDpkgReconfigureProgress, \66 DaemonDpkgInstallProgress,
61 DaemonDpkgRecoverProgress, \67 DaemonDpkgReconfigureProgress,
62 DaemonLintianProgress, \68 DaemonDpkgRecoverProgress,
63 DaemonForkProgress69 DaemonLintianProgress,
70 DaemonForkProgress,
71 )
6472
65log = logging.getLogger("AptDaemon.Worker")73log = logging.getLogger("AptDaemon.Worker")
6674
@@ -69,6 +77,62 @@
69_ = lambda s: s77_ = lambda s: s
7078
7179
80def transaction_only_installs_packages_from_high_trust_repos(trans,
81 whitelist=set()):
82 """ Return True if this transaction only touches packages in the
83 aptdaemon repoisotry high trust repository whitelist
84 """
85 # the transaction *must* be simulated before
86 if not trans.simulated:
87 return False
88 # we never allow unauthenticated ones
89 if trans.unauthenticated:
90 return False
91 # paranoia: wrong role
92 if not trans.role in (ROLE_INSTALL_PACKAGES, ROLE_COMMIT_PACKAGES):
93 return False
94 # if there is anything touched that is not a install bail out
95 for enum in (PKGS_REINSTALL, PKGS_REMOVE, PKGS_PURGE, PKGS_DOWNGRADE,
96 PKGS_UPGRADE):
97 if trans.packages[enum]:
98 return False
99 # paranoia(2): we must want to install something
100 if not trans.packages[PKGS_INSTALL]:
101 return False
102 # if the install packages matches the whitelisted set we are good
103 return set(trans.packages[PKGS_INSTALL]) == set(trans.high_trust_packages)
104
105
106def read_high_trust_repository_dir(whitelist_cfg_d):
107 """Return a set of (origin, component, pkgname-regexp) from a
108 high-trust-repository-whitelist.d directory
109 """
110 whitelist = set()
111 for path in glob.glob(os.path.join(whitelist_cfg_d, "*.cfg")):
112 whitelist |= _read_high_trust_repository_whitelist_file(path)
113 return whitelist
114
115
116def _read_high_trust_repository_whitelist_file(path):
117 """Read a individual high-trust-repository whitelist file and return
118 a set of tuples (origin, component, pkgname-regexp)
119 """
120 parser = ConfigParser()
121 whitelist = set()
122 try:
123 parser.read(path)
124 except Exception as e:
125 log.error("Failed to read repository whitelist '%s' (%s)" % (
126 path, e))
127 return whitelist
128 for section in parser.sections():
129 origin = parser.get(section, "origin")
130 component = parser.get(section, "component")
131 pkgnames = parser.get(section, "pkgnames")
132 whitelist.add( (origin, component, pkgnames) )
133 return whitelist
134
135
72class AptWorker(GObject.GObject):136class AptWorker(GObject.GObject):
73137
74 """Worker which processes transactions from the queue."""138 """Worker which processes transactions from the queue."""
@@ -92,7 +156,7 @@
92 # Store the the tid of the transaction whose changes are currently156 # Store the the tid of the transaction whose changes are currently
93 # marked in the cache157 # marked in the cache
94 self.marked_tid = None158 self.marked_tid = None
95159
96 # Change to a given chroot160 # Change to a given chroot
97 if chroot:161 if chroot:
98 apt_conf_file = os.path.join(chroot, "etc/apt/apt.conf")162 apt_conf_file = os.path.join(chroot, "etc/apt/apt.conf")
@@ -118,6 +182,16 @@
118 lock.lists_lock.path = os.path.join(lists_dir, "lock")182 lock.lists_lock.path = os.path.join(lists_dir, "lock")
119 apt_pkg.init_system()183 apt_pkg.init_system()
120184
185 # a set of tuples of the type (origin, component, pkgname-regexp)
186 # that on install will trigger a different kind of polkit
187 # authentication request (see LP: #1035207), useful for e.g.
188 # webapps/company repos
189 self._high_trust_repositories = read_high_trust_repository_dir(
190 os.path.join(apt_pkg.config.find_dir("Dir"),
191 "etc/aptdaemon/high-trust-repository-whitelist.d"))
192 log.debug(
193 "using high-trust whitelist: '%s'" % self._high_trust_repositories)
194
121 self._status_orig = apt_pkg.config.find_file("Dir::State::status")195 self._status_orig = apt_pkg.config.find_file("Dir::State::status")
122 self._status_frozen = None196 self._status_frozen = None
123 self.plugins = {}197 self.plugins = {}
@@ -347,6 +421,25 @@
347 version = release = None421 version = release = None
348 return name, version, release422 return name, version, release
349423
424 def _get_high_trust_packages(self):
425 """ Return a list of packages that come from a high-trust repo """
426 def _in_high_trust_repository(pkgname, pkgorigin):
427 for origin, component, regexp in self._high_trust_repositories:
428 if (origin == pkgorigin.origin and
429 component == pkgorigin.component and
430 re.match(regexp, pkgname)):
431 return True
432 return False
433 # loop
434 from_high_trust_repo = []
435 for pkg in self._iterate_packages():
436 if pkg.marked_install:
437 for origin in pkg.candidate.origins:
438 if _in_high_trust_repository(pkg.name, origin):
439 from_high_trust_repo.append(pkg.name)
440 break
441 return from_high_trust_repo
442
350 def _get_unauthenticated(self):443 def _get_unauthenticated(self):
351 """Return a list of unauthenticated package names """444 """Return a list of unauthenticated package names """
352 unauthenticated = []445 unauthenticated = []
@@ -1041,7 +1134,7 @@
1041 def _simulate(self, trans):1134 def _simulate(self, trans):
1042 try:1135 try:
1043 trans.depends, trans.download, trans.space, \1136 trans.depends, trans.download, trans.space, \
1044 trans.unauthenticated = self.__simulate(trans)1137 trans.unauthenticated, trans.high_trust_packages = self.__simulate(trans)
1045 except TransactionFailed as excep:1138 except TransactionFailed as excep:
1046 trans.error = excep1139 trans.error = excep
1047 trans.exit = EXIT_FAILED1140 trans.exit = EXIT_FAILED
@@ -1068,6 +1161,7 @@
1068 def __simulate(self, trans):1161 def __simulate(self, trans):
1069 depends = [[], [], [], [], [], [], []]1162 depends = [[], [], [], [], [], [], []]
1070 unauthenticated = []1163 unauthenticated = []
1164 high_trust_packages = []
1071 skip_pkgs = []1165 skip_pkgs = []
1072 size = 01166 size = 0
1073 installs = reinstalls = removals = purges = upgrades = upgradables = \1167 installs = reinstalls = removals = purges = upgrades = upgradables = \
@@ -1079,7 +1173,7 @@
1079 ROLE_UPGRADE_SYSTEM, ROLE_REMOVE_PACKAGES,1173 ROLE_UPGRADE_SYSTEM, ROLE_REMOVE_PACKAGES,
1080 ROLE_COMMIT_PACKAGES, ROLE_INSTALL_FILE,1174 ROLE_COMMIT_PACKAGES, ROLE_INSTALL_FILE,
1081 ROLE_FIX_BROKEN_DEPENDS]:1175 ROLE_FIX_BROKEN_DEPENDS]:
1082 return depends, 0, 0, []1176 return depends, 0, 0, [], []
10831177
1084 # If a transaction is currently running use the former status file1178 # If a transaction is currently running use the former status file
1085 if self._status_frozen:1179 if self._status_frozen:
@@ -1157,6 +1251,7 @@
1157 changes_names.append(pkg.name)1251 changes_names.append(pkg.name)
1158 # get the unauthenticated packages1252 # get the unauthenticated packages
1159 unauthenticated = self._get_unauthenticated()1253 unauthenticated = self._get_unauthenticated()
1254 high_trust_packages = self._get_high_trust_packages()
1160 # Check for skipped upgrades1255 # Check for skipped upgrades
1161 for pkg in upgradables:1256 for pkg in upgradables:
1162 if pkg.marked_keep:1257 if pkg.marked_keep:
@@ -1174,7 +1269,8 @@
11741269
1175 required_space = size + self._cache.required_space1270 required_space = size + self._cache.required_space
11761271
1177 return depends, required_download, required_space, unauthenticated1272 return depends, required_download, required_space, unauthenticated, \
1273 high_trust_packages
11781274
1179 def _check_deb_file(self, trans, path, force):1275 def _check_deb_file(self, trans, path, force):
1180 """Perform some basic checks for the Debian package.1276 """Perform some basic checks for the Debian package.
11811277
=== modified file 'data/org.debian.apt.policy.in'
--- data/org.debian.apt.policy.in 2011-03-17 11:07:09 +0000
+++ data/org.debian.apt.policy.in 2012-09-03 09:53:19 +0000
@@ -106,6 +106,30 @@
106 </defaults>106 </defaults>
107 </action>107 </action>
108108
109 <action id="org.debian.apt.install-packages.high-trust-repo">
110 <!-- This priviledge will be requested when installing a package
111 from a high trusted repository that can be explicitely whitelisted.
112
113 The defaults for this action are the same as
114 "org.debian.apt.install-or-remove-packages".
115
116 The admin can override them to e.g. allow passwordless installs for
117 leightweight applications like unity-webapps or for packages
118 comming from high trust repositories (like internal repositories)
119 -->
120 <_description>
121 Install software from a high-trust whitelisted repository.
122 </_description>
123 <_message>
124 To install software, you need to authenticate.
125 </_message>
126 <defaults>
127 <allow_any>auth_admin</allow_any>
128 <allow_inactive>auth_admin</allow_inactive>
129 <allow_active>auth_admin_keep</allow_active>
130 </defaults>
131 </action>
132
109 <action id="org.debian.apt.install-packages-from-new-repo">133 <action id="org.debian.apt.install-packages-from-new-repo">
110 <!-- This privilege allows to call AddRepository, UpdateCache(Partially)134 <!-- This privilege allows to call AddRepository, UpdateCache(Partially)
111 and InstallPackages in a row and only authenticating once.135 and InstallPackages in a row and only authenticating once.
112136
=== added directory 'tests/data'
=== added file 'tests/data/high-trust-repository-whitelist-broken.cfg'
--- tests/data/high-trust-repository-whitelist-broken.cfg 1970-01-01 00:00:00 +0000
+++ tests/data/high-trust-repository-whitelist-broken.cfg 2012-09-03 09:53:19 +0000
@@ -0,0 +1,10 @@
1
2[repo name1]
3origin = Ubuntu
4component = main
5pkgnames = foo.*
6
7[repo name1]
8origin = Debian-Security
9component = non-free
10pkgnames = ^bar$
0\ No newline at end of file11\ No newline at end of file
112
=== added file 'tests/data/high-trust-repository-whitelist.cfg'
--- tests/data/high-trust-repository-whitelist.cfg 1970-01-01 00:00:00 +0000
+++ tests/data/high-trust-repository-whitelist.cfg 2012-09-03 09:53:19 +0000
@@ -0,0 +1,10 @@
1
2[repo name1]
3origin = Ubuntu
4component = main
5pkgnames = foo.*
6
7[repo whitelist2]
8origin = Debian-Security
9component = non-free
10pkgnames = ^bar$
0\ No newline at end of file11\ No newline at end of file
112
=== added directory 'tests/data/high-trust-repository-whitelist.d'
=== added symlink 'tests/data/high-trust-repository-whitelist.d/high-trust-repository-whitelist-broken.cfg'
=== target is u'../high-trust-repository-whitelist-broken.cfg'
=== added symlink 'tests/data/high-trust-repository-whitelist.d/high-trust-repository-whitelist.cfg'
=== target is u'../high-trust-repository-whitelist.cfg'
=== added directory 'tests/repo/whitelisted'
=== added file 'tests/repo/whitelisted/Packages'
--- tests/repo/whitelisted/Packages 1970-01-01 00:00:00 +0000
+++ tests/repo/whitelisted/Packages 2012-09-03 09:53:19 +0000
@@ -0,0 +1,34 @@
1Package: silly-base
2Priority: extra
3Section: admin
4Installed-Size: 44
5Maintainer: Sebastian Heinlein <devel@glatzor.de>
6Architecture: all
7Source: silly-packages (0.1-0)
8Version: 0.1-0update1
9Filename: ./silly-base_0.1-0update1_all.deb
10Size: 1934
11MD5sum: 8e20af56a63a1e0cf40db3b0d07e7989
12SHA1: 7ce87423d9c7a734478c464021994944d07fbf1b
13SHA256: d3693c0e3e7a9519b2833fdf1301c7e03e0620edf15b95b4c7329d9eb0bee553
14SHA512: e9eded74e2449a98b02828539c55a83de85a762d2361cd8c929292eb9c5a6e5a9b8eb9b64c26c45d7b73280e12a280cd799a9b831126e484bcf55b56456d559f
15Description: working package
16 Silly packages is a set of packages which will break your package
17 management tool. They are created only for debugging purposes.
18 .
19 This package doesn't contain any files and should always be installable.
20
21Package: other-pkg
22Priority: extra
23Section: admin
24Installed-Size: 44
25Maintainer: Sebastian Heinlein <devel@glatzor.de>
26Architecture: all
27Version: 2.0
28Filename: ./other-pkg_2.0_all.deb
29Size: 1934
30MD5sum: 8e20af56a63a1e0cf40db3b0d07e7989
31SHA1: 7ce87423d9c7a734478c464021994944d07fbf1b
32SHA256: d3693c0e3e7a9519b2833fdf1301c7e03e0620edf15b95b4c7329d9eb0bee553
33SHA512: e9eded74e2449a98b02828539c55a83de85a762d2361cd8c929292eb9c5a6e5a9b8eb9b64c26c45d7b73280e12a280cd799a9b831126e484bcf55b56456d559f
34Description: another working package
035
=== added file 'tests/repo/whitelisted/Packages.gpg'
--- tests/repo/whitelisted/Packages.gpg 1970-01-01 00:00:00 +0000
+++ tests/repo/whitelisted/Packages.gpg 2012-09-03 09:53:19 +0000
@@ -0,0 +1,34 @@
1-----BEGIN PGP SIGNED MESSAGE-----
2Hash: SHA256
3
4Package: silly-base
5Priority: extra
6Section: admin
7Installed-Size: 44
8Maintainer: Sebastian Heinlein <devel@glatzor.de>
9Architecture: all
10Source: silly-packages (0.1-0)
11Version: 0.1-0update1
12Filename: ./silly-base_0.1-0update1_all.deb
13Size: 1934
14MD5sum: 8e20af56a63a1e0cf40db3b0d07e7989
15SHA1: 7ce87423d9c7a734478c464021994944d07fbf1b
16SHA256: d3693c0e3e7a9519b2833fdf1301c7e03e0620edf15b95b4c7329d9eb0bee553
17SHA512: e9eded74e2449a98b02828539c55a83de85a762d2361cd8c929292eb9c5a6e5a9b8eb9b64c26c45d7b73280e12a280cd799a9b831126e484bcf55b56456d559f
18Description: working package
19 Silly packages is a set of packages which will break your package
20 management tool. They are created only for debugging purposes.
21 .
22 This package doesn't contain any files and should always be installable.
23
24-----BEGIN PGP SIGNATURE-----
25Version: GnuPG v1.4.11 (GNU/Linux)
26
27iQEcBAEBCAAGBQJO3ZylAAoJEGg8U8fPmC0YooQH/inTQPInDCiN3pTDvzWfV16F
28Ea+UpwBN0vzuyS6f3xmhLRxLQpuz/yk8buc1H+f/XKn6eygydJRFwIEgtdWAN/Tk
29eG9I4c5zYiHzZnNWe4XNBhRdPVkIHPkbmbRs/RvDiM5Cq0LvXIe0X0RV+empJyrC
30EgKbt3PJxh8qpMfrf/OIF+GkSqAug4tq0i0n6QxLOi0raeb9PjfDwErmBpbLDSFg
31XyDnNvPET5BtWxjgupOwoFqs2QRkrLv10JBdGRz+7qG6WhH1BCAOfzYxxCtn++Ip
32kmwo8c/pmtOr1BzZyyNMWP8nvVtB728eb/M84WGYynIEyCObUQ+Q2HVsXcsPQLc=
33=j3Nx
34-----END PGP SIGNATURE-----
035
=== added file 'tests/repo/whitelisted/Release'
--- tests/repo/whitelisted/Release 1970-01-01 00:00:00 +0000
+++ tests/repo/whitelisted/Release 2012-09-03 09:53:19 +0000
@@ -0,0 +1,17 @@
1Origin: Ubuntu
2Label: Ubuntu-Whitelisted
3Components: main
4Suite: sid
5Date: Tue, 06 Dec 2011 04:40:53 UTC
6MD5Sum:
7 0bd018b62f0031d1ee4993896f50ddd5 782 Packages
8 d41d8cd98f00b204e9800998ecf8427e 0 Release
9SHA1:
10 42201c0ab580cb2d557ca22a7ed04717a68b2e6e 782 Packages
11 da39a3ee5e6b4b0d3255bfef95601890afd80709 0 Release
12SHA256:
13 950233659ae7e86ac11ae886298963e57f4414691881af1357456c6a4038ef91 782 Packages
14 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 Release
15SHA512:
16 984c9203b8e534b9b9cc486b21da94cf807428dc1d2daa56427a870f4d53111c58ce749c35509a5fcf7112433d7a403830378a0f482354fb382c0aa7a25bdbc2 782 Packages
17 cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e 0 Release
018
=== added file 'tests/repo/whitelisted/Release.gpg'
=== added symlink 'tests/repo/whitelisted/silly-base_0.1-0update1_all.deb'
=== target is u'../silly-base_0.1-0update1_all.deb'
=== modified file 'tests/test_client.py'
--- tests/test_client.py 2012-05-09 22:47:34 +0000
+++ tests/test_client.py 2012-09-03 09:53:19 +0000
@@ -111,6 +111,7 @@
111 aptdaemon.enums.ERROR_DEP_RESOLUTION_FAILED)111 aptdaemon.enums.ERROR_DEP_RESOLUTION_FAILED)
112112
113113
114
114if __name__ == "__main__":115if __name__ == "__main__":
115 if DEBUG:116 if DEBUG:
116 logging.basicConfig(level=logging.DEBUG)117 logging.basicConfig(level=logging.DEBUG)
117118
=== added file 'tests/test_high_trust_repository_whitelist.py'
--- tests/test_high_trust_repository_whitelist.py 1970-01-01 00:00:00 +0000
+++ tests/test_high_trust_repository_whitelist.py 2012-09-03 09:53:19 +0000
@@ -0,0 +1,201 @@
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3"""Provides unit tests for the APTDAEMON high-trust-repo feature."""
4# Copyright (C) 2011 Sebastian Heinlein <devel@glatzor.de>
5#
6# Licensed under the GNU General Public License Version 2
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software
20# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21# Licensed under the GNU General Public License Version 2
22
23__author__ = "Michael Vogt <michael.vogt@ubuntu.com>"
24
25import os
26import sys
27import time
28import unittest
29
30import dbus
31from gi.repository import GObject
32from mock import (
33 patch,
34 )
35
36import aptdaemon.client
37import aptdaemon.policykit1
38import aptdaemon.test
39
40from aptdaemon.worker import (
41 _read_high_trust_repository_whitelist_file,
42 read_high_trust_repository_dir,
43 transaction_only_installs_packages_from_high_trust_repos,
44 AptWorker,
45 )
46from aptdaemon.core import Transaction
47from aptdaemon import enums
48
49
50REPO_PATH = os.path.join(aptdaemon.test.get_tests_dir(), "repo")
51
52PY3K = sys.version_info.major > 2
53
54
55class BaseHighTrustTestCase(aptdaemon.test.AptDaemonTestCase):
56
57 def setUp(self):
58 self.chroot = aptdaemon.test.Chroot()
59 self.chroot.setup()
60 self.addCleanup(self.chroot.remove)
61 self.loop = GObject.MainLoop()
62
63
64class HighTrustRepositoryTestCase(BaseHighTrustTestCase):
65 """ Test the worker low-level bits of the high-trust repo implementation"""
66
67
68 def setUp(self):
69 super(HighTrustRepositoryTestCase, self).setUp()
70 self.queue = aptdaemon.test.MockQueue()
71 self.worker = AptWorker(chroot=self.chroot.path, load_plugins=False)
72 self.worker.connect("transaction-done", lambda w,t: self.loop.quit())
73 self.worker.connect("transaction-simulated",
74 lambda w,t: self.loop.quit())
75
76 def test_read_high_trust_repository_whitelist_dir(self):
77 whitelist = read_high_trust_repository_dir(
78 os.path.join(aptdaemon.test.get_tests_dir(),
79 "data/high-trust-repository-whitelist.d"))
80 self.assertEqual(
81 whitelist, set( [
82 ("Ubuntu", "main", "foo.*"),
83 ("Debian-Security", "non-free", "^bar$"),
84 ]))
85
86 def test_read_high_trust_repository_whitelist(self):
87 whitelist = _read_high_trust_repository_whitelist_file(
88 os.path.join(aptdaemon.test.get_tests_dir(),
89 "data/high-trust-repository-whitelist.cfg"))
90 self.assertEqual(
91 whitelist, set( [
92 ("Ubuntu", "main", "foo.*"),
93 ("Debian-Security", "non-free", "^bar$"),
94 ]))
95
96 @patch("aptdaemon.worker.log")
97 def test_read_high_trust_repository_whitelist_broken(self, mock_log):
98 whitelist = _read_high_trust_repository_whitelist_file(
99 os.path.join(aptdaemon.test.get_tests_dir(),
100 "data/high-trust-repository-whitelist-broken.cfg"))
101 self.assertEqual(whitelist, set())
102 # ensure we log a error if the config file is broken
103 mock_log.error.assert_called()
104
105 @patch("aptdaemon.worker.log")
106 def test_read_high_trust_repository_whitelist_not_there(self, mock_log):
107 whitelist = _read_high_trust_repository_whitelist_file(
108 "lalalala-not-there-really.cfg")
109 self.assertEqual(whitelist, set())
110 # ensure we log no error if there is no config file
111 self.assertFalse(mock_log.called)
112
113 def test_high_trust_repository(self):
114 """Test if using a high_trust repo is working """
115 self.chroot.add_repository(os.path.join(aptdaemon.test.get_tests_dir(),
116 "repo/whitelisted"))
117 # setup a whitelist
118 self.worker._high_trust_repositories.add(
119 ("Ubuntu", "", "silly.*"))
120 # a high-trust whitelisted pkg and a non-whitelisted one
121 trans = Transaction(None, enums.ROLE_INSTALL_PACKAGES, self.queue,
122 os.getpid(), os.getuid(), sys.argv[0],
123 "org.debian.apt.test", connect=False,
124 packages=[["silly-base", "other-pkg"],[],[],[],[], []])
125 self.worker.simulate(trans)
126 self.loop.run()
127 self.assertEqual(trans.high_trust_packages, ["silly-base"])
128 self.assertFalse(
129 transaction_only_installs_packages_from_high_trust_repos(
130 trans, self.worker._high_trust_repositories))
131 # whitelisted only
132 trans = Transaction(None, enums.ROLE_INSTALL_PACKAGES, self.queue,
133 os.getpid(), os.getuid(), sys.argv[0],
134 "org.debian.apt.test", connect=False,
135 packages=[["silly-base"],[],[],[],[], []])
136 self.worker.simulate(trans)
137 self.loop.run()
138 self.assertTrue(
139 transaction_only_installs_packages_from_high_trust_repos(
140 trans, self.worker._high_trust_repositories))
141
142
143class HighTrustRepositoryIntegrationTestCase(BaseHighTrustTestCase):
144 """ Test the whitelist feature inside the chroot """
145
146 def setUp(self):
147 super(HighTrustRepositoryIntegrationTestCase, self).setUp()
148 # Start aptdaemon with the chroot on the session bus
149 self.start_dbus_daemon()
150 self.bus = dbus.bus.BusConnection(self.dbus_address)
151 # setup the environment first including the high-trust whitelist
152 self.chroot.add_repository(os.path.join(aptdaemon.test.get_tests_dir(),
153 "repo/whitelisted"))
154 whitelist_file = os.path.join(
155 self.chroot.path, "etc", "aptdaemon",
156 "high-trust-repository-whitelist.d", "test.cfg")
157 os.makedirs(os.path.dirname(whitelist_file))
158
159 with open(whitelist_file, "w") as f:
160 f.write("""
161[test repo"]
162origin = Ubuntu
163component =
164pkgnames = silly.*
165""")
166 # *after* that start the aptdaemon
167 self.start_session_aptd(self.chroot.path)
168 # start policykit and *only* allow from-whitelisted repo pk action
169 self.start_fake_polkitd(
170 aptdaemon.policykit1.PK_ACTION_INSTALL_PACKAGES_FROM_HIGH_TRUST_REPO)
171 time.sleep(1)
172 self.client = aptdaemon.client.AptClient(self.bus)
173
174 def test_high_trust_polkit_ok(self):
175 # test that the high trust whitelist works
176 trans = self.client.install_packages(["silly-base"])
177 trans.simulate()
178 trans.connect("finished", lambda a,b: self.loop.quit())
179 trans.run()
180 self.loop.run()
181 self.assertEqual(trans.exit, aptdaemon.enums.EXIT_SUCCESS)
182 # plus ensure removal will not work
183 trans = self.client.remove_packages(["silly-base"])
184 with self.assertRaises(aptdaemon.errors.NotAuthorizedError):
185 trans.run()
186
187 def test_high_trust_polkit_not_ok(self):
188 # ensure that non-whitelisted packages can not be installed
189 trans = self.client.install_packages(["other-pkg"])
190 trans.simulate()
191 trans.connect("finished", lambda a,b: self.loop.quit())
192 with self.assertRaises(aptdaemon.errors.NotAuthorizedError):
193 trans.run()
194
195
196if __name__ == "__main__":
197 #import logging
198 #logging.basicConfig(level=logging.DEBUG)
199 unittest.main()
200
201# vim: ts=4 et sts=4
0202
=== modified file 'tests/test_worker.py'
--- tests/test_worker.py 2012-08-10 13:59:10 +0000
+++ tests/test_worker.py 2012-09-03 09:53:19 +0000
@@ -31,26 +31,25 @@
3131
32import apt_pkg32import apt_pkg
33from gi.repository import GObject33from gi.repository import GObject
34import dbus34from mock import (
35 Mock,
36 patch,
37 )
3538
36import aptdaemon.test39import aptdaemon.test
37from aptdaemon.worker import AptWorker40from aptdaemon.worker import (
41 transaction_only_installs_packages_from_high_trust_repos,
42 AptWorker,
43 )
38from aptdaemon.core import Transaction44from aptdaemon.core import Transaction
39from aptdaemon import enums, errors45from aptdaemon import enums, errors
4046
47
41REPO_PATH = os.path.join(aptdaemon.test.get_tests_dir(), "repo")48REPO_PATH = os.path.join(aptdaemon.test.get_tests_dir(), "repo")
4249
43PY3K = sys.version_info.major > 250PY3K = sys.version_info.major > 2
4451
4552
46class MockQueue(object):
47
48 """A fake TransactionQueue which only provides a limbo attribute."""
49
50 def __init__(self):
51 self.limbo = {}
52
53
54class WorkerTestCase(aptdaemon.test.AptDaemonTestCase):53class WorkerTestCase(aptdaemon.test.AptDaemonTestCase):
5554
56 """Test suite for the worker which performs the actual package55 """Test suite for the worker which performs the actual package
@@ -61,7 +60,7 @@
61 self.chroot.setup()60 self.chroot.setup()
62 self.addCleanup(self.chroot.remove)61 self.addCleanup(self.chroot.remove)
63 self.loop = GObject.MainLoop()62 self.loop = GObject.MainLoop()
64 self.queue = MockQueue()63 self.queue = aptdaemon.test.MockQueue()
65 self.worker = AptWorker(chroot=self.chroot.path, load_plugins=False)64 self.worker = AptWorker(chroot=self.chroot.path, load_plugins=False)
66 self.worker.connect("transaction-done", lambda w,t: self.loop.quit())65 self.worker.connect("transaction-done", lambda w,t: self.loop.quit())
67 self.worker.connect("transaction-simulated",66 self.worker.connect("transaction-simulated",
@@ -438,8 +437,6 @@
438 """Test if credentials of repositories are store securely in a437 """Test if credentials of repositories are store securely in a
439 separate file.438 separate file.
440 """439 """
441 from mock import Mock
442
443 source_file_name = "private_source.list"440 source_file_name = "private_source.list"
444 self.worker.add_repository(Mock(), "deb",441 self.worker.add_repository(Mock(), "deb",
445 "https://user:pass@host.example.com/path",442 "https://user:pass@host.example.com/path",
@@ -469,6 +466,8 @@
469466
470467
471if __name__ == "__main__":468if __name__ == "__main__":
469 #import logging
470 #logging.basicConfig(level=logging.DEBUG)
472 unittest.main()471 unittest.main()
473472
474# vim: ts=4 et sts=4473# vim: ts=4 et sts=4

Subscribers

People subscribed via source and target branches

to status/vote changes: