Merge lp:~mvo/aptdaemon/add-license-key-call-from-server into lp:aptdaemon

Proposed by Michael Vogt
Status: Merged
Merged at revision: 703
Proposed branch: lp:~mvo/aptdaemon/add-license-key-call-from-server
Merge into: lp:aptdaemon
Diff against target: 993 lines (+614/-113)
14 files modified
NEWS (+10/-0)
aptdaemon/client.py (+27/-0)
aptdaemon/core.py (+34/-2)
aptdaemon/enums.py (+17/-0)
aptdaemon/piston/scaclient.py (+55/-0)
aptdaemon/worker.py (+114/-0)
helper/ubuntu-license-key-helper (+68/-0)
setup.py (+6/-2)
tests/repo/Packages (+137/-96)
tests/repo/Release (+4/-4)
tests/repo/Release.gpg (+7/-7)
tests/test_client.py (+40/-1)
tests/test_helper.py (+60/-0)
tests/test_worker.py (+35/-1)
To merge this branch: bzr merge lp:~mvo/aptdaemon/add-license-key-call-from-server
Reviewer Review Type Date Requested Status
Aptdaemon Developers Pending
Review via email: mp+81860@code.launchpad.net

Description of the change

This branch adds system wide license keys. The keys are obtained via a helper utility directly from the server.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2011-10-08 16:23:14 +0000
3+++ NEWS 2011-11-10 14:28:46 +0000
4@@ -9,6 +9,7 @@
5 - Freeze (copy) the dpkg status file during a dpkg call to allow
6 simulating other transactions with the frozen dpkg status
7
8+ - Allow to install global license keys, see below
9 - Add compatibilty for py-gobject 3.0 to the client module by switching
10 to gobject from introspection repository
11
12@@ -30,6 +31,15 @@
13 - aptdaemon.test.Chroot now has got new methods to handle faked cdrom
14 repositories: add_cdrom_repository(), mount_cdrom() and unmount_cdrom()
15
16+ - org.debian.apt.transaction D-Bus interface: The Download and Space
17+ properties are now of type Int64 (signed 64-bit integer)
18+
19+ - org.debian.apt D-Bus interface: Add new AddLicenseKey method which
20+ allows to place a license key file at the loaction specified in the
21+ record/control field LicenseKeyPath of the corresponding package.
22+
23+ - aptdaemon.client: New AptClient.add_license_key method.
24+
25 - org.debian.apt D-Bus interface: Add the RebootRequired property. Should
26 be quite self-explanatory
27
28
29=== modified file 'aptdaemon/client.py'
30--- aptdaemon/client.py 2011-08-17 11:23:20 +0000
31+++ aptdaemon/client.py 2011-11-10 14:28:46 +0000
32@@ -1536,6 +1536,33 @@
33 return self._run_transaction("Clean", [], wait, reply_handler,
34 error_handler)
35
36+ @deferable
37+ @convert_dbus_exception
38+ def add_license_key(self, pkg_name, json_token, server_name, wait=False,
39+ reply_handler=None, error_handler=None):
40+ """Install a license key to use a piece of proprietary software.
41+
42+ :param pkg_name: The package which requires the license
43+ :param json_token: The oauth token in json format
44+ :param server_name: The server name (ubuntu-procduction, ubuntu-staging)
45+
46+ :param wait: if True run the transaction immediately and return its exit
47+ state instead of the transaction itself.
48+ :param reply_handler: Callback function. If specified in combination
49+ with error_handler the method will be called asynchrounsouly.
50+ :param error_handler: Errback function. In case of an error the given
51+ callback gets the corresponding exception instance.
52+ :param defer: Run the method asynchrounsly and return a defer.Deferred.
53+ This options is only available as an keyword.
54+
55+ :raises: dbus.DBusException
56+
57+ :returns: An AptTransaction instance.
58+ """
59+ return self._run_transaction("AddLicenseKey",
60+ [pkg_name, json_token, server_name],
61+ wait, reply_handler,
62+ error_handler)
63
64 def _run_transaction(self, method_name, args, wait, reply_handler,
65 error_handler):
66
67=== modified file 'aptdaemon/core.py'
68--- aptdaemon/core.py 2011-09-15 14:23:50 +0000
69+++ aptdaemon/core.py 2011-11-10 14:28:46 +0000
70@@ -296,7 +296,10 @@
71 enums.ROLE_CLEAN: \
72 policykit1.PK_ACTION_CLEAN,
73 enums.ROLE_ENABLE_DISTRO_COMP: \
74- policykit1.PK_ACTION_CHANGE_REPOSITORY}
75+ policykit1.PK_ACTION_CHANGE_REPOSITORY,
76+ enums.ROLE_ADD_LICENSE_KEY: \
77+ policykit1.PK_ACTION_INSTALL_OR_REMOVE_PACKAGES,
78+ }
79
80 WRITABLE_PROPERTIES = ("HttpProxy", "Terminal", "AllowUnauthenticated",
81 "DebconfSocket", "MetaData", "Locale",
82@@ -875,7 +878,8 @@
83 if self.role not in [enums.ROLE_ADD_REPOSITORY,
84 enums.ROLE_ADD_VENDOR_KEY_FROM_KEYSERVER,
85 enums.ROLE_UPDATE_CACHE,
86- enums.ROLE_INSTALL_PACKAGES]:
87+ enums.ROLE_INSTALL_PACKAGES,
88+ enums.ROLE_ADD_LICENSE_KEY]:
89 return_value(False)
90 flags = policykit1.CHECK_AUTH_NONE
91 for action in [policykit1.PK_ACTION_INSTALL_PACKAGES_FROM_NEW_REPO,
92@@ -1856,6 +1860,34 @@
93 mainloop.quit()
94 log.debug("Exit")
95
96+ # pylint: disable-msg=C0103,C0322
97+ @dbus_deferred_method(APTDAEMON_DBUS_INTERFACE,
98+ in_signature="sss", out_signature="s",
99+ sender_keyword="sender")
100+ def AddLicenseKey(self, pkg_name, json_token, server_name, sender):
101+ """Install a license key to use a piece of proprietary software.
102+
103+ Requires the ``org.debian.apt.install-or-remove-packages``
104+ :ref:`PolicyKit privilege <policykit>`.
105+
106+ :param pkg_name: The name of the package which requires the license
107+ :type pkg_name: s
108+ :param json_token: The oauth token to use with the server in json format
109+ :type pkg_name: s
110+ :param pkg_name: The name of the server to use
111+ (ubuntu-production, ubuntu-staging)
112+ :type pkg_name: s
113+
114+ :returns: The D-Bus path of the new transaction object which
115+ performs this action.
116+ """
117+ log.info("AddLicenseKey() was called")
118+ return self._create_trans(enums.ROLE_ADD_LICENSE_KEY, sender,
119+ kwargs={'pkg_name': pkg_name,
120+ 'json_token' : json_token,
121+ 'server_name' : server_name,
122+ })
123+
124 @inline_callbacks
125 def _set_property(self, iface, name, value, sender):
126 """Helper to set a property on the properties D-Bus interface."""
127
128=== modified file 'aptdaemon/enums.py'
129--- aptdaemon/enums.py 2011-10-24 12:30:38 +0000
130+++ aptdaemon/enums.py 2011-11-10 14:28:46 +0000
131@@ -35,6 +35,9 @@
132 "ERROR_PACKAGE_MANAGER_FAILED", "ERROR_CACHE_BROKEN",
133 "ERROR_PACKAGE_UNAUTHENTICATED", "ERROR_INCOMPLETE_INSTALL",
134 "ERROR_UNREADABLE_PACKAGE_FILE", "ERROR_INVALID_PACKAGE_FILE",
135+ "ERROR_LICENSE_KEY_PATH_INVALID", "ERROR_LICENSE_KEY_PATH_UNSECURE",
136+ "ERROR_LICENSE_KEY_ALREADY_EXISTS", "ERROR_LICENSE_KEY_INVALID",
137+ "ERROR_LICENSE_KEY_SERVER",
138 "ERROR_UNKNOWN",
139 "STATUS_SETTING_UP", "STATUS_WAITING", "STATUS_WAITING_MEDIUM",
140 "STATUS_WAITING_CONFIG_FILE_PROMPT", "STATUS_WAITING_LOCK",
141@@ -48,6 +51,7 @@
142 "ROLE_REMOVE_VENDOR_KEY", "ROLE_FIX_INCOMPLETE_INSTALL",
143 "ROLE_FIX_BROKEN_DEPENDS", "ROLE_ADD_REPOSITORY",
144 "ROLE_ENABLE_DISTRO_COMP", "ROLE_CLEAN", "ROLE_RECONFIGURE",
145+ "ROLE_ADD_LICENSE_KEY",
146 "DOWNLOAD_DONE", "DOWNLOAD_AUTH_ERROR", "DOWNLOAD_ERROR",
147 "DOWNLOAD_FETCHING", "DOWNLOAD_IDLE", "DOWNLOAD_NETWORK_ERROR",
148 "PKG_INSTALLING", "PKG_CONFIGURING", "PKG_REMOVING",
149@@ -143,6 +147,16 @@
150 ERROR_UNREADABLE_PACKAGE_FILE = "error-unreadable-package-file"
151 #: The package file violates the Debian/Ubuntu policy
152 ERROR_INVALID_PACKAGE_FILE = "error-invalid-package-file"
153+#: The license key path is not provided from a trusted source
154+ERROR_LICENSE_KEY_PATH_INVALID = "error-license-key-path-invalid"
155+#: The license key path is not provided from a trusted source
156+ERROR_LICENSE_KEY_PATH_UNSECURE = "error-license-key-path-unsecure"
157+#: The license key can not be retrivied from the server
158+ERROR_LICENSE_KEY_SERVER = "error-license-key-server"
159+#: The license key is already installed
160+ERROR_LICENSE_KEY_ALREADY_EXISTS = "error-license-key-already-exists"
161+#: The license key is invalid
162+ERROR_LICENSE_KEY_INVALID = "error-license-key-invalid"
163 #: An unknown error occured. In most cases these are programming ones.
164 ERROR_UNKNOWN = "error-unknown"
165
166@@ -253,6 +267,8 @@
167 ROLE_RECONFIGURE = "role-reconfigure"
168 #: The transaction will remove all downloaded package files.
169 ROLE_CLEAN = "role-clean"
170+#: The transaction will add a license key to the system
171+ROLE_ADD_LICENSE_KEY = "role-add-license-key"
172
173 # DOWNLOAD STATES
174 #: The download has been completed.
175@@ -434,6 +450,7 @@
176 ROLE_ENABLE_DISTRO_COMP: _("Enabling component of the distribution failed"),
177 ROLE_CLEAN: _("Removing downloaded package files failed"),
178 ROLE_RECONFIGURE: _("Removing downloaded package files failed"),
179+ ROLE_ADD_LICENSE_KEY: _("Adding license key"),
180 ROLE_UNSET: ""
181 }
182
183
184=== added directory 'aptdaemon/piston'
185=== added file 'aptdaemon/piston/__init__.py'
186=== added file 'aptdaemon/piston/scaclient.py'
187--- aptdaemon/piston/scaclient.py 1970-01-01 00:00:00 +0000
188+++ aptdaemon/piston/scaclient.py 2011-11-10 14:28:46 +0000
189@@ -0,0 +1,55 @@
190+from piston_mini_client import (PistonAPI, PistonResponseObject,
191+ returns_list_of, returns_json)
192+from piston_mini_client.validators import (validate_pattern, validate,
193+ oauth_protected)
194+
195+# These are factored out as constants for if you need to work against a
196+# server that doesn't support both schemes (like http-only dev servers)
197+PUBLIC_API_SCHEME = 'http'
198+AUTHENTICATED_API_SCHEME = 'https'
199+
200+class SoftwareCenterAgentAPI(PistonAPI):
201+ default_service_root = 'http://localhost:8000/api/2.0'
202+
203+ @validate_pattern('lang', r'[^/]{1,9}$')
204+ @validate_pattern('series', r'[^/]{1,20}$')
205+ @validate_pattern('arch', r'[^/]{1,10}$')
206+ @returns_list_of(PistonResponseObject)
207+ def available_apps(self, lang, series, arch):
208+ """Retrieve the list of currently available apps for purchase."""
209+ return self._get(
210+ 'applications/%s/ubuntu/%s/%s/' % (lang, series, arch),
211+ scheme=PUBLIC_API_SCHEME)
212+
213+ @validate_pattern('lang', r'[^/]{1,9}$')
214+ @validate_pattern('series', r'[^/]{1,20}$')
215+ @validate_pattern('arch', r'[^/]{1,10}$')
216+ @oauth_protected
217+ @returns_list_of(PistonResponseObject)
218+ def available_apps_qa(self, lang, series, arch):
219+ """Retrieve the list of currently available apps for purchase."""
220+ return self._get(
221+ 'applications_qa/%s/ubuntu/%s/%s/' % (lang, series, arch),
222+ scheme=AUTHENTICATED_API_SCHEME)
223+
224+ @oauth_protected
225+ @validate('complete_only', bool, required=False)
226+ @returns_list_of(PistonResponseObject)
227+ def subscriptions_for_me(self, complete_only=False):
228+ return self._get('subscriptions/',
229+ args={'complete_only': complete_only},
230+ scheme=AUTHENTICATED_API_SCHEME)
231+
232+ @oauth_protected
233+ @validate('id', int)
234+ @returns_json
235+ def subscription_by_id(self, id=None):
236+ return self._get('subscription/%d' % (id),
237+ scheme=AUTHENTICATED_API_SCHEME)
238+
239+ @validate_pattern('lang', r'[^/]{1,9}$')
240+ @returns_list_of(PistonResponseObject)
241+ def exhibits(self, lang):
242+ """Retrieve the list of currently published exhibits."""
243+ return self._get(
244+ 'exhibits/%s/' % (lang))
245
246=== modified file 'aptdaemon/worker.py'
247--- aptdaemon/worker.py 2011-09-18 06:31:09 +0000
248+++ aptdaemon/worker.py 2011-11-10 14:28:46 +0000
249@@ -25,6 +25,7 @@
250 import logging
251 import os
252 import re
253+import json
254 import shutil
255 import sys
256 import tempfile
257@@ -61,6 +62,7 @@
258 # core.Transaction.gettext
259 _ = lambda s: s
260
261+
262 class AptWorker(gobject.GObject):
263
264 """Worker which processes transactions from the queue."""
265@@ -69,6 +71,9 @@
266 gobject.TYPE_NONE,
267 (gobject.TYPE_PYOBJECT,))}
268
269+ # the basedir under which license keys can be stored
270+ LICENSE_KEY_ROOTDIR = "/opt/"
271+
272 def __init__(self, chroot=None):
273 """Initialize a new AptWorker instance."""
274 gobject.GObject.__init__(self)
275@@ -204,6 +209,8 @@
276 elif trans.role == ROLE_UPDATE_CACHE:
277 self.update_cache(trans, **trans.kwargs)
278 # Process the transactions which require a consistent cache
279+ elif trans.role == ROLE_ADD_LICENSE_KEY:
280+ self.add_license_key(trans, **trans.kwargs)
281 elif self._cache and self._cache.broken_count:
282 broken = [pkg.name for pkg in self._cache if pkg.is_now_broken]
283 raise TransactionFailed(ERROR_CACHE_BROKEN,
284@@ -1168,6 +1175,113 @@
285 log.debug("Removing file %s", path)
286 os.remove(path)
287
288+ def add_license_key(self, trans, pkg_name, json_token, server_name):
289+ """Add a license key data to the given package.
290+
291+ Keyword arguemnts:
292+ trans -- the coresponding transaction
293+ pkg_name -- the name of the corresponding package
294+ json_token -- the oauth token as json
295+ server_name -- the server to use (ubuntu-production, ubuntu-staging)
296+ """
297+ # set transaction state to downloading
298+ trans.status = STATUS_DOWNLOADING
299+ rootdir = apt_pkg.config["Dir"]
300+ license_key_helper = os.path.join(
301+ rootdir, "usr/share/aptdaemon/helper/ubuntu-license-key-helper")
302+ cmd = [license_key_helper,
303+ "--server", server_name,
304+ "--pkgname", pkg_name,
305+ ]
306+ proc = subprocess.Popen(cmd,
307+ stdin=subprocess.PIPE,
308+ stderr=subprocess.PIPE,
309+ stdout=subprocess.PIPE,
310+ preexec_fn=lambda: os.setuid(trans.uid),
311+ close_fds=True)
312+ # send json token to the process
313+ proc.stdin.write(json_token + "\n")
314+ # wait until it finishes
315+ while proc.poll() is None:
316+ while gobject.main_context_default().pending():
317+ gobject.main_context_default().iteration()
318+ time.sleep(0.05)
319+
320+ if proc.returncode != 0:
321+ stderr = unicode(proc.stderr.read(),
322+ sys.stdin.encoding or "UTF-8",
323+ errors="replace")
324+ raise TransactionFailed(ERROR_LICENSE_KEY_SERVER, stderr)
325+
326+ # get data from stdout
327+ license_key_path = proc.stdout.readline().strip()
328+ license_key = proc.stdout.read()
329+
330+ # ensure stuff is good
331+ if not license_key_path or not license_key:
332+ raise TransactionFailed(
333+ ERROR_LICENSE_KEY_SERVER,
334+ _("Recived no license key data from the server."))
335+
336+ # add license key if we have one
337+ self._add_license_key_to_system(pkg_name,
338+ license_key,
339+ license_key_path)
340+
341+
342+ def _add_license_key_to_system(self, pkg_name, license_key, license_key_path):
343+ # fixup path
344+ license_key_path = os.path.join(apt_pkg.config.find_dir("Dir"),
345+ license_key_path.lstrip("/"))
346+
347+ # Perform some very basic security checks
348+
349+ # Check content of the key
350+ if (license_key.strip().startswith("#!") or
351+ license_key.startswith("\x7fELF")):
352+ raise TransactionFailed(ERROR_LICENSE_KEY_INVALID,
353+ _("The license key is not allowed to "
354+ "contain executable code."))
355+ # Perform some very basic security checks
356+ license_key_path = os.path.normpath(license_key_path)
357+ license_key_path_rootdir = os.path.join(apt_pkg.config["Dir"],
358+ self.LICENSE_KEY_ROOTDIR.lstrip("/"),
359+ pkg_name)
360+ if not license_key_path.startswith(license_key_path_rootdir):
361+ raise TransactionFailed(ERROR_LICENSE_KEY_PATH_INVALID,
362+ _("The license key path %s is invalid"),
363+ license_key_path)
364+ if os.path.lexists(license_key_path):
365+ raise TransactionFaild(ERROR_LICENSE_KEY_ALREADY_INSTALLED,
366+ _("The license key already exists: %s"),
367+ license_key_path)
368+ # Symlink attacks!
369+ if os.path.realpath(license_key_path) != license_key_path:
370+ raise TransactionFailed(ERROR_LICENSE_KEY_PATH_INVALID,
371+ _("The location of the license key is "
372+ "unsecure since it contains symbolic "
373+ "links. The path %s maps to %s"),
374+ license_key_path,
375+ os.path.realpath(licese_key_path))
376+ # Check if the directory already exists
377+ if not os.path.isdir(os.path.dirname(license_key_path)):
378+ raise TransactionFailed(ERROR_LICENSE_KEY_PATH_INVALID,
379+ _("The directory where to install the key "
380+ "to doesn't exist yet: %s"),
381+ license_key_path)
382+ # write it
383+ log.info("Writing license key to '%s'" % license_key_path)
384+ old_umask = os.umask(18)
385+ try:
386+ with open(license_key_path, "w") as license_file:
387+ license_file.write(license_key)
388+ except IOError:
389+ raise TransactionFailed(ERROR_LICENSE_KEY_PATH_INVALID,
390+ _("Failed to write key file to: %s"),
391+ license_key_path)
392+ finally:
393+ os.umask(old_umask)
394+
395 def _get_broken_details(self, trans, now=True):
396 """Return a message which provides debugging information about
397 broken packages.
398
399=== added directory 'helper'
400=== added file 'helper/ubuntu-license-key-helper'
401--- helper/ubuntu-license-key-helper 1970-01-01 00:00:00 +0000
402+++ helper/ubuntu-license-key-helper 2011-11-10 14:28:46 +0000
403@@ -0,0 +1,68 @@
404+#!/usr/bin/python
405+
406+import json
407+from optparse import OptionParser
408+import sys
409+
410+from aptdaemon.piston.scaclient import SoftwareCenterAgentAPI
411+import piston_mini_client.auth
412+
413+import gettext
414+gettext.install("aptdaemon")
415+
416+if __name__ == "__main__":
417+
418+ parser = OptionParser()
419+ parser.add_option("-s", "--server", default="ubuntu-production")
420+ parser.add_option("-p", "--pkgname")
421+ (options, args) = parser.parse_args()
422+
423+ # server to use
424+ if options.server == "ubuntu-production":
425+ server = "http://software-center.ubuntu.com/api/2.0"
426+ elif options.server == "ubuntu-staging":
427+ server = "https://sc.staging.ubuntu.com/api/2.0"
428+ else:
429+ raise Exception("Unknown license key server")
430+ SoftwareCenterAgentAPI.default_service_root = server
431+
432+ # pkgname
433+ pkgname = options.pkgname
434+
435+ # wait for oauth token on stdin
436+ json_token = sys.stdin.readline()
437+ token = json.loads(json_token)
438+
439+ # get the data
440+ auth = piston_mini_client.auth.OAuthAuthorizer(token["token_key"],
441+ token["token_secret"],
442+ token["consumer_key"],
443+ token["consumer_secret"])
444+
445+ api = SoftwareCenterAgentAPI(auth=auth)
446+
447+
448+ try:
449+ subscriptions = api.subscriptions_for_me(complete_only=True)
450+ except piston_mini_client.APIError as e:
451+ sys.stderr.write(_("Failed to get the license key from "
452+ "the server."))
453+ sys.exit(1)
454+
455+ license_key = None
456+ license_key_path = None
457+ for subscription in subscriptions:
458+ if subscription.application["package_name"] == pkg_name:
459+ license_key = subscription.license_key
460+ license_key_path = subscription.license_key_path
461+
462+ if license_key and license_key_path:
463+ sys.stdout.write(license_key_path)
464+ sys.stdout.write(license_key)
465+ sys.exit(0)
466+
467+ # generic error
468+ sys.stderr.write(_("Failed to get the license key from "
469+ "the server."))
470+ sys.exit(1)
471+
472
473=== modified file 'setup.py'
474--- setup.py 2011-08-26 06:37:34 +0000
475+++ setup.py 2011-11-10 14:28:46 +0000
476@@ -20,7 +20,8 @@
477 long_description=readme,
478 author="Sebastian Heinlein",
479 author_email="devel@glatzor.de",
480- packages=["aptdaemon"],
481+ packages=["aptdaemon",
482+ "aptdaemon.piston"],
483 scripts=["aptd", "aptdcon"],
484 test_suite="unittest2.collector",
485 license="GNU LGPL",
486@@ -47,7 +48,10 @@
487 ["doc/aptd.1", "doc/aptdcon.1"]),
488 ("share/man/man7",
489 ["doc/org.debian.apt.7",
490- "doc/org.debian.apt.transaction.7"])],
491+ "doc/org.debian.apt.transaction.7"]),
492+ ("share/aptdaemon/helper",
493+ glob.glob("helper/*")),
494+ ],
495 platforms="posix",
496 use_2to3=sys.version_info[0] >= 3,
497 )
498
499=== modified file 'tests/repo/Packages'
500--- tests/repo/Packages 2011-01-10 07:37:44 +0000
501+++ tests/repo/Packages 2011-11-10 14:28:46 +0000
502@@ -1,61 +1,3 @@
503-Package: silly-fail
504-Priority: extra
505-Section: admin
506-Installed-Size: 36
507-Maintainer: Sebastian Heinlein <devel@glatzor.de>
508-Architecture: all
509-Source: silly-packages
510-Version: 0.1-0
511-Filename: ./silly-fail_0.1-0_all.deb
512-Size: 1874
513-MD5sum: 623a67e49bb7a1351a9bf1ae72fc6cfb
514-SHA1: 76417770f2b788b680ef18be66b5a6c4ec51bc62
515-SHA256: 45fea3eb64a427a288a90b12f42cc3a6abea8e8387efd4879b319db736890ebd
516-Description: installation fails
517- Silly packages is a set of packages which will break your package
518- management tool. They are created only for debugging purposes.
519- .
520- This installation of this package will always fail.
521-
522-Package: silly-depend-base
523-Priority: extra
524-Section: admin
525-Installed-Size: 32
526-Maintainer: Sebastian Heinlein <devel@glatzor.de>
527-Architecture: all
528-Source: silly-packages
529-Version: 0.1-0
530-Depends: silly-base
531-Filename: ./silly-depend-base_0.1-0_all.deb
532-Size: 1812
533-MD5sum: 3ee7181283e4926558d2afdf7b2657f1
534-SHA1: 8294794b1727e22b123866f9124431a2c7b79380
535-SHA256: 5fa74f7bdc0d207188790419130330becb0cf7484a972762ed512090885016f9
536-Description: package depending on silly-base
537- Silly packages is a set of packages which will break your package
538- management tool. They are created only for debugging purposes.
539- .
540- This package depends on silly-base.
541-
542-Package: silly-config
543-Priority: extra
544-Section: admin
545-Installed-Size: 44
546-Maintainer: Sebastian Heinlein <devel@glatzor.de>
547-Architecture: all
548-Source: silly-packages
549-Version: 0.1-0
550-Filename: ./silly-config_0.1-0_all.deb
551-Size: 1928
552-MD5sum: 565698b052640ed3d514f6b38ad457c7
553-SHA1: e7169cc58f10e8ff9481cf31b2ffb947bc99f617
554-SHA256: 255f025bae5b24a91158e7daa986f9bf59146ab1895f39452104eb97c412d190
555-Description: wants to update a locally changed config file
556- Silly packages is a set of packages which will break your package
557- management tool. They are created only for debugging purposes.
558- .
559- This package will install a configuration file.
560-
561 Package: silly-postinst-input
562 Priority: extra
563 Section: admin
564@@ -75,44 +17,6 @@
565 .
566 This package will wait for user input in the postinst script.
567
568-Package: silly-base
569-Priority: extra
570-Section: admin
571-Installed-Size: 44
572-Maintainer: Sebastian Heinlein <devel@glatzor.de>
573-Architecture: all
574-Source: silly-packages (0.1-0)
575-Version: 0.1-0update1
576-Filename: ./silly-base_0.1-0update1_all.deb
577-Size: 1934
578-MD5sum: 8e20af56a63a1e0cf40db3b0d07e7989
579-SHA1: 7ce87423d9c7a734478c464021994944d07fbf1b
580-SHA256: d3693c0e3e7a9519b2833fdf1301c7e03e0620edf15b95b4c7329d9eb0bee553
581-Description: working package
582- Silly packages is a set of packages which will break your package
583- management tool. They are created only for debugging purposes.
584- .
585- This package doesn't contain any files and should always be installable.
586-
587-Package: silly-base
588-Priority: extra
589-Section: admin
590-Installed-Size: 32
591-Maintainer: Sebastian Heinlein <devel@glatzor.de>
592-Architecture: all
593-Source: silly-packages
594-Version: 0.1-0
595-Filename: ./silly-base_0.1-0_all.deb
596-Size: 1824
597-MD5sum: 81ba1cf86142c466f35a36a11b5b7b52
598-SHA1: f949c8e5962a43fbcd4f7d67df0ebe741052c497
599-SHA256: a7c2a30abdb6488d5c3d2894db398f5dfafbc6be098da1135822be4e3ac94b32
600-Description: working package
601- Silly packages is a set of packages which will break your package
602- management tool. They are created only for debugging purposes.
603- .
604- This package doesn't contain any files and should always be installable.
605-
606 Package: gstreamer0.10-silly
607 Priority: extra
608 Section: admin
609@@ -133,6 +37,25 @@
610 .
611 This package is a GStreamer test plugin package.
612
613+Package: silly-base
614+Priority: extra
615+Section: admin
616+Installed-Size: 44
617+Maintainer: Sebastian Heinlein <devel@glatzor.de>
618+Architecture: all
619+Source: silly-packages (0.1-0)
620+Version: 0.1-0update1
621+Filename: ./silly-base_0.1-0update1_all.deb
622+Size: 1934
623+MD5sum: 8e20af56a63a1e0cf40db3b0d07e7989
624+SHA1: 7ce87423d9c7a734478c464021994944d07fbf1b
625+SHA256: d3693c0e3e7a9519b2833fdf1301c7e03e0620edf15b95b4c7329d9eb0bee553
626+Description: working package
627+ Silly packages is a set of packages which will break your package
628+ management tool. They are created only for debugging purposes.
629+ .
630+ This package doesn't contain any files and should always be installable.
631+
632 Package: silly-essential
633 Essential: yes
634 Priority: extra
635@@ -153,6 +76,25 @@
636 .
637 This package is an essential one.
638
639+Package: silly-base
640+Priority: extra
641+Section: admin
642+Installed-Size: 32
643+Maintainer: Sebastian Heinlein <devel@glatzor.de>
644+Architecture: all
645+Source: silly-packages
646+Version: 0.1-0
647+Filename: ./silly-base_0.1-0_all.deb
648+Size: 1824
649+MD5sum: 81ba1cf86142c466f35a36a11b5b7b52
650+SHA1: f949c8e5962a43fbcd4f7d67df0ebe741052c497
651+SHA256: a7c2a30abdb6488d5c3d2894db398f5dfafbc6be098da1135822be4e3ac94b32
652+Description: working package
653+ Silly packages is a set of packages which will break your package
654+ management tool. They are created only for debugging purposes.
655+ .
656+ This package doesn't contain any files and should always be installable.
657+
658 Package: silly-important
659 Priority: important
660 Section: admin
661@@ -172,6 +114,27 @@
662 .
663 This package is an essential one.
664
665+Package: silly-license
666+Essential: yes
667+Priority: extra
668+Section: admin
669+Installed-Size: 44
670+Maintainer: Sebastian Heinlein <devel@glatzor.de>
671+Architecture: all
672+Source: silly-packages
673+Version: 0.1-0
674+Filename: ./silly-license_0.1-0_all.deb
675+Size: 3884
676+MD5sum: 8498a97dd25e37760f26e215dc16668e
677+SHA1: b42846a26132fc771ec2bd8e7eb07e17041c501b
678+SHA256: 1eed24cbaa3e5dd54a7e5302b98ab8b1e97c1433f6e9bb1e14f641994249a323
679+Description: package with optional license key
680+ Silly packages is a set of packages which will break your package
681+ management tool. They are created only for debugging purposes.
682+ .
683+ This package provides allows to install an optional license key file.
684+Licensekeypath: /opt/silly-license/NASTY.KEY
685+
686 Package: silly-broken
687 Priority: extra
688 Section: admin
689@@ -192,3 +155,81 @@
690 .
691 This package cannot be installed because of a missing dependency.
692
693+Package: silly-config
694+Priority: extra
695+Section: admin
696+Installed-Size: 44
697+Maintainer: Sebastian Heinlein <devel@glatzor.de>
698+Architecture: all
699+Source: silly-packages
700+Version: 0.1-0
701+Filename: ./silly-config_0.1-0_all.deb
702+Size: 1928
703+MD5sum: 565698b052640ed3d514f6b38ad457c7
704+SHA1: e7169cc58f10e8ff9481cf31b2ffb947bc99f617
705+SHA256: 255f025bae5b24a91158e7daa986f9bf59146ab1895f39452104eb97c412d190
706+Description: wants to update a locally changed config file
707+ Silly packages is a set of packages which will break your package
708+ management tool. They are created only for debugging purposes.
709+ .
710+ This package will install a configuration file.
711+
712+Package: silly-depend-base-lintian-broken
713+Priority: extra
714+Section: admin
715+Installed-Size: 32
716+Architecture: all
717+Source: silly-packages
718+Version: 0.1-0
719+Depends: silly-base
720+Filename: ./silly-depend-base-lintian-broken_0.1-0_all.deb
721+Size: 1758
722+MD5sum: ba9e0b746b08fae8f8a8fbb4cf9ba41e
723+SHA1: 8bcaf6034b09383e461f0e366762e6b6683e69e2
724+SHA256: ff1302170d232c21ab66f332e38c51b2af9b6d06d8f377ae5944465be9467c22
725+Description: package depending on silly-base (but lintian broken)
726+ Silly packages is a set of packages which will break your package
727+ management tool. They are created only for debugging purposes.
728+ .
729+ This package depends on silly-base and has no "Maintainer" set
730+ and the file owner is totally wrong so lintian complains about it.
731+
732+Package: silly-fail
733+Priority: extra
734+Section: admin
735+Installed-Size: 36
736+Maintainer: Sebastian Heinlein <devel@glatzor.de>
737+Architecture: all
738+Source: silly-packages
739+Version: 0.1-0
740+Filename: ./silly-fail_0.1-0_all.deb
741+Size: 1874
742+MD5sum: 623a67e49bb7a1351a9bf1ae72fc6cfb
743+SHA1: 76417770f2b788b680ef18be66b5a6c4ec51bc62
744+SHA256: 45fea3eb64a427a288a90b12f42cc3a6abea8e8387efd4879b319db736890ebd
745+Description: installation fails
746+ Silly packages is a set of packages which will break your package
747+ management tool. They are created only for debugging purposes.
748+ .
749+ This installation of this package will always fail.
750+
751+Package: silly-depend-base
752+Priority: extra
753+Section: admin
754+Installed-Size: 32
755+Maintainer: Sebastian Heinlein <devel@glatzor.de>
756+Architecture: all
757+Source: silly-packages
758+Version: 0.1-0
759+Depends: silly-base
760+Filename: ./silly-depend-base_0.1-0_all.deb
761+Size: 1812
762+MD5sum: 3ee7181283e4926558d2afdf7b2657f1
763+SHA1: 8294794b1727e22b123866f9124431a2c7b79380
764+SHA256: 5fa74f7bdc0d207188790419130330becb0cf7484a972762ed512090885016f9
765+Description: package depending on silly-base
766+ Silly packages is a set of packages which will break your package
767+ management tool. They are created only for debugging purposes.
768+ .
769+ This package depends on silly-base.
770+
771
772=== modified file 'tests/repo/Release'
773--- tests/repo/Release 2011-01-10 07:37:44 +0000
774+++ tests/repo/Release 2011-11-10 14:28:46 +0000
775@@ -1,10 +1,10 @@
776-Date: Mo, 10 Jan 2011 07:25:04 UTC
777+Date: Di, 23 Aug 2011 11:53:15 UTC
778 MD5Sum:
779- 76890eae32625a32d270e667eadf7672 6466 Packages
780+ a4c8c6d48d8429c070d8b2a19c900428 7902 Packages
781 d41d8cd98f00b204e9800998ecf8427e 0 Release
782 SHA1:
783- ad2d57d7fb357429bdf72bd84a30a0ecaf427ccf 6466 Packages
784+ 5a3ddd070333e9954d92c20397883a5e58ca86e0 7902 Packages
785 da39a3ee5e6b4b0d3255bfef95601890afd80709 0 Release
786 SHA256:
787- 0a755d4bc1cf098032ba2b74764fcf8f5c22d0c0e5944a0d63032db55af036fa 6466 Packages
788+ b7d04a99c3907fa1b0521f2130935fc48597d56739134d2e5453635bf530c015 7902 Packages
789 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 Release
790
791=== modified file 'tests/repo/Release.gpg'
792--- tests/repo/Release.gpg 2011-01-10 07:37:44 +0000
793+++ tests/repo/Release.gpg 2011-11-10 14:28:46 +0000
794@@ -1,11 +1,11 @@
795 -----BEGIN PGP SIGNATURE-----
796 Version: GnuPG v1.4.10 (GNU/Linux)
797
798-iQEcBAABCAAGBQJNKrRcAAoJEGg8U8fPmC0Y0bgIAMJUX9WLV2KMJFtIts+6mI+J
799-stbq5GviUNPMVlp1eoVY4aGG+DHx0xtMBfxiBbW6QBrreAttrxXujwe1Jn85qcbn
800-Gzz6rSvK999KZN/XjZskKa01hcpA2qYuRLn0buJ2BWno9jBmaZqxbL7aKargh9LJ
801-dVDk+/Bw4inTN7I7iboQDGiUTGH88IB4kCO1Xu8hKExicoKvfSu6JtIaTCKhqbpm
802-F0PLq/tXwSaBAeHzHqvKXDi5YOFVmeePv6ACUq/MEi3wnvRt+LRIyaZW+TBIc3zW
803-yqyeQjTZ2DM9JoE+8F9e4M93KI+t1jwkcM2fWYmpar3ThoLLjdm+KpO5xTVtkvw=
804-=6oo7
805+iQEcBAABCAAGBQJOU5UdAAoJEGg8U8fPmC0Y8b0IAJJx/g2AXbf743kynB8hM87B
806+OzBEdOol3ekdCnryiwxCRiUS1eNCHl2WUMD2wbC7aTU34fmJk7cjAoCiBIEImJXS
807+EFN56YM4nhHoC1dUvOsaNHTMzkHpAuYO1Fjy0WiBELqgk4g/+ADZWiJa8zyFghpi
808+g48GFMLHBpfNL53MJFwBLjORCBNtrZLhUi0oXkzyRM81gi6k6ds09pyyUiJwdAI8
809+ovSPbFHzqSiuefRtxpQk9cyvuaUD40XLAwHBIW2mLOvXsYocRwhR7XCA2d9rJuvO
810+EXxb2ALypcwHDZEh2FtNZwbqK+7BNf4RPIAVFjzp7ORB3BLEqyDiQdmUKlEqqZ8=
811+=mZj1
812 -----END PGP SIGNATURE-----
813
814=== added file 'tests/repo/silly-license_0.1-0_all.deb'
815Binary files tests/repo/silly-license_0.1-0_all.deb 1970-01-01 00:00:00 +0000 and tests/repo/silly-license_0.1-0_all.deb 2011-11-10 14:28:46 +0000 differ
816=== modified file 'tests/test_client.py'
817--- tests/test_client.py 2011-09-15 14:23:50 +0000
818+++ tests/test_client.py 2011-11-10 14:28:46 +0000
819@@ -6,8 +6,10 @@
820 import time
821 import unittest2
822
823+import dbus
824 import defer
825-import dbus
826+import os
827+import json
828
829 import aptdaemon.client
830 import aptdaemon.loop
831@@ -77,6 +79,43 @@
832 exit = f(*args, wait=True)
833 self.assertEqual(exit, aptdaemon.enums.EXIT_SUCCESS)
834
835+ def test_add_license_key(self):
836+ os.makedirs(os.path.join(self.chroot.path, "opt/silly-pkg"))
837+ helper_dir = os.path.join(self.chroot.path, "usr/share/aptdaemon/helper/")
838+ os.makedirs(helper_dir)
839+ # data
840+ license_key = "Bli bla blub. I am a BLOB"
841+ license_key_path = "/opt/silly-pkg/NASTY.KEY"
842+ # write out mock helper
843+ helper = os.path.join(helper_dir, "ubuntu-license-key-helper")
844+ f=open(helper, "w")
845+ f.write("""#!/bin/sh
846+echo "%s"
847+echo -n "%s"
848+""" % (license_key_path, license_key))
849+ f.close()
850+ os.chmod(helper, 0755)
851+ # run test
852+ chroot_license_key_path = os.path.join(
853+ self.chroot.path, license_key_path[1:])
854+ client = aptdaemon.client.AptClient(self.bus)
855+ json_token = json.dumps({'token_key' : 'key',
856+ 'token_secret' : 'token_secret',
857+ 'consumer_key' : 'consumer_key',
858+ 'consumer_secret' : 'consumer_secret',
859+ 'subscription_id' : '99',
860+ 'api_version' : '2.0',
861+ })
862+ trans = client.add_license_key(
863+ "silly-pkg", json_token, "mock")
864+ trans.connect("finished", self._on_finished)
865+ trans.run()
866+ aptdaemon.loop.mainloop.run()
867+ self.assertEqual(trans.exit, aptdaemon.enums.EXIT_SUCCESS,
868+ trans.error)
869+ self.assertEqual(license_key, open(chroot_license_key_path, "r").read(),
870+ "Specified and stored license key don't match")
871+
872
873 if __name__ == "__main__":
874 if DEBUG:
875
876=== added file 'tests/test_helper.py'
877--- tests/test_helper.py 1970-01-01 00:00:00 +0000
878+++ tests/test_helper.py 2011-11-10 14:28:46 +0000
879@@ -0,0 +1,60 @@
880+#!/usr/bin/env python
881+# -*- coding: utf-8 -*-
882+"""Tests the helper scripts."""
883+
884+import logging
885+import time
886+import unittest2
887+
888+import dbus
889+import defer
890+import os
891+import json
892+import subprocess
893+
894+import aptdaemon.client
895+import aptdaemon.loop
896+import aptdaemon.enums
897+
898+import aptdaemon.test
899+
900+class HelperTest(aptdaemon.test.AptDaemonTestCase):
901+
902+ def setUp(self):
903+ basedir = os.path.dirname(__file__)
904+ self.basedir = os.path.abspath(os.path.join(basedir, ".."))
905+ self.helperdir = os.path.join(self.basedir, "helper")
906+
907+ def test_ubuntu_license_key_helper(self):
908+ license_key_helper = os.path.join(
909+ self.helperdir, "ubuntu-license-key-helper")
910+
911+ os.environ["PYTHONPATH"] = self.basedir
912+
913+ p = subprocess.Popen([license_key_helper,
914+ "--pkgname", "2vcard",
915+ "--server", "ubuntu-production"],
916+ stdin=subprocess.PIPE,
917+ stdout=subprocess.PIPE,
918+ stderr=subprocess.PIPE)
919+ token = {}
920+ token["token_key"] = "foo"
921+ token["token_secret"] = "bar"
922+ token["consumer_key"] = "baz"
923+ token["consumer_secret"] = "foobar"
924+ #print json.dumps(token)
925+ p.stdin.write(json.dumps(token) + "\n")
926+ p.wait()
927+ # it fails, that is expected
928+ self.assertEqual(p.returncode, 1)
929+ stderr = p.stderr.read()
930+ stdout = p.stdout.read()
931+ self.assertEqual(stdout, "")
932+ self.assertEqual(
933+ stderr, "Failed to get the license key from the server.")
934+
935+
936+if __name__ == "__main__":
937+ unittest2.main()
938+
939+# vim: ts=4 et sts=4
940
941=== modified file 'tests/test_worker.py'
942--- tests/test_worker.py 2011-09-18 06:29:49 +0000
943+++ tests/test_worker.py 2011-11-10 14:28:46 +0000
944@@ -30,7 +30,6 @@
945 import dbus
946 import gobject
947
948-
949 import aptdaemon.test
950 from aptdaemon.worker import AptWorker
951 from aptdaemon.core import Transaction
952@@ -312,6 +311,41 @@
953 self.worker._cache.open()
954 self.assertEqual(self.worker._cache.broken_count, 0)
955
956+ def test_add_license_key_unsecure(self):
957+ """Test if we refuse to install license key files to an unsecure
958+ location."""
959+ self.chroot.add_test_repository(copy_sig=False)
960+ # Should fail because of an untrusted source
961+ license_key = "NASTY_BLOB"
962+ license_key_path = "/opt/silly-license/NASTY.KEY"
963+ pkg_name = "silly-license"
964+ self.assertRaises(errors.TransactionFailed,
965+ self.worker._add_license_key_to_system,
966+ pkg_name, license_key, license_key_path)
967+
968+ def test_add_license_key(self):
969+ """Test the installation of license key files."""
970+ self.chroot.add_test_repository()
971+ # Check if we don't allow to install executables
972+ with open("/bin/ls", "r") as sample_exec:
973+ license_key = sample_exec.read()
974+ license_key_path = "/opt/silly-license/NASTY.KEY"
975+ pkg_name = "silly-license"
976+ self.assertRaises(errors.TransactionFailed,
977+ self.worker._add_license_key_to_system,
978+ pkg_name, license_key, license_key_path)
979+ # Should finally work
980+ os.makedirs(os.path.join(
981+ aptdaemon.worker.apt_pkg.config["Dir"], "opt/silly-license/"))
982+ license_key = "some key"
983+ license_key_path = "/opt/silly-license/NASTY.KEY"
984+ self.worker._add_license_key_to_system(pkg_name, license_key, license_key_path)
985+ # Check the content of the installed key
986+ license_key_path = os.path.join(aptdaemon.worker.apt_pkg.config["Dir"],
987+ "opt/silly-license/NASTY.KEY")
988+ self.assertEqual(license_key, open(license_key_path).read(),
989+ "Content of license key doesn't match")
990+
991
992 if __name__ == "__main__":
993 unittest2.main()

Subscribers

People subscribed via source and target branches

to status/vote changes: