From 2f4d6b60f5c5581a0e53ffa77b747485b1e64d7b Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Thu, 30 May 2013 14:45:06 -0400 Subject: initial debian packaging --- PKG-INFO | 20 + changes/bug_add-data-files | 1 - changes/bug_allow-absolute-paths | 1 - changes/bug_fix-deprecation-warning | 1 - ...ix-do-not-attempt-to-fetch-privkeys-from-server | 1 - changes/bug_fix-imports | 1 - changes/feature_events_signals | 1 - changes/feature_improve_which | 2 - changes/feature_key-manager | 1 - changes/feature_openpgp-context-manager | 1 - changes/feature_openpgp-sign-verify | 1 - changes/feature_raise-window-event | 1 - .../feature_use-pycrypto-for-symmetric-encryption | 1 - debian/#control# | 14 + debian/.#control | 1 + debian/changelog | 5 + debian/clean | 1 + debian/compat | 1 + debian/control | 14 + debian/copyright | 16 + debian/files | 1 + debian/python-leap.common.debhelper.log | 16 + debian/python-leap.common.postinst.debhelper | 7 + debian/python-leap.common.prerm.debhelper | 12 + debian/python-leap.common.substvars | 4 + debian/python-leap.common/DEBIAN/control | 12 + debian/python-leap.common/DEBIAN/md5sums | 33 ++ debian/python-leap.common/DEBIAN/postinst | 9 + debian/python-leap.common/DEBIAN/prerm | 14 + .../dist-packages/leap.common-0.2.3_dev-nspkg.pth | 1 + .../dist-packages/leap/common/__init__.py | 1 + .../python2.7/dist-packages/leap/common/certs.py | 1 + .../python2.7/dist-packages/leap/common/check.py | 1 + .../dist-packages/leap/common/config/__init__.py | 1 + .../dist-packages/leap/common/config/baseconfig.py | 1 + .../leap/common/config/pluggableconfig.py | 1 + .../dist-packages/leap/common/config/prefixers.py | 1 + .../dist-packages/leap/common/events/__init__.py | 1 + .../dist-packages/leap/common/events/component.py | 1 + .../dist-packages/leap/common/events/daemon.py | 1 + .../dist-packages/leap/common/events/events_pb2.py | 1 + .../dist-packages/leap/common/events/mac_auth.py | 1 + .../dist-packages/leap/common/events/server.py | 1 + .../python2.7/dist-packages/leap/common/files.py | 1 + .../leap/common/keymanager/__init__.py | 1 + .../dist-packages/leap/common/keymanager/errors.py | 1 + .../dist-packages/leap/common/keymanager/gpg.py | 1 + .../dist-packages/leap/common/keymanager/keys.py | 1 + .../leap/common/keymanager/openpgp.py | 1 + .../dist-packages/leap/common/testing/__init__.py | 1 + .../dist-packages/leap/common/testing/basetest.py | 1 + .../leap/common/testing/https_server.py | 1 + .../leap/common/testing/test_basetest.py | 1 + .../doc/python-leap.common/changelog.Debian.gz | Bin 0 -> 149 bytes .../usr/share/doc/python-leap.common/copyright | 16 + .../share/pyshared/leap.common-0.2.3_dev-nspkg.pth | 1 + .../usr/share/pyshared/leap/common/__init__.py | 19 + .../usr/share/pyshared/leap/common/certs.py | 179 ++++++++ .../usr/share/pyshared/leap/common/check.py | 61 +++ .../share/pyshared/leap/common/config/__init__.py | 0 .../pyshared/leap/common/config/baseconfig.py | 185 ++++++++ .../pyshared/leap/common/config/pluggableconfig.py | 475 +++++++++++++++++++++ .../share/pyshared/leap/common/config/prefixers.py | 132 ++++++ .../share/pyshared/leap/common/events/__init__.py | 100 +++++ .../share/pyshared/leap/common/events/component.py | 238 +++++++++++ .../share/pyshared/leap/common/events/daemon.py | 208 +++++++++ .../pyshared/leap/common/events/events_pb2.py | 371 ++++++++++++++++ .../share/pyshared/leap/common/events/mac_auth.py | 31 ++ .../share/pyshared/leap/common/events/server.py | 149 +++++++ .../usr/share/pyshared/leap/common/files.py | 126 ++++++ .../pyshared/leap/common/keymanager/__init__.py | 286 +++++++++++++ .../pyshared/leap/common/keymanager/errors.py | 46 ++ .../share/pyshared/leap/common/keymanager/gpg.py | 397 +++++++++++++++++ .../share/pyshared/leap/common/keymanager/keys.py | 230 ++++++++++ .../pyshared/leap/common/keymanager/openpgp.py | 459 ++++++++++++++++++++ .../share/pyshared/leap/common/testing/__init__.py | 0 .../share/pyshared/leap/common/testing/basetest.py | 140 ++++++ .../pyshared/leap/common/testing/https_server.py | 87 ++++ .../pyshared/leap/common/testing/test_basetest.py | 140 ++++++ .../usr/share/python/ns/python-leap.common | 1 + debian/rules | 6 + debian/source/format | 1 + setup.cfg | 5 + 83 files changed, 4293 insertions(+), 13 deletions(-) create mode 100644 PKG-INFO delete mode 100644 changes/bug_add-data-files delete mode 100644 changes/bug_allow-absolute-paths delete mode 100644 changes/bug_fix-deprecation-warning delete mode 100644 changes/bug_fix-do-not-attempt-to-fetch-privkeys-from-server delete mode 100644 changes/bug_fix-imports delete mode 100644 changes/feature_events_signals delete mode 100644 changes/feature_improve_which delete mode 100644 changes/feature_key-manager delete mode 100644 changes/feature_openpgp-context-manager delete mode 100644 changes/feature_openpgp-sign-verify delete mode 100644 changes/feature_raise-window-event delete mode 100644 changes/feature_use-pycrypto-for-symmetric-encryption create mode 100644 debian/#control# create mode 120000 debian/.#control create mode 100644 debian/changelog create mode 100644 debian/clean create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/files create mode 100644 debian/python-leap.common.debhelper.log create mode 100644 debian/python-leap.common.postinst.debhelper create mode 100644 debian/python-leap.common.prerm.debhelper create mode 100644 debian/python-leap.common.substvars create mode 100644 debian/python-leap.common/DEBIAN/control create mode 100644 debian/python-leap.common/DEBIAN/md5sums create mode 100755 debian/python-leap.common/DEBIAN/postinst create mode 100755 debian/python-leap.common/DEBIAN/prerm create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap.common-0.2.3_dev-nspkg.pth create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/__init__.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/certs.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/check.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/__init__.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/baseconfig.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/pluggableconfig.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/prefixers.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/__init__.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/component.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/daemon.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/events_pb2.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/mac_auth.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/server.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/files.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/__init__.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/errors.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/gpg.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/keys.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/openpgp.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/__init__.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/basetest.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/https_server.py create mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/test_basetest.py create mode 100644 debian/python-leap.common/usr/share/doc/python-leap.common/changelog.Debian.gz create mode 100644 debian/python-leap.common/usr/share/doc/python-leap.common/copyright create mode 100644 debian/python-leap.common/usr/share/pyshared/leap.common-0.2.3_dev-nspkg.pth create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/__init__.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/certs.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/check.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/config/__init__.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/config/baseconfig.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/config/pluggableconfig.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/config/prefixers.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/events/__init__.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/events/component.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/events/daemon.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/events/events_pb2.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/events/mac_auth.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/events/server.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/files.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/__init__.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/errors.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/gpg.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/keys.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/openpgp.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/testing/__init__.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/testing/basetest.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/testing/https_server.py create mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/testing/test_basetest.py create mode 100644 debian/python-leap.common/usr/share/python/ns/python-leap.common create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 setup.cfg diff --git a/PKG-INFO b/PKG-INFO new file mode 100644 index 0000000..a504e67 --- /dev/null +++ b/PKG-INFO @@ -0,0 +1,20 @@ +Metadata-Version: 1.1 +Name: leap.common +Version: 0.2.5 +Summary: Common files used by the LEAP project. +Home-page: https://leap.se/ +Author: The LEAP Encryption Access Project +Author-email: info@leap.se +License: GPLv3+ +Description: Common files used by the LEAP Client project. +Platform: UNKNOWN +Classifier: Development Status :: 3 - Alpha +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Topic :: Communications +Classifier: Topic :: Security +Classifier: Topic :: Utilities diff --git a/changes/bug_add-data-files b/changes/bug_add-data-files deleted file mode 100644 index 5231fb8..0000000 --- a/changes/bug_add-data-files +++ /dev/null @@ -1 +0,0 @@ - o Add data files to setup and manifest (certificates for tests) diff --git a/changes/bug_allow-absolute-paths b/changes/bug_allow-absolute-paths deleted file mode 100644 index deaff1f..0000000 --- a/changes/bug_allow-absolute-paths +++ /dev/null @@ -1 +0,0 @@ - o Allow absolute paths in baseconfig.load diff --git a/changes/bug_fix-deprecation-warning b/changes/bug_fix-deprecation-warning deleted file mode 100644 index ac58117..0000000 --- a/changes/bug_fix-deprecation-warning +++ /dev/null @@ -1 +0,0 @@ - o Fix deprecation warnings diff --git a/changes/bug_fix-do-not-attempt-to-fetch-privkeys-from-server b/changes/bug_fix-do-not-attempt-to-fetch-privkeys-from-server deleted file mode 100644 index 4c8c0eb..0000000 --- a/changes/bug_fix-do-not-attempt-to-fetch-privkeys-from-server +++ /dev/null @@ -1 +0,0 @@ - o Fix attempt to fetch private keys from server. diff --git a/changes/bug_fix-imports b/changes/bug_fix-imports deleted file mode 100644 index 509f2f4..0000000 --- a/changes/bug_fix-imports +++ /dev/null @@ -1 +0,0 @@ - o Fix missing imports diff --git a/changes/feature_events_signals b/changes/feature_events_signals deleted file mode 100644 index ae0b7b0..0000000 --- a/changes/feature_events_signals +++ /dev/null @@ -1 +0,0 @@ - o Add a mechanism for events signaling between components. diff --git a/changes/feature_improve_which b/changes/feature_improve_which deleted file mode 100644 index d1d1fb5..0000000 --- a/changes/feature_improve_which +++ /dev/null @@ -1,2 +0,0 @@ - o Prioritize the path_extension in the which method so it finds our bundled - app before the system one, if any. diff --git a/changes/feature_key-manager b/changes/feature_key-manager deleted file mode 100644 index 47a62ed..0000000 --- a/changes/feature_key-manager +++ /dev/null @@ -1 +0,0 @@ - o Move the Key Manager to leap client repository. diff --git a/changes/feature_openpgp-context-manager b/changes/feature_openpgp-context-manager deleted file mode 100644 index 4dbf759..0000000 --- a/changes/feature_openpgp-context-manager +++ /dev/null @@ -1 +0,0 @@ - o Refactor opengpg utility functions implementation so it uses a context manager. diff --git a/changes/feature_openpgp-sign-verify b/changes/feature_openpgp-sign-verify deleted file mode 100644 index 9422edc..0000000 --- a/changes/feature_openpgp-sign-verify +++ /dev/null @@ -1 +0,0 @@ - o Add OpenPGP sign/verify diff --git a/changes/feature_raise-window-event b/changes/feature_raise-window-event deleted file mode 100644 index 382ff3f..0000000 --- a/changes/feature_raise-window-event +++ /dev/null @@ -1 +0,0 @@ - o Add RAISE_WINDOW event diff --git a/changes/feature_use-pycrypto-for-symmetric-encryption b/changes/feature_use-pycrypto-for-symmetric-encryption deleted file mode 100644 index 5448483..0000000 --- a/changes/feature_use-pycrypto-for-symmetric-encryption +++ /dev/null @@ -1 +0,0 @@ - o Add AES-256 (CTR mode) encrypting/decrypting functions using PyCrypto. diff --git a/debian/#control# b/debian/#control# new file mode 100644 index 0000000..9f79cc3 --- /dev/null +++ b/debian/#control# @@ -0,0 +1,14 @@ +Source: leap-common +Maintainer: Micah Anderson +Section: python +Priority: optional +Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 9), python-protobuf, python-protobuf.socketrpc, protobuf-c-compiler +Standards-Version: 3.9.4 + +Package: python-leap.common +Architecture: all +Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-xdg, python-protobuf, python-protobuf.socketrpc, + python-dateutil, python-autopep8, python-openssl, python-gnupg +Description: Common python files needed by LEAP projects. + This package contains common python functions that are needed + for the LEAP Client project diff --git a/debian/.#control b/debian/.#control new file mode 120000 index 0000000..cedfffd --- /dev/null +++ b/debian/.#control @@ -0,0 +1 @@ +micah@muck.riseup.net.4743:1369862767 \ No newline at end of file diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..f448d18 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +leap-common (0.2.3-dev1) unstable; urgency=low + + * Initial debianization + + -- Micah Anderson Thu, 30 May 2013 14:06:16 -0400 diff --git a/debian/clean b/debian/clean new file mode 100644 index 0000000..8dbc13e --- /dev/null +++ b/debian/clean @@ -0,0 +1 @@ +src/leap.common.egg-info/* diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..f5bb89e --- /dev/null +++ b/debian/control @@ -0,0 +1,14 @@ +Source: leap-common +Maintainer: Micah Anderson +Section: python +Priority: optional +Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 9), python-protobuf, python-protobuf.socketrpc +Standards-Version: 3.9.4 + +Package: python-leap.common +Architecture: all +Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-xdg, python-protobuf, python-protobuf.socketrpc, + python-dateutil, python-autopep8, python-openssl, python-gnupg +Description: Common python files needed by LEAP projects. + This package contains common python functions that are needed + for the LEAP Client project diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..a132a29 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,16 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: leap-common +Upstream-Contact: kali@leap.se +Source: + +Files: * + Copyright (C) 2013 LEAP +License: GPL-3+ + +Files: debian/* +Copyright: Copyright 2013 Micah Anderson +License: GPL-3+ + +License: GPL-3+ + On Debian systems, the complete text of the GNU General + Public License can be found in `/usr/share/common-licenses/GPL'. diff --git a/debian/files b/debian/files new file mode 100644 index 0000000..0fce32e --- /dev/null +++ b/debian/files @@ -0,0 +1 @@ +python-leap.common_0.2.3-dev1_all.deb python optional diff --git a/debian/python-leap.common.debhelper.log b/debian/python-leap.common.debhelper.log new file mode 100644 index 0000000..b57ac90 --- /dev/null +++ b/debian/python-leap.common.debhelper.log @@ -0,0 +1,16 @@ +dh_auto_configure +dh_auto_build +dh_auto_test +dh_prep +dh_auto_install +dh_installdocs +dh_installchangelogs +dh_perl +dh_link +dh_compress +dh_fixperms +dh_installdeb +dh_gencontrol +dh_md5sums +dh_builddeb +dh_builddeb diff --git a/debian/python-leap.common.postinst.debhelper b/debian/python-leap.common.postinst.debhelper new file mode 100644 index 0000000..f15a4c9 --- /dev/null +++ b/debian/python-leap.common.postinst.debhelper @@ -0,0 +1,7 @@ + +# Automatically added by dh_python2: +if which pycompile >/dev/null 2>&1; then + pycompile -p python-leap.common +fi + +# End automatically added section diff --git a/debian/python-leap.common.prerm.debhelper b/debian/python-leap.common.prerm.debhelper new file mode 100644 index 0000000..285a1b2 --- /dev/null +++ b/debian/python-leap.common.prerm.debhelper @@ -0,0 +1,12 @@ + +# Automatically added by dh_python2: +if which pyclean >/dev/null 2>&1; then + pyclean -p python-leap.common +else + dpkg -L python-leap.common | grep \.py$ | while read file + do + rm -f "${file}"[co] >/dev/null + done +fi + +# End automatically added section diff --git a/debian/python-leap.common.substvars b/debian/python-leap.common.substvars new file mode 100644 index 0000000..c2bab2c --- /dev/null +++ b/debian/python-leap.common.substvars @@ -0,0 +1,4 @@ +python:Versions=2.7 +python:Provides=python2.7-leap.common +python:Depends=python (>= 2.7), python (<< 2.8), python (>= 2.6.6-7~), python-jsonschema, python-xdg, python-protobuf, python-protobuf.socketrpc, python-openssl, python-dateutil +misc:Depends= diff --git a/debian/python-leap.common/DEBIAN/control b/debian/python-leap.common/DEBIAN/control new file mode 100644 index 0000000..7ec433d --- /dev/null +++ b/debian/python-leap.common/DEBIAN/control @@ -0,0 +1,12 @@ +Package: python-leap.common +Source: leap-common +Version: 0.2.3-dev1 +Architecture: all +Maintainer: Micah Anderson +Installed-Size: 234 +Depends: python (>= 2.7), python (<< 2.8), python-jsonschema, python-xdg, python-protobuf, python-protobuf.socketrpc, python-openssl, python-dateutil, python-autopep8, python-gnupg +Section: python +Priority: optional +Description: Common python files needed by LEAP projects. + This package contains common python functions that are needed + for the LEAP Client project diff --git a/debian/python-leap.common/DEBIAN/md5sums b/debian/python-leap.common/DEBIAN/md5sums new file mode 100644 index 0000000..1bfa617 --- /dev/null +++ b/debian/python-leap.common/DEBIAN/md5sums @@ -0,0 +1,33 @@ +31fd2c104cd4c1d193ec9aa4f20e6001 usr/share/doc/python-leap.common/changelog.Debian.gz +21d310049c19f5d33ba5ea6c60e5dca8 usr/share/doc/python-leap.common/copyright +d22e9cf7bd13e4ede09bac84421f142d usr/share/pyshared/leap.common-0.2.3_dev-nspkg.pth +990b94e8f6be8bb2739002e88ab33b2b usr/share/pyshared/leap.common-0.2.3_dev.egg-info/PKG-INFO +37c1614b30fee94ef7e62c0eb4045f65 usr/share/pyshared/leap.common-0.2.3_dev.egg-info/SOURCES.txt +e8d5c7c249b5856392d6448fd5899f5e usr/share/pyshared/leap.common-0.2.3_dev.egg-info/dependency_links.txt +15efee23227e2dd46960cbb8a3b1a9f0 usr/share/pyshared/leap.common-0.2.3_dev.egg-info/namespace_packages.txt +613023c3d0b371605285222494503930 usr/share/pyshared/leap.common-0.2.3_dev.egg-info/requires.txt +15efee23227e2dd46960cbb8a3b1a9f0 usr/share/pyshared/leap.common-0.2.3_dev.egg-info/top_level.txt +533cda9d3cb16103fbcc42de8fac6a34 usr/share/pyshared/leap/common/__init__.py +4435367cd0c437d5b2d4a191b26dabd1 usr/share/pyshared/leap/common/certs.py +6c883a1051129c65f75c53378f0e7a1d usr/share/pyshared/leap/common/check.py +d41d8cd98f00b204e9800998ecf8427e usr/share/pyshared/leap/common/config/__init__.py +877407026ce9d42d0da03d3b72301740 usr/share/pyshared/leap/common/config/baseconfig.py +ff1eb89cf37e042e1b572e9138ae5269 usr/share/pyshared/leap/common/config/pluggableconfig.py +f5284b4e8d3aaae6de58ad42043e1162 usr/share/pyshared/leap/common/config/prefixers.py +33eb1eeb41079ad0234afba2edbf0a9d usr/share/pyshared/leap/common/events/__init__.py +732e2b7ca5f22d151c42ce64c736b82e usr/share/pyshared/leap/common/events/component.py +a861976eef97e7ed95bb33f6dbeb05f4 usr/share/pyshared/leap/common/events/daemon.py +cb7221fa887ff5e2e08c47bf13f6a23e usr/share/pyshared/leap/common/events/events_pb2.py +c8dbde173372f58602fe6dd19fc30cf1 usr/share/pyshared/leap/common/events/mac_auth.py +bd6ebdc4f0441b689d6f0101f3b9fa80 usr/share/pyshared/leap/common/events/server.py +8a9d3a856b42c4a5c5e69dcfe8982307 usr/share/pyshared/leap/common/files.py +77bbd4d03995b6f4652958516d710efa usr/share/pyshared/leap/common/keymanager/__init__.py +ddef5455c3b7964365267184a3fa2c0c usr/share/pyshared/leap/common/keymanager/errors.py +05828143ff4d2b9da92227da4bc12c4d usr/share/pyshared/leap/common/keymanager/gpg.py +b85230945b9a597bed40bfadb5f01178 usr/share/pyshared/leap/common/keymanager/keys.py +0f7ea32cb387a08d9a043decd51d0f03 usr/share/pyshared/leap/common/keymanager/openpgp.py +d41d8cd98f00b204e9800998ecf8427e usr/share/pyshared/leap/common/testing/__init__.py +5dd5c2a35014c45510c0cec0874c3cb1 usr/share/pyshared/leap/common/testing/basetest.py +8f232808fe0fa9ab35baee7290a1081b usr/share/pyshared/leap/common/testing/https_server.py +848e9db64230b00a1b4047f1f0ce4e7d usr/share/pyshared/leap/common/testing/test_basetest.py +15efee23227e2dd46960cbb8a3b1a9f0 usr/share/python/ns/python-leap.common diff --git a/debian/python-leap.common/DEBIAN/postinst b/debian/python-leap.common/DEBIAN/postinst new file mode 100755 index 0000000..5dc39f3 --- /dev/null +++ b/debian/python-leap.common/DEBIAN/postinst @@ -0,0 +1,9 @@ +#!/bin/sh +set -e + +# Automatically added by dh_python2: +if which pycompile >/dev/null 2>&1; then + pycompile -p python-leap.common +fi + +# End automatically added section diff --git a/debian/python-leap.common/DEBIAN/prerm b/debian/python-leap.common/DEBIAN/prerm new file mode 100755 index 0000000..702eec2 --- /dev/null +++ b/debian/python-leap.common/DEBIAN/prerm @@ -0,0 +1,14 @@ +#!/bin/sh +set -e + +# Automatically added by dh_python2: +if which pyclean >/dev/null 2>&1; then + pyclean -p python-leap.common +else + dpkg -L python-leap.common | grep \.py$ | while read file + do + rm -f "${file}"[co] >/dev/null + done +fi + +# End automatically added section diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap.common-0.2.3_dev-nspkg.pth b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap.common-0.2.3_dev-nspkg.pth new file mode 120000 index 0000000..596c637 --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap.common-0.2.3_dev-nspkg.pth @@ -0,0 +1 @@ +../../../share/pyshared/leap.common-0.2.3_dev-nspkg.pth \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/__init__.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/__init__.py new file mode 120000 index 0000000..1553bef --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/__init__.py @@ -0,0 +1 @@ +../../../../../share/pyshared/leap/common/__init__.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/certs.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/certs.py new file mode 120000 index 0000000..7585fb4 --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/certs.py @@ -0,0 +1 @@ +../../../../../share/pyshared/leap/common/certs.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/check.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/check.py new file mode 120000 index 0000000..6a7db1e --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/check.py @@ -0,0 +1 @@ +../../../../../share/pyshared/leap/common/check.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/__init__.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/__init__.py new file mode 120000 index 0000000..2c55977 --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/__init__.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/config/__init__.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/baseconfig.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/baseconfig.py new file mode 120000 index 0000000..98b7bba --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/baseconfig.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/config/baseconfig.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/pluggableconfig.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/pluggableconfig.py new file mode 120000 index 0000000..1172cd4 --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/pluggableconfig.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/config/pluggableconfig.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/prefixers.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/prefixers.py new file mode 120000 index 0000000..e87c993 --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/prefixers.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/config/prefixers.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/__init__.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/__init__.py new file mode 120000 index 0000000..075facb --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/__init__.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/events/__init__.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/component.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/component.py new file mode 120000 index 0000000..8959c67 --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/component.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/events/component.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/daemon.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/daemon.py new file mode 120000 index 0000000..afc5565 --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/daemon.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/events/daemon.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/events_pb2.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/events_pb2.py new file mode 120000 index 0000000..82bbbc2 --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/events_pb2.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/events/events_pb2.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/mac_auth.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/mac_auth.py new file mode 120000 index 0000000..935cc1c --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/mac_auth.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/events/mac_auth.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/server.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/server.py new file mode 120000 index 0000000..3696d19 --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/server.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/events/server.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/files.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/files.py new file mode 120000 index 0000000..3c572b0 --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/files.py @@ -0,0 +1 @@ +../../../../../share/pyshared/leap/common/files.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/__init__.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/__init__.py new file mode 120000 index 0000000..c34b26b --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/__init__.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/keymanager/__init__.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/errors.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/errors.py new file mode 120000 index 0000000..cbd005a --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/errors.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/keymanager/errors.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/gpg.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/gpg.py new file mode 120000 index 0000000..35582de --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/gpg.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/keymanager/gpg.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/keys.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/keys.py new file mode 120000 index 0000000..bcb823c --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/keys.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/keymanager/keys.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/openpgp.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/openpgp.py new file mode 120000 index 0000000..a403cb0 --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/openpgp.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/keymanager/openpgp.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/__init__.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/__init__.py new file mode 120000 index 0000000..16a0445 --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/__init__.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/testing/__init__.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/basetest.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/basetest.py new file mode 120000 index 0000000..31a3556 --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/basetest.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/testing/basetest.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/https_server.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/https_server.py new file mode 120000 index 0000000..298c389 --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/https_server.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/testing/https_server.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/test_basetest.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/test_basetest.py new file mode 120000 index 0000000..a5ff0cd --- /dev/null +++ b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/test_basetest.py @@ -0,0 +1 @@ +../../../../../../share/pyshared/leap/common/testing/test_basetest.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/share/doc/python-leap.common/changelog.Debian.gz b/debian/python-leap.common/usr/share/doc/python-leap.common/changelog.Debian.gz new file mode 100644 index 0000000..9261f57 Binary files /dev/null and b/debian/python-leap.common/usr/share/doc/python-leap.common/changelog.Debian.gz differ diff --git a/debian/python-leap.common/usr/share/doc/python-leap.common/copyright b/debian/python-leap.common/usr/share/doc/python-leap.common/copyright new file mode 100644 index 0000000..a132a29 --- /dev/null +++ b/debian/python-leap.common/usr/share/doc/python-leap.common/copyright @@ -0,0 +1,16 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: leap-common +Upstream-Contact: kali@leap.se +Source: + +Files: * + Copyright (C) 2013 LEAP +License: GPL-3+ + +Files: debian/* +Copyright: Copyright 2013 Micah Anderson +License: GPL-3+ + +License: GPL-3+ + On Debian systems, the complete text of the GNU General + Public License can be found in `/usr/share/common-licenses/GPL'. diff --git a/debian/python-leap.common/usr/share/pyshared/leap.common-0.2.3_dev-nspkg.pth b/debian/python-leap.common/usr/share/pyshared/leap.common-0.2.3_dev-nspkg.pth new file mode 100644 index 0000000..0f2034f --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap.common-0.2.3_dev-nspkg.pth @@ -0,0 +1 @@ +import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('leap',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('leap',types.ModuleType('leap')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/__init__.py b/debian/python-leap.common/usr/share/pyshared/leap/common/__init__.py new file mode 100644 index 0000000..5bcbb38 --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/__init__.py @@ -0,0 +1,19 @@ +import logging + +from leap.common import certs +from leap.common import check +from leap.common import files +from leap.common import events + +logger = logging.getLogger(__name__) + +try: + import pygeoip + HAS_GEOIP = True +except ImportError: + #logger.debug('PyGeoIP not found. Disabled Geo support.') + HAS_GEOIP = False + +__all__ = ["certs", "check", "files", "events"] + +__version__ = "0.2.3-dev" diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/certs.py b/debian/python-leap.common/usr/share/pyshared/leap/common/certs.py new file mode 100644 index 0000000..4cb70dd --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/certs.py @@ -0,0 +1,179 @@ +# -*- coding: utf-8 -*- +# certs.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Implements cert checks and helpers +""" + +import os +import time +import logging + +from OpenSSL import crypto +from dateutil.parser import parse as dateparse + +from leap.common.check import leap_assert + +logger = logging.getLogger(__name__) + + +def get_cert_from_string(string): + """ + Returns the x509 from the contents of this string + + @param string: certificate contents as downloaded + @type string: str + + @return: x509 or None + """ + leap_assert(string, "We need something to load") + + x509 = None + try: + x509 = crypto.load_certificate(crypto.FILETYPE_PEM, string) + except Exception as e: + logger.error("Something went wrong while loading the certificate: %r" + % (e,)) + return x509 + + +def get_privatekey_from_string(string): + """ + Returns the private key from the contents of this string + + @param string: private key contents as downloaded + @type string: str + + @return: private key or None + """ + leap_assert(string, "We need something to load") + + pkey = None + try: + pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, string) + except Exception as e: + logger.error("Something went wrong while loading the certificate: %r" + % (e,)) + return pkey + + +def get_digest(cert_data, method): + """ + Returns the digest for the cert_data using the method specified + + @param cert_data: certificate data in string form + @type cert_data: str + @param method: method to be used for digest + @type method: str + + @rtype: str + """ + x509 = get_cert_from_string(cert_data) + digest = x509.digest(method).replace(":", "").lower() + + return digest + + +def can_load_cert_and_pkey(string): + """ + Loads certificate and private key from a buffer, returns True if + everything went well, False otherwise + + @param string: buffer containing the cert and private key + @type string: str or any kind of buffer + + @rtype: bool + """ + can_load = True + + try: + cert = get_cert_from_string(string) + key = get_privatekey_from_string(string) + + leap_assert(cert, 'The certificate could not be loaded') + leap_assert(key, 'The private key could not be loaded') + except Exception as e: + can_load = False + logger.error("Something went wrong while trying to load " + "the certificate: %r" % (e,)) + + return can_load + + +def is_valid_pemfile(cert): + """ + Checks that the passed string is a valid pem certificate + + @param cert: String containing pem content + @type cert: str + + @rtype: bool + """ + leap_assert(cert, "We need a cert to load") + + return can_load_cert_and_pkey(cert) + + +def get_cert_time_boundaries(certfile): + """ + Returns the time boundaries for the certificate saved in certfile + + @param certfile: path to certificate + @type certfile: str + + @rtype: tuple (from, to) + """ + cert = get_cert_from_string(certfile) + leap_assert(cert, 'There was a problem loading the certificate') + + fromts, tots = (cert.get_notBefore(), cert.get_notAfter()) + from_, to_ = map( + lambda ts: time.gmtime(time.mktime(dateparse(ts).timetuple())), + (fromts, tots)) + return from_, to_ + + +def should_redownload(certfile, now=time.gmtime): + """ + Returns True if any of the checks don't pass, False otherwise + + @param certfile: path to certificate + @type certfile: str + @param now: current date function, ONLY USED FOR TESTING + + @rtype: bool + """ + exists = os.path.isfile(certfile) + + if not exists: + return True + + certdata = None + try: + with open(certfile, "r") as f: + certdata = f.read() + if not is_valid_pemfile(certdata): + return True + except: + return True + + valid_from, valid_to = get_cert_time_boundaries(certdata) + + if not (valid_from < now() < valid_to): + return True + + return False diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/check.py b/debian/python-leap.common/usr/share/pyshared/leap/common/check.py new file mode 100644 index 0000000..359673b --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/check.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# check.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Set of functions to help checking situations +""" + +import inspect +import logging +import traceback + + +logger = logging.getLogger(__name__) + + +def leap_assert(condition, message=""): + """ + Asserts the condition and displays the message if that's not + met. It also logs the error and its backtrace. + + @param condition: condition to check + @type condition: bool + @param message: message to display if the condition isn't met + @type message: str + """ + if not condition: + logger.error("Bug: %s" % (message,)) + try: + frame = inspect.currentframe() + stack_trace = traceback.format_stack(frame) + logger.error(''.join(stack_trace)) + except Exception as e: + logger.error("Bug in leap_assert: %r" % (e,)) + assert condition, message + + +def leap_assert_type(var, expectedType): + """ + Helper assert check for a variable's expected type + + @param var: variable to check + @type var: any + @param expectedType: type to check agains + @type expectedType: type + """ + leap_assert(isinstance(var, expectedType), + "Expected type %r instead of %r" % + (expectedType, type(var))) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/config/__init__.py b/debian/python-leap.common/usr/share/pyshared/leap/common/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/config/baseconfig.py b/debian/python-leap.common/usr/share/pyshared/leap/common/config/baseconfig.py new file mode 100644 index 0000000..146f1e4 --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/config/baseconfig.py @@ -0,0 +1,185 @@ +# -*- coding: utf-8 -*- +# baseconfig.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Implements the abstract base class for configuration +""" + +import copy +import logging +import functools +import os + +from abc import ABCMeta, abstractmethod + +from leap.common.check import leap_assert +from leap.common.files import mkdir_p +from leap.common.config.pluggableconfig import PluggableConfig +from leap.common.config.prefixers import get_platform_prefixer + +logger = logging.getLogger(__name__) + + +class BaseConfig: + """ + Abstract base class for any JSON based configuration + """ + + __metaclass__ = ABCMeta + + """ + Standalone is a class wide parameter + + @param standalone: if True it will return the prefix for a + standalone application. Otherwise, it will return the system + default for configuration storage. + @type standalone: bool + """ + standalone = False + + def __init__(self): + self._data = {} + self._config_checker = None + + @abstractmethod + def _get_spec(self): + """ + Returns the spec object for the specific configuration + """ + return None + + def _safe_get_value(self, key): + """ + Tries to return a value only if the config has already been loaded + + @rtype: depends on the config structure, dict, str, array, int + @return: returns the value for the specified key in the config + """ + leap_assert(self._config_checker, "Load the config first") + return self._config_checker.config.get(key, None) + + def get_path_prefix(self): + """ + Returns the platform dependant path prefixer + """ + return get_platform_prefixer().get_path_prefix( + standalone=self.standalone) + + def loaded(self): + """ + Returns True if the configuration has been already + loaded. False otherwise + """ + return self._config_checker is not None + + def save(self, path_list): + """ + Saves the current configuration to disk + + @param path_list: list of components that form the relative + path to configuration. The absolute path will be calculated + depending on the platform. + @type path_list: list + + @return: True if saved to disk correctly, False otherwise + """ + config_path = os.path.join(self.get_path_prefix(), *(path_list[:-1])) + mkdir_p(config_path) + + try: + self._config_checker.serialize(os.path.join(config_path, + path_list[-1])) + except Exception as e: + logger.warning("%s" % (e,)) + raise + return True + + def load(self, path="", data=None, mtime=None): + """ + Loads the configuration from disk + + @type path: str + @param path: relative path to configuration. The absolute path + will be calculated depending on the platform + + @return: True if loaded from disk correctly, False otherwise + """ + + config_path = os.path.join(self.get_path_prefix(), + path) + + self._config_checker = PluggableConfig(format="json") + self._config_checker.options = copy.deepcopy(self._get_spec()) + + try: + if data is None: + self._config_checker.load(fromfile=config_path, mtime=mtime) + else: + self._config_checker.load(data, mtime=mtime) + except Exception as e: + logger.warning("Something went wrong while loading " + + "the config from %s\n%s" % (config_path, e)) + self._config_checker = None + return False + return True + + +class LocalizedKey(object): + """ + Decorator used for keys that are localized in a configuration + """ + + def __init__(self, func, **kwargs): + self._func = func + + def __call__(self, instance, lang="en"): + """ + Tries to return the string for the specified language, otherwise + informs the problem and returns an empty string + + @param lang: language code + @type lang: str + + @return: localized value from the possible values returned by + self._func + """ + descriptions = self._func(instance) + description_lang = "" + config_lang = "en" + for key in descriptions.keys(): + if lang.startswith(key): + config_lang = key + break + + description_lang = descriptions[config_lang] + return description_lang + + def __get__(self, instance, instancetype): + """ + Implement the descriptor protocol to make decorating instance + method possible. + """ + # Return a partial function with the first argument is the instance + # of the class decorated. + return functools.partial(self.__call__, instance) + +if __name__ == "__main__": + try: + config = BaseConfig() # should throw TypeError for _get_spec + except Exception as e: + assert isinstance(e, TypeError), "Something went wrong" + print "Abstract BaseConfig class is working as expected" diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/config/pluggableconfig.py b/debian/python-leap.common/usr/share/pyshared/leap/common/config/pluggableconfig.py new file mode 100644 index 0000000..8535fa6 --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/config/pluggableconfig.py @@ -0,0 +1,475 @@ +# -*- coding: utf-8 -*- +# pluggableconfig.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +generic configuration handlers +""" +import copy +import json +import logging +import os +import time +import urlparse + +import jsonschema + +#from leap.base.util.translations import LEAPTranslatable +from leap.common.check import leap_assert + + +logger = logging.getLogger(__name__) + + +__all__ = ['PluggableConfig', + 'adaptors', + 'types', + 'UnknownOptionException', + 'MissingValueException', + 'ConfigurationProviderException', + 'TypeCastException'] + +# exceptions + + +class UnknownOptionException(Exception): + """exception raised when a non-configuration + value is present in the configuration""" + + +class MissingValueException(Exception): + """exception raised when a required value is missing""" + + +class ConfigurationProviderException(Exception): + """exception raised when a configuration provider is missing, etc""" + + +class TypeCastException(Exception): + """exception raised when a + configuration item cannot be coerced to a type""" + + +class ConfigAdaptor(object): + """ + abstract base class for config adaotors for + serialization/deserialization and custom validation + and type casting. + """ + def read(self, filename): + raise NotImplementedError("abstract base class") + + def write(self, config, filename): + with open(filename, 'w') as f: + self._write(f, config) + + def _write(self, fp, config): + raise NotImplementedError("abstract base class") + + def validate(self, config, schema): + raise NotImplementedError("abstract base class") + + +adaptors = {} + + +class JSONSchemaEncoder(json.JSONEncoder): + """ + custom default encoder that + casts python objects to json objects for + the schema validation + """ + def default(self, obj): + if obj is str: + return 'string' + if obj is unicode: + return 'string' + if obj is int: + return 'integer' + if obj is list: + return 'array' + if obj is dict: + return 'object' + if obj is bool: + return 'boolean' + + +class JSONAdaptor(ConfigAdaptor): + indent = 2 + extensions = ['json'] + + def read(self, _from): + if isinstance(_from, file): + _from_string = _from.read() + if isinstance(_from, str): + _from_string = _from + return json.loads(_from_string) + + def _write(self, fp, config): + fp.write(json.dumps(config, + indent=self.indent, + sort_keys=True)) + + def validate(self, config, schema_obj): + schema_json = JSONSchemaEncoder().encode(schema_obj) + schema = json.loads(schema_json) + jsonschema.validate(config, schema) + + +adaptors['json'] = JSONAdaptor() + +# +# Adaptors +# +# Allow to apply a predefined set of types to the +# specs, so it checks the validity of formats and cast it +# to proper python types. + +# TODO: +# - HTTPS uri + + +class DateType(object): + fmt = '%Y-%m-%d' + + def to_python(self, data): + return time.strptime(data, self.fmt) + + def get_prep_value(self, data): + return time.strftime(self.fmt, data) + + +class TranslatableType(object): + """ + a type that casts to LEAPTranslatable objects. + Used for labels we get from providers and stuff. + """ + + def to_python(self, data): + # TODO: add translatable + return data # LEAPTranslatable(data) + + # needed? we already have an extended dict... + #def get_prep_value(self, data): + #return dict(data) + + +class URIType(object): + + def to_python(self, data): + parsed = urlparse.urlparse(data) + if not parsed.scheme: + raise TypeCastException("uri %s has no schema" % data) + return parsed.geturl() + + def get_prep_value(self, data): + return data + + +class HTTPSURIType(object): + + def to_python(self, data): + parsed = urlparse.urlparse(data) + if not parsed.scheme: + raise TypeCastException("uri %s has no schema" % data) + if parsed.scheme != "https": + raise TypeCastException( + "uri %s does not has " + "https schema" % data) + return parsed.geturl() + + def get_prep_value(self, data): + return data + + +types = { + 'date': DateType(), + 'uri': URIType(), + 'https-uri': HTTPSURIType(), + 'translatable': TranslatableType(), +} + + +class PluggableConfig(object): + + options = {} + + def __init__(self, + adaptors=adaptors, + types=types, + format=None): + + self.config = {} + self.adaptors = adaptors + self.types = types + self._format = format + self.mtime = None + self.dirty = False + + @property + def option_dict(self): + if hasattr(self, 'options') and isinstance(self.options, dict): + return self.options.get('properties', None) + + def items(self): + """ + act like an iterator + """ + if isinstance(self.option_dict, dict): + return self.option_dict.items() + return self.options + + def validate(self, config, format=None): + """ + validate config + """ + schema = self.options + if format is None: + format = self._format + + if format: + adaptor = self.get_adaptor(self._format) + adaptor.validate(config, schema) + else: + # we really should make format mandatory... + logger.error('no format passed to validate') + + # first round of validation is ok. + # now we proceed to cast types if any specified. + self.to_python(config) + + def to_python(self, config): + """ + cast types following first type and then format indications. + """ + unseen_options = [i for i in config if i not in self.option_dict] + if unseen_options: + raise UnknownOptionException( + "Unknown options: %s" % ', '.join(unseen_options)) + + for key, value in config.items(): + _type = self.option_dict[key].get('type') + if _type is None and 'default' in self.option_dict[key]: + _type = type(self.option_dict[key]['default']) + if _type is not None: + tocast = True + if not callable(_type) and isinstance(value, _type): + tocast = False + if tocast: + try: + config[key] = _type(value) + except BaseException, e: + raise TypeCastException( + "Could not coerce %s, %s, " + "to type %s: %s" % (key, value, _type.__name__, e)) + _format = self.option_dict[key].get('format', None) + _ftype = self.types.get(_format, None) + if _ftype: + try: + config[key] = _ftype.to_python(value) + except BaseException, e: + raise TypeCastException( + "Could not coerce %s, %s, " + "to format %s: %s" % (key, value, + _ftype.__class__.__name__, + e)) + + return config + + def prep_value(self, config): + """ + the inverse of to_python method, + called just before serialization + """ + for key, value in config.items(): + _format = self.option_dict[key].get('format', None) + _ftype = self.types.get(_format, None) + if _ftype and hasattr(_ftype, 'get_prep_value'): + try: + config[key] = _ftype.get_prep_value(value) + except BaseException, e: + raise TypeCastException( + "Could not serialize %s, %s, " + "by format %s: %s" % (key, value, + _ftype.__class__.__name__, + e)) + else: + config[key] = value + return config + + # methods for adding configuration + + def get_default_values(self): + """ + return a config options from configuration defaults + """ + defaults = {} + for key, value in self.items(): + if 'default' in value: + defaults[key] = value['default'] + return copy.deepcopy(defaults) + + def get_adaptor(self, format): + """ + get specified format adaptor or + guess for a given filename + """ + adaptor = self.adaptors.get(format, None) + if adaptor: + return adaptor + + # not registered in adaptors dict, let's try all + for adaptor in self.adaptors.values(): + if format in adaptor.extensions: + return adaptor + + def filename2format(self, filename): + extension = os.path.splitext(filename)[-1] + return extension.lstrip('.') or None + + def serialize(self, filename, format=None, full=False): + if not format: + format = self._format + if not format: + format = self.filename2format(filename) + if not format: + raise Exception('Please specify a format') + # TODO: more specific exception type + + adaptor = self.get_adaptor(format) + if not adaptor: + raise Exception("Adaptor not found for format: %s" % format) + + config = copy.deepcopy(self.config) + serializable = self.prep_value(config) + adaptor.write(serializable, filename) + + if self.mtime: + self.touch_mtime(filename) + + def touch_mtime(self, filename): + mtime = self.mtime + os.utime(filename, (mtime, mtime)) + + def deserialize(self, string=None, fromfile=None, format=None): + """ + load configuration from a file or string + """ + + def _try_deserialize(): + if fromfile: + with open(fromfile, 'r') as f: + content = adaptor.read(f) + elif string: + content = adaptor.read(string) + return content + + # XXX cleanup this! + + if fromfile: + leap_assert(os.path.exists(fromfile)) + if not format: + format = self.filename2format(fromfile) + + if not format: + format = self._format + if format: + adaptor = self.get_adaptor(format) + else: + adaptor = None + + if adaptor: + content = _try_deserialize() + return content + + # no adaptor, let's try rest of adaptors + + adaptors = self.adaptors[:] + + if format: + adaptors.sort( + key=lambda x: int( + format in x.extensions), + reverse=True) + + for adaptor in adaptors: + content = _try_deserialize() + return content + + def set_dirty(self): + self.dirty = True + + def is_dirty(self): + return self.dirty + + def load(self, *args, **kwargs): + """ + load from string or file + if no string of fromfile option is given, + it will attempt to load from defaults + defined in the schema. + """ + string = args[0] if args else None + fromfile = kwargs.get("fromfile", None) + mtime = kwargs.pop("mtime", None) + self.mtime = mtime + content = None + + # start with defaults, so we can + # have partial values applied. + content = self.get_default_values() + if string and isinstance(string, str): + content = self.deserialize(string) + + if not string and fromfile is not None: + #import ipdb;ipdb.set_trace() + content = self.deserialize(fromfile=fromfile) + + if not content: + logger.error('no content could be loaded') + # XXX raise! + return + + # lazy evaluation until first level of nesting + # to allow lambdas with context-dependant info + # like os.path.expanduser + for k, v in content.iteritems(): + if callable(v): + content[k] = v() + + self.validate(content) + self.config = content + return True + + +def testmain(): # pragma: no cover + + from tests import test_validation as t + import pprint + + config = PluggableConfig(_format="json") + properties = copy.deepcopy(t.sample_spec) + + config.options = properties + config.load(fromfile='data.json') + + print 'config' + pprint.pprint(config.config) + + config.serialize('/tmp/testserial.json') + +if __name__ == "__main__": + testmain() diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/config/prefixers.py b/debian/python-leap.common/usr/share/pyshared/leap/common/config/prefixers.py new file mode 100644 index 0000000..27274bd --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/config/prefixers.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +# prefixers.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Platform dependant configuration path prefixers +""" +import os +import platform + +from abc import ABCMeta, abstractmethod +from xdg import BaseDirectory + +from leap.common.check import leap_assert + + +class Prefixer: + """ + Abstract prefixer class + """ + + __metaclass__ = ABCMeta + + @abstractmethod + def get_path_prefix(self, standalone=False): + """ + Returns the platform dependant path prefixer + + @param standalone: if True it will return the prefix for a + standalone application. Otherwise, it will return the system + default for configuration storage. + @type standalone: bool + """ + return "" + + +def get_platform_prefixer(): + prefixer = globals()[platform.system() + "Prefixer"] + leap_assert(prefixer, "Unimplemented platform prefixer: %s" % + (platform.system(),)) + return prefixer() + + +class LinuxPrefixer(Prefixer): + """ + Config prefixer for the Linux platform + """ + + def get_path_prefix(self, standalone=False): + """ + Returns the platform dependant path prefixer. + This method expects an env variable named LEAP_CLIENT_PATH if + standalone is used. + + @param standalone: if True it will return the prefix for a + standalone application. Otherwise, it will return the system + default for configuration storage. + @type standalone: bool + """ + config_dir = BaseDirectory.xdg_config_home + if not standalone: + return config_dir + return os.path.join(os.getcwd(), "config") + + +class DarwinPrefixer(Prefixer): + """ + Config prefixer for the Darwin platform + """ + + def get_path_prefix(self, standalone=False): + """ + Returns the platform dependant path prefixer. + This method expects an env variable named LEAP_CLIENT_PATH if + standalone is used. + + @param standalone: if True it will return the prefix for a + standalone application. Otherwise, it will return the system + default for configuration storage. + @type standalone: bool + """ + config_dir = BaseDirectory.xdg_config_home + if not standalone: + return config_dir + return os.getenv(os.getcwd(), "config") + + +class WindowsPrefixer(Prefixer): + """ + Config prefixer for the Windows platform + """ + + def get_path_prefix(self, standalone=False): + """ + Returns the platform dependant path prefixer. + This method expects an env variable named LEAP_CLIENT_PATH if + standalone is used. + + @param standalone: if True it will return the prefix for a + standalone application. Otherwise, it will return the system + default for configuration storage. + @type standalone: bool + """ + config_dir = BaseDirectory.xdg_config_home + + if not standalone: + return config_dir + return os.path.join(os.getcwd(), "config") + +if __name__ == "__main__": + try: + abs_prefixer = Prefixer() + except Exception as e: + assert isinstance(e, TypeError), "Something went wrong" + print "Abstract Prefixer class is working as expected" + + linux_prefixer = LinuxPrefixer() + print linux_prefixer.get_path_prefix(standalone=True) + print linux_prefixer.get_path_prefix() diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/events/__init__.py b/debian/python-leap.common/usr/share/pyshared/leap/common/events/__init__.py new file mode 100644 index 0000000..c949080 --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/events/__init__.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +# __init__.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +An events mechanism that allows for signaling of events between components. +""" + +import logging +import socket + + +from leap.common.events import ( + events_pb2, + server, + component, + daemon, +) + + +logger = logging.getLogger(__name__) + + +def register(signal, callback, uid=None, replace=False, reqcbk=None, + timeout=1000): + """ + Register a callback to be called when the given signal is received. + + Will timeout after timeout ms if response has not been received. The + timeout arg is only used for asynch requests. If a reqcbk callback has + been supplied the timeout arg is not used. The response value will be + returned for a synch request but nothing will be returned for an asynch + request. + + @param signal: the signal that causes the callback to be launched + @type signal: int (see the `events.proto` file) + @param callback: the callback to be called when the signal is received + @type callback: function + @param uid: a unique id for the callback + @type uid: int + @param replace: should an existent callback with same uid be replaced? + @type replace: bool + @param reqcbk: a callback to be called when a response from server is + received + @type reqcbk: function + callback(leap.common.events.events_pb2.EventResponse) + @param timeout: the timeout for synch calls + @type timeout: int + + @return: the response from server for synch calls or nothing for asynch + calls + @rtype: leap.common.events.events_pb2.EventsResponse or None + """ + return component.register(signal, callback, uid, replace, reqcbk, timeout) + + +def signal(signal, content="", mac_method="", mac="", reqcbk=None, + timeout=1000): + """ + Send `signal` event to events server. + + Will timeout after timeout ms if response has not been received. The + timeout arg is only used for asynch requests. If a reqcbk callback has + been supplied the timeout arg is not used. The response value will be + returned for a synch request but nothing will be returned for an asynch + request. + + @param signal: the signal that causes the callback to be launched + @type signal: int (see the `events.proto` file) + @param content: the contents of the event signal + @type content: str + @param mac_method: the method used to auth mac + @type mac_method: str + @param mac: the content of the auth mac + @type mac: str + @param reqcbk: a callback to be called when a response from server is + received + @type reqcbk: function + callback(leap.common.events.events_pb2.EventResponse) + @param timeout: the timeout for synch calls + @type timeout: int + + @return: the response from server for synch calls or nothing for asynch + calls + @rtype: leap.common.events.events_pb2.EventsResponse or None + """ + return component.signal(signal, content, mac_method, mac, reqcbk, timeout) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/events/component.py b/debian/python-leap.common/usr/share/pyshared/leap/common/events/component.py new file mode 100644 index 0000000..0cf0e38 --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/events/component.py @@ -0,0 +1,238 @@ +# -*- coding: utf-8 -*- +# component.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +The component end point of the events mechanism. + +Components are the communicating parties of the events mechanism. They +communicate by sending messages to a server, which in turn redistributes +messages to other components. + +When a component registers a callback for a given signal, it also tells the +server that it wants to be notified whenever signals of that type are sent by +some other component. +""" + + +import logging +import threading + + +from protobuf.socketrpc import RpcService +from leap.common.events import ( + events_pb2 as proto, + server, + daemon, + mac_auth, +) + + +logger = logging.getLogger(__name__) + + +# the `registered_callbacks` dictionary below should have the following +# format: +# +# { event_signal: [ (uid, callback), ... ], ... } +# +registered_callbacks = {} + + +class CallbackAlreadyRegistered(Exception): + """ + Raised when trying to register an already registered callback. + """ + + +def ensure_component_daemon(): + """ + Ensure the component daemon is running and listening for incoming + messages. + + @return: the daemon instance + @rtype: EventsComponentDaemon + """ + import time + daemon = EventsComponentDaemon.ensure(0) + logger.debug('ensure component daemon') + + # Because we use a random port we want to wait until a port is assigned to + # local component daemon. + + while not (EventsComponentDaemon.get_instance() and + EventsComponentDaemon.get_instance().get_port()): + time.sleep(0.1) + return daemon + + +def register(signal, callback, uid=None, replace=False, reqcbk=None, + timeout=1000): + """ + Registers a callback to be called when a specific signal event is + received. + + Will timeout after timeout ms if response has not been received. The + timeout arg is only used for asynch requests. If a reqcbk callback has + been supplied the timeout arg is not used. The response value will be + returned for a synch request but nothing will be returned for an asynch + request. + + @param signal: the signal that causes the callback to be launched + @type signal: int (see the `events.proto` file) + @param callback: the callback to be called when the signal is received + @type callback: function + callback(leap.common.events.events_pb2.SignalRequest) + @param uid: a unique id for the callback + @type uid: int + @param replace: should an existent callback with same uid be replaced? + @type replace: bool + @param reqcbk: a callback to be called when a response from server is + received + @type reqcbk: function + callback(leap.common.events.events_pb2.EventResponse) + @param timeout: the timeout for synch calls + @type timeout: int + + Might raise a CallbackAlreadyRegistered exception if there's already a + callback identified by the given uid and replace is False. + + @return: the response from server for synch calls or nothing for asynch + calls + @rtype: leap.common.events.events_pb2.EventsResponse or None + """ + ensure_component_daemon() # so we can receive registered signals + # register callback locally + if signal not in registered_callbacks: + registered_callbacks[signal] = [] + cbklist = registered_callbacks[signal] + if uid and filter(lambda (x, y): x == uid, cbklist): + if not replace: + raise CallbackAlreadyRegisteredException() + else: + registered_callbacks[signal] = filter(lambda(x, y): x != uid, + cbklist) + registered_callbacks[signal].append((uid, callback)) + # register callback on server + request = proto.RegisterRequest() + request.event = signal + request.port = EventsComponentDaemon.get_instance().get_port() + request.mac_method = mac_auth.MacMethod.MAC_NONE + request.mac = "" + service = RpcService(proto.EventsServerService_Stub, + server.SERVER_PORT, 'localhost') + logger.info("Sending registration request to server on port %s: %s", + server.SERVER_PORT, + str(request)) + return service.register(request, callback=reqcbk, timeout=timeout) + + +def signal(signal, content="", mac_method="", mac="", reqcbk=None, + timeout=1000): + """ + Send `signal` event to events server. + + Will timeout after timeout ms if response has not been received. The + timeout arg is only used for asynch requests. If a reqcbk callback has + been supplied the timeout arg is not used. The response value will be + returned for a synch request but nothing will be returned for an asynch + request. + + @param signal: the signal that causes the callback to be launched + @type signal: int (see the `events.proto` file) + @param content: the contents of the event signal + @type content: str + @param mac_method: the method used for auth mac + @type mac_method: str + @param mac: the content of the auth mac + @type mac: str + @param reqcbk: a callback to be called when a response from server is + received + @type reqcbk: function + callback(leap.common.events.events_pb2.EventResponse) + @param timeout: the timeout for synch calls + @type timeout: int + + @return: the response from server for synch calls or nothing for asynch + calls + @rtype: leap.common.events.events_pb2.EventsResponse or None + """ + request = proto.SignalRequest() + request.event = signal + request.content = content + request.mac_method = mac_method + request.mac = mac + service = RpcService(proto.EventsServerService_Stub, server.SERVER_PORT, + 'localhost') + logger.info("Sending signal to server: %s", str(request)) + return service.signal(request, callback=reqcbk, timeout=timeout) + + +class EventsComponentService(proto.EventsComponentService): + """ + Service for receiving signal events in components. + """ + + def __init__(self): + proto.EventsComponentService.__init__(self) + + def signal(self, controller, request, done): + """ + Receive a signal and run callbacks registered for that signal. + + This method is called whenever a signal request is received from + server. + + @param controller: used to mediate a single method call + @type controller: protobuf.socketrpc.controller.SocketRpcController + @param request: the request received from the component + @type request: leap.common.events.events_pb2.SignalRequest + @param done: callback to be called when done + @type done: protobuf.socketrpc.server.Callback + """ + logger.info('Received signal from server: %s' % str(request)) + + # run registered callbacks + # TODO: verify authentication using mac in incoming message + if request.event in registered_callbacks: + for (_, cbk) in registered_callbacks[request.event]: + # callbacks should be prepared to receive a + # events_pb2.SignalRequest. + cbk(request) + + # send response back to server + response = proto.EventResponse() + response.status = proto.EventResponse.OK + done.run(response) + + +class EventsComponentDaemon(daemon.EventsSingletonDaemon): + """ + A daemon that listens for incoming events from server. + """ + + @classmethod + def ensure(cls, port): + """ + Make sure the daemon is running on the given port. + + @param port: the port in which the daemon should listen + @type port: int + + @return: a daemon instance + @rtype: EventsComponentDaemon + """ + return cls.ensure_service(port, EventsComponentService()) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/events/daemon.py b/debian/python-leap.common/usr/share/pyshared/leap/common/events/daemon.py new file mode 100644 index 0000000..d2c7b9b --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/events/daemon.py @@ -0,0 +1,208 @@ +# -*- coding: utf-8 -*- +# daemon.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +A singleton daemon for running RPC services using protobuf.socketrpc. +""" + + +import logging +import threading + + +from protobuf.socketrpc.server import ( + SocketRpcServer, + ThreadedTCPServer, + SocketHandler, +) + + +logger = logging.getLogger(__name__) + + +class ServiceAlreadyRunningException(Exception): + """ + Raised whenever a service is already running in this process but someone + attemped to start it in a different port. + """ + + +class EventsRpcServer(SocketRpcServer): + """ + RPC server used in server and component interfaces to receive messages. + """ + + def __init__(self, port, host='localhost'): + """ + Initialize a RPC server. + + @param port: the port in which to listen for incoming messages + @type port: int + @param host: the address to bind to + @type host: str + """ + SocketRpcServer.__init__(self, port, host) + self._server = None + + def run(self): + """ + Run the server. + """ + logger.info('Running server on port %d.' % self.port) + # parent implementation does not hold the server instance, so we do it + # here. + self._server = ThreadedTCPServer((self.host, self.port), + SocketHandler, self) + # if we chose to use a random port, fetch the port number info. + if self.port is 0: + self.port = self._server.socket.getsockname()[1] + self._server.serve_forever() + + def stop(self): + """ + Stop the server. + """ + self._server.shutdown() + + +class EventsSingletonDaemon(threading.Thread): + """ + Singleton class for for launching and terminating a daemon. + + This class is used so every part of the mechanism that needs to listen for + messages can launch its own daemon (thread) to do the job. + """ + + # Singleton instance + __instance = None + + def __new__(cls, *args, **kwargs): + """ + Return a singleton instance if it exists or create and initialize one. + """ + if len(args) is not 2: + raise TypeError("__init__() takes exactly 2 arguments (%d given)" + % len(args)) + if cls.__instance is None: + cls.__instance = object.__new__( + EventsSingletonDaemon) + cls.__initialize(cls.__instance, args[0], args[1]) + return cls.__instance + + @staticmethod + def __initialize(self, port, service): + """ + Initialize a singleton daemon. + + This is a static method disguised as instance method that actually + does the initialization of the daemon instance. + + @param port: the port in which to listen for incoming messages + @type port: int + @param service: the service to provide in this daemon + @type service: google.protobuf.service.Service + """ + threading.Thread.__init__(self) + self._port = port + self._service = service + self._server = EventsRpcServer(self._port) + self._server.registerService(self._service) + self.daemon = True + + def __init__(self): + """ + Singleton placeholder initialization method. + + Initialization is made in __new__ so we can always return the same + instance upon object creation. + """ + pass + + @classmethod + def ensure(cls, port): + """ + Make sure the daemon instance is running. + + Each implementation of this method should call `self.ensure_service` + with the appropriate service from the `events.proto` definitions, and + return the daemon instance. + + @param port: the port in which the daemon should be listening + @type port: int + + @return: a daemon instance + @rtype: EventsSingletonDaemon + """ + raise NotImplementedError(self.ensure) + + @classmethod + def ensure_service(cls, port, service): + """ + Start the singleton instance if not already running. + + Might return ServiceAlreadyRunningException + + @param port: the port in which the daemon should be listening + @type port: int + + @return: a daemon instance + @rtype: EventsSingletonDaemon + """ + daemon = cls(port, service) + if not daemon.is_alive(): + daemon.start() + elif port and port != cls.__instance._port: + # service is running in this process but someone is trying to + # start it in another port + raise ServiceAlreadyRunningException( + "Service is already running in this process on port %d." + % self.__instance._port) + return daemon + + @classmethod + def get_instance(cls): + """ + Retrieve singleton instance of this daemon. + + @return: a daemon instance + @rtype: EventsSingletonDaemon + """ + return cls.__instance + + def run(self): + """ + Run the server. + """ + self._server.run() + + def stop(self): + """ + Stop the daemon. + """ + self._server.stop() + + def get_port(self): + """ + Retrieve the value of the port to which the service running in this + daemon is binded to. + + @return: the port to which the daemon is binded to + @rtype: int + """ + if self._port is 0: + self._port = self._server.port + return self._port diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/events/events_pb2.py b/debian/python-leap.common/usr/share/pyshared/leap/common/events/events_pb2.py new file mode 100644 index 0000000..a4f1df4 --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/events/events_pb2.py @@ -0,0 +1,371 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! + +from google.protobuf import descriptor +from google.protobuf import message +from google.protobuf import reflection +from google.protobuf import service +from google.protobuf import service_reflection +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + + +DESCRIPTOR = descriptor.FileDescriptor( + name='events.proto', + package='leap.common.events', + serialized_pb='\n\x0c\x65vents.proto\x12\x12leap.common.events\"\x97\x01\n\rSignalRequest\x12(\n\x05\x65vent\x18\x01 \x02(\x0e\x32\x19.leap.common.events.Event\x12\x0f\n\x07\x63ontent\x18\x02 \x02(\t\x12\x12\n\nmac_method\x18\x03 \x02(\t\x12\x0b\n\x03mac\x18\x04 \x02(\x0c\x12\x12\n\nenc_method\x18\x05 \x01(\t\x12\x16\n\x0e\x65rror_occurred\x18\x06 \x01(\x08\"j\n\x0fRegisterRequest\x12(\n\x05\x65vent\x18\x01 \x02(\x0e\x32\x19.leap.common.events.Event\x12\x0c\n\x04port\x18\x02 \x02(\x05\x12\x12\n\nmac_method\x18\x03 \x02(\t\x12\x0b\n\x03mac\x18\x04 \x02(\x0c\"\x82\x01\n\rEventResponse\x12\x38\n\x06status\x18\x01 \x02(\x0e\x32(.leap.common.events.EventResponse.Status\x12\x0e\n\x06result\x18\x02 \x01(\t\"\'\n\x06Status\x12\x06\n\x02OK\x10\x01\x12\n\n\x06UNAUTH\x10\x02\x12\t\n\x05\x45RROR\x10\x03*\xe7\x02\n\x05\x45vent\x12\x15\n\x11\x43LIENT_SESSION_ID\x10\x01\x12\x0e\n\nCLIENT_UID\x10\x02\x12\x19\n\x15SOLEDAD_CREATING_KEYS\x10\x03\x12\x1e\n\x1aSOLEDAD_DONE_CREATING_KEYS\x10\x04\x12\x1a\n\x16SOLEDAD_UPLOADING_KEYS\x10\x05\x12\x1f\n\x1bSOLEDAD_DONE_UPLOADING_KEYS\x10\x06\x12\x1c\n\x18SOLEDAD_DOWNLOADING_KEYS\x10\x07\x12!\n\x1dSOLEDAD_DONE_DOWNLOADING_KEYS\x10\x08\x12\x1c\n\x18SOLEDAD_NEW_DATA_TO_SYNC\x10\t\x12\x1a\n\x16SOLEDAD_DONE_DATA_SYNC\x10\n\x12\x17\n\x13UPDATER_NEW_UPDATES\x10\x0b\x12\x19\n\x15UPDATER_DONE_UPDATING\x10\x0c\x12\x10\n\x0cRAISE_WINDOW\x10\r2\xb9\x01\n\x13\x45ventsServerService\x12R\n\x08register\x12#.leap.common.events.RegisterRequest\x1a!.leap.common.events.EventResponse\x12N\n\x06signal\x12!.leap.common.events.SignalRequest\x1a!.leap.common.events.EventResponse2h\n\x16\x45ventsComponentService\x12N\n\x06signal\x12!.leap.common.events.SignalRequest\x1a!.leap.common.events.EventResponseB\x03\x90\x01\x01') + +_EVENT = descriptor.EnumDescriptor( + name='Event', + full_name='leap.common.events.Event', + filename=None, + file=DESCRIPTOR, + values=[ + descriptor.EnumValueDescriptor( + name='CLIENT_SESSION_ID', index=0, number=1, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='CLIENT_UID', index=1, number=2, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='SOLEDAD_CREATING_KEYS', index=2, number=3, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='SOLEDAD_DONE_CREATING_KEYS', index=3, number=4, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='SOLEDAD_UPLOADING_KEYS', index=4, number=5, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='SOLEDAD_DONE_UPLOADING_KEYS', index=5, number=6, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='SOLEDAD_DOWNLOADING_KEYS', index=6, number=7, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='SOLEDAD_DONE_DOWNLOADING_KEYS', index=7, number=8, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='SOLEDAD_NEW_DATA_TO_SYNC', index=8, number=9, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='SOLEDAD_DONE_DATA_SYNC', index=9, number=10, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='UPDATER_NEW_UPDATES', index=10, number=11, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='UPDATER_DONE_UPDATING', index=11, number=12, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='RAISE_WINDOW', index=12, number=13, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=432, + serialized_end=791, +) + + +CLIENT_SESSION_ID = 1 +CLIENT_UID = 2 +SOLEDAD_CREATING_KEYS = 3 +SOLEDAD_DONE_CREATING_KEYS = 4 +SOLEDAD_UPLOADING_KEYS = 5 +SOLEDAD_DONE_UPLOADING_KEYS = 6 +SOLEDAD_DOWNLOADING_KEYS = 7 +SOLEDAD_DONE_DOWNLOADING_KEYS = 8 +SOLEDAD_NEW_DATA_TO_SYNC = 9 +SOLEDAD_DONE_DATA_SYNC = 10 +UPDATER_NEW_UPDATES = 11 +UPDATER_DONE_UPDATING = 12 +RAISE_WINDOW = 13 + + +_EVENTRESPONSE_STATUS = descriptor.EnumDescriptor( + name='Status', + full_name='leap.common.events.EventResponse.Status', + filename=None, + file=DESCRIPTOR, + values=[ + descriptor.EnumValueDescriptor( + name='OK', index=0, number=1, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='UNAUTH', index=1, number=2, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='ERROR', index=2, number=3, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=390, + serialized_end=429, +) + + +_SIGNALREQUEST = descriptor.Descriptor( + name='SignalRequest', + full_name='leap.common.events.SignalRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='event', full_name='leap.common.events.SignalRequest.event', index=0, + number=1, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=1, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='content', full_name='leap.common.events.SignalRequest.content', index=1, + number=2, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=unicode("", "utf-8"), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='mac_method', full_name='leap.common.events.SignalRequest.mac_method', index=2, + number=3, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=unicode("", "utf-8"), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='mac', full_name='leap.common.events.SignalRequest.mac', index=3, + number=4, type=12, cpp_type=9, label=2, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='enc_method', full_name='leap.common.events.SignalRequest.enc_method', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=unicode("", "utf-8"), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='error_occurred', full_name='leap.common.events.SignalRequest.error_occurred', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=37, + serialized_end=188, +) + + +_REGISTERREQUEST = descriptor.Descriptor( + name='RegisterRequest', + full_name='leap.common.events.RegisterRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='event', full_name='leap.common.events.RegisterRequest.event', index=0, + number=1, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=1, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='port', full_name='leap.common.events.RegisterRequest.port', index=1, + number=2, type=5, cpp_type=1, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='mac_method', full_name='leap.common.events.RegisterRequest.mac_method', index=2, + number=3, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=unicode("", "utf-8"), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='mac', full_name='leap.common.events.RegisterRequest.mac', index=3, + number=4, type=12, cpp_type=9, label=2, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=190, + serialized_end=296, +) + + +_EVENTRESPONSE = descriptor.Descriptor( + name='EventResponse', + full_name='leap.common.events.EventResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='status', full_name='leap.common.events.EventResponse.status', index=0, + number=1, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=1, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='result', full_name='leap.common.events.EventResponse.result', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=unicode("", "utf-8"), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _EVENTRESPONSE_STATUS, + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=299, + serialized_end=429, +) + +_SIGNALREQUEST.fields_by_name['event'].enum_type = _EVENT +_REGISTERREQUEST.fields_by_name['event'].enum_type = _EVENT +_EVENTRESPONSE.fields_by_name['status'].enum_type = _EVENTRESPONSE_STATUS +_EVENTRESPONSE_STATUS.containing_type = _EVENTRESPONSE; +DESCRIPTOR.message_types_by_name['SignalRequest'] = _SIGNALREQUEST +DESCRIPTOR.message_types_by_name['RegisterRequest'] = _REGISTERREQUEST +DESCRIPTOR.message_types_by_name['EventResponse'] = _EVENTRESPONSE + + +class SignalRequest(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _SIGNALREQUEST + + # @@protoc_insertion_point(class_scope:leap.common.events.SignalRequest) + + +class RegisterRequest(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _REGISTERREQUEST + + # @@protoc_insertion_point(class_scope:leap.common.events.RegisterRequest) + + +class EventResponse(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _EVENTRESPONSE + + # @@protoc_insertion_point(class_scope:leap.common.events.EventResponse) + + +_EVENTSSERVERSERVICE = descriptor.ServiceDescriptor( + name='EventsServerService', + full_name='leap.common.events.EventsServerService', + file=DESCRIPTOR, + index=0, + options=None, + serialized_start=794, + serialized_end=979, + methods=[ + descriptor.MethodDescriptor( + name='register', + full_name='leap.common.events.EventsServerService.register', + index=0, + containing_service=None, + input_type=_REGISTERREQUEST, + output_type=_EVENTRESPONSE, + options=None, + ), + descriptor.MethodDescriptor( + name='signal', + full_name='leap.common.events.EventsServerService.signal', + index=1, + containing_service=None, + input_type=_SIGNALREQUEST, + output_type=_EVENTRESPONSE, + options=None, + ), +]) + + +class EventsServerService(service.Service): + __metaclass__ = service_reflection.GeneratedServiceType + DESCRIPTOR = _EVENTSSERVERSERVICE + + +class EventsServerService_Stub(EventsServerService): + __metaclass__ = service_reflection.GeneratedServiceStubType + DESCRIPTOR = _EVENTSSERVERSERVICE + + +_EVENTSCOMPONENTSERVICE = descriptor.ServiceDescriptor( + name='EventsComponentService', + full_name='leap.common.events.EventsComponentService', + file=DESCRIPTOR, + index=1, + options=None, + serialized_start=981, + serialized_end=1085, + methods=[ + descriptor.MethodDescriptor( + name='signal', + full_name='leap.common.events.EventsComponentService.signal', + index=0, + containing_service=None, + input_type=_SIGNALREQUEST, + output_type=_EVENTRESPONSE, + options=None, + ), +]) + + +class EventsComponentService(service.Service): + __metaclass__ = service_reflection.GeneratedServiceType + DESCRIPTOR = _EVENTSCOMPONENTSERVICE + + +class EventsComponentService_Stub(EventsComponentService): + __metaclass__ = service_reflection.GeneratedServiceStubType + DESCRIPTOR = _EVENTSCOMPONENTSERVICE + +# @@protoc_insertion_point(module_scope) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/events/mac_auth.py b/debian/python-leap.common/usr/share/pyshared/leap/common/events/mac_auth.py new file mode 100644 index 0000000..49d48f7 --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/events/mac_auth.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# mac_auth.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Authentication system for events. + +This is not implemented yet. +""" + + +class MacMethod(object): + """ + Representation of possible MAC authentication methods. + """ + + MAC_NONE = 'none' + MAC_HMAC = 'hmac' diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/events/server.py b/debian/python-leap.common/usr/share/pyshared/leap/common/events/server.py new file mode 100644 index 0000000..16c6513 --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/events/server.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- +# server.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +A server for the events mechanism. + +A server can receive different kinds of requests from components: + + 1. Registration request: store component port number to be notified when + a specific signal arrives. + + 2. Signal request: redistribute the signal to registered components. +""" +import logging +import socket + + +from protobuf.socketrpc import RpcService +from leap.common.events import ( + events_pb2 as proto, + daemon, +) + + +logger = logging.getLogger(__name__) + + +SERVER_PORT = 8090 + +# the `registered_components` dictionary below should have the following +# format: +# +# { event_signal: [ port, ... ], ... } +# +registered_components = {} + + +def ensure_server(port=SERVER_PORT): + """ + Make sure the server is running on the given port. + + Attempt to connect to given local port. Upon success, assume that the + events server has already been started. Upon failure, start events server. + + @param port: the port in which server should be listening + @type port: int + + @return: the daemon instance or nothing + @rtype: EventsServerDaemon or None + """ + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect(('localhost', port)) + s.close() + logger.info('Server is already running on port %d.', port) + return None + except socket.error: + logger.info('Launching server on port %d.', port) + return EventsServerDaemon.ensure(port) + + +class EventsServerService(proto.EventsServerService): + """ + Service for receiving events in components. + """ + + def register(self, controller, request, done): + """ + Register a component port to be signaled when specific events come in. + + @param controller: used to mediate a single method call + @type controller: protobuf.socketrpc.controller.SocketRpcController + @param request: the request received from the component + @type request: leap.common.events.events_pb2.RegisterRequest + @param done: callback to be called when done + @type done: protobuf.socketrpc.server.Callback + """ + logger.info("Received registration request: %s" % str(request)) + # add component port to signal list + if request.event not in registered_components: + registered_components[request.event] = set([]) + registered_components[request.event].add(request.port) + # send response back to component + + logger.debug('sending response back') + response = proto.EventResponse() + response.status = proto.EventResponse.OK + done.run(response) + + def signal(self, controller, request, done): + """ + Perform an RPC call to signal all components registered to receive a + specific signal. + + @param controller: used to mediate a single method call + @type controller: protobuf.socketrpc.controller.SocketRpcController + @param request: the request received from the component + @type request: leap.common.events.events_pb2.SignalRequest + @param done: callback to be called when done + @type done: protobuf.socketrpc.server.Callback + """ + logger.info('Received signal from component: %s', str(request)) + # send signal to all registered components + # TODO: verify signal auth + if request.event in registered_components: + for port in registered_components[request.event]: + + def callback(req, resp): + logger.info("Signal received by " + str(port)) + + service = RpcService(proto.EventsComponentService_Stub, + port, 'localhost') + service.signal(request, callback=callback) + # send response back to component + response = proto.EventResponse() + response.status = proto.EventResponse.OK + done.run(response) + + +class EventsServerDaemon(daemon.EventsSingletonDaemon): + """ + Singleton class for starting an events server daemon. + """ + + @classmethod + def ensure(cls, port): + """ + Make sure the daemon is running on the given port. + + @param port: the port in which the daemon should listen + @type port: int + + @return: a daemon instance + @rtype: EventsServerDaemon + """ + return cls.ensure_service(port, EventsServerService()) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/files.py b/debian/python-leap.common/usr/share/pyshared/leap/common/files.py new file mode 100644 index 0000000..4c443dd --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/files.py @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- +# files.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Implements file helper methods +""" + +import errno +import logging +import os +import stat +import time + +logger = logging.getLogger(__name__) + + +def check_and_fix_urw_only(cert): + """ + Test for 600 mode and try to set it if anything different found + + Might raise OSError + + @param cert: Certificate path + @type cert: str + """ + mode = stat.S_IMODE(os.stat(cert).st_mode) + + if mode != int('600', 8): + try: + logger.warning('Bad permission on %s attempting to set 600' % + (cert,)) + os.chmod(cert, stat.S_IRUSR | stat.S_IWUSR) + except OSError: + logger.error('Error while trying to chmod 600 %s' % + cert) + raise + + +def get_mtime(filename): + """ + Returns the modified time or None if the file doesn't exist + + @param filename: path to check + @type filename: str + + @rtype: str + """ + try: + mtime = time.ctime(os.path.getmtime(filename)) + " GMT" + return mtime + except OSError: + return None + + +def mkdir_p(path): + """ + Creates the path and all the intermediate directories that don't + exist + + Might raise OSError + + @param path: path to create + @type path: str + """ + try: + os.makedirs(path) + except OSError as exc: + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: + raise + + +# Twisted implementation of which +def which(name, flags=os.X_OK, path_extension="/usr/sbin:/sbin"): + """ + Search PATH for executable files with the given name. + + On newer versions of MS-Windows, the PATHEXT environment variable will be + set to the list of file extensions for files considered executable. This + will normally include things like ".EXE". This fuction will also find files + with the given name ending with any of these extensions. + + On MS-Windows the only flag that has any meaning is os.F_OK. Any other + flags will be ignored. + + @type name: C{str} + @param name: The name for which to search. + + @type flags: C{int} + @param flags: Arguments to L{os.access}. + + @rtype: C{list} + @param: A list of the full paths to files found, in the + order in which they were found. + """ + + result = [] + exts = filter(None, os.environ.get('PATHEXT', '').split(os.pathsep)) + path = os.environ.get('PATH', None) + path = path_extension + os.pathsep + path + if path is None: + return [] + parts = path.split(os.pathsep) + for p in parts: + p = os.path.join(p, name) + if os.access(p, flags): + result.append(p) + for e in exts: + pext = p + e + if os.access(pext, flags): + result.append(pext) + return result diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/__init__.py b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/__init__.py new file mode 100644 index 0000000..d6dbb8a --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/__init__.py @@ -0,0 +1,286 @@ +# -*- coding: utf-8 -*- +# __init__.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +""" +Key Manager is a Nicknym agent for LEAP client. +""" + +import requests + +try: + import simplejson as json +except ImportError: + import json # noqa + +from leap.common.check import leap_assert +from leap.common.keymanager.errors import ( + KeyNotFound, + NoPasswordGiven, +) +from leap.common.keymanager.keys import ( + build_key_from_dict, +) +from leap.common.keymanager.openpgp import ( + OpenPGPKey, + OpenPGPScheme, + encrypt_sym, +) + + +TAGS_INDEX = 'by-tags' +TAGS_AND_PRIVATE_INDEX = 'by-tags-and-private' +INDEXES = { + TAGS_INDEX: ['tags'], + TAGS_AND_PRIVATE_INDEX: ['tags', 'bool(private)'], +} + + +class KeyManager(object): + + def __init__(self, address, nickserver_url, soledad, token=None): + """ + Initialize a Key Manager for user's C{address} with provider's + nickserver reachable in C{url}. + + @param address: The address of the user of this Key Manager. + @type address: str + @param url: The URL of the nickserver. + @type url: str + @param soledad: A Soledad instance for local storage of keys. + @type soledad: leap.soledad.Soledad + """ + self._address = address + self._nickserver_url = nickserver_url + self._soledad = soledad + self.token = token + self._wrapper_map = { + OpenPGPKey: OpenPGPScheme(soledad), + # other types of key will be added to this mapper. + } + self._init_indexes() + self._fetcher = requests + + # + # utilities + # + + def _key_class_from_type(self, ktype): + """ + Return key class from string representation of key type. + """ + return filter( + lambda klass: str(klass) == ktype, + self._wrapper_map).pop() + + def _init_indexes(self): + """ + Initialize the database indexes. + """ + # Ask the database for currently existing indexes. + db_indexes = dict(self._soledad.list_indexes()) + # Loop through the indexes we expect to find. + for name, expression in INDEXES.items(): + if name not in db_indexes: + # The index does not yet exist. + self._soledad.create_index(name, *expression) + continue + if expression == db_indexes[name]: + # The index exists and is up to date. + continue + # The index exists but the definition is not what expected, so we + # delete it and add the proper index expression. + self._soledad.delete_index(name) + self._soledad.create_index(name, *expression) + + def _get_dict_from_http_json(self, path): + """ + Make a GET HTTP request and return a dictionary containing the + response. + """ + response = self._fetcher.get(self._nickserver_url+path) + leap_assert(response.status_code == 200, 'Invalid response.') + leap_assert( + response.headers['content-type'].startswith('application/json') + is True, + 'Content-type is not JSON.') + return response.json() + + # + # key management + # + + def send_key(self, ktype, send_private=False, password=None): + """ + Send user's key of type C{ktype} to provider. + + Public key bound to user's is sent to provider, which will sign it and + replace any prior keys for the same address in its database. + + If C{send_private} is True, then the private key is encrypted with + C{password} and sent to server in the same request, together with a + hash string of user's address and password. The encrypted private key + will be saved in the server in a way it is publicly retrievable + through the hash string. + + @param ktype: The type of the key. + @type ktype: KeyType + + @raise httplib.HTTPException: + @raise KeyNotFound: If the key was not found both locally and in + keyserver. + """ + # prepare the public key bound to address + pubkey = self.get_key( + self._address, ktype, private=False, fetch_remote=False) + data = { + 'address': self._address, + 'keys': [ + json.loads(pubkey.get_json()), + ] + } + # prepare the private key bound to address + if send_private: + if password is None or password == '': + raise NoPasswordGiven('Can\'t send unencrypted private keys!') + privkey = self.get_key( + self._address, ktype, private=True, fetch_remote=False) + privkey = json.loads(privkey.get_json()) + privkey.key_data = encrypt_sym(privkey.key_data, password) + data['keys'].append(privkey) + self._fetcher.put( + self._nickserver_url + '/key/' + self._address, + data=data, + auth=(self._address, self._token)) + + def get_key(self, address, ktype, private=False, fetch_remote=True): + """ + Return a key of type C{ktype} bound to C{address}. + + First, search for the key in local storage. If it is not available, + then try to fetch from nickserver. + + @param address: The address bound to the key. + @type address: str + @param ktype: The type of the key. + @type ktype: KeyType + @param private: Look for a private key instead of a public one? + @type private: bool + + @return: A key of type C{ktype} bound to C{address}. + @rtype: EncryptionKey + @raise KeyNotFound: If the key was not found both locally and in + keyserver. + """ + leap_assert( + ktype in self._wrapper_map, + 'Unkown key type: %s.' % str(ktype)) + try: + return self._wrapper_map[ktype].get_key(address, private=private) + except KeyNotFound: + if fetch_remote is False: + raise + # fetch keys from server and discard unwanted types. + keys = filter(lambda k: isinstance(k, ktype), + self.fetch_keys_from_server(address)) + if len(keys) is 0: + raise KeyNotFound() + leap_assert( + len(keys) == 1, + 'Got more than one key of type %s for %s.' % + (str(ktype), address)) + self._wrapper_map[ktype].put_key(keys[0]) + return self._wrapper_map[ktype].get_key(address, private=private) + + def fetch_keys_from_server(self, address): + """ + Fetch keys bound to C{address} from nickserver. + + @param address: The address bound to the keys. + @type address: str + + @return: A list of keys bound to C{address}. + @rtype: list of EncryptionKey + @raise KeyNotFound: If the key was not found on nickserver. + @raise httplib.HTTPException: + """ + keydata = self._get_dict_from_http_json('/key/%s' % address) + leap_assert( + keydata['address'] == address, + "Fetched key for wrong address.") + keys = [] + for key in keydata['keys']: + keys.append( + build_key_from_dict( + self._key_class_from_type(key['type']), + address, + key)) + return keys + + def get_all_keys_in_local_db(self, private=False): + """ + Return all keys stored in local database. + + @return: A list with all keys in local db. + @rtype: list + """ + return map( + lambda doc: build_key_from_dict( + self._key_class_from_type(doc.content['type']), + doc.content['address'], + doc.content), + self._soledad.get_from_index( + TAGS_AND_PRIVATE_INDEX, + 'keymanager-key', + '1' if private else '0')) + + def refresh_keys(self): + """ + Fetch keys from nickserver and update them locally. + """ + addresses = set(map( + lambda doc: doc.address, + self.get_all_keys_in_local_db(private=False))) + # TODO: maybe we should not attempt to refresh our own public key? + for address in addresses: + for key in self.fetch_keys_from_server(address): + self._wrapper_map[key.__class__].put_key(key) + + def gen_key(self, ktype): + """ + Generate a key of type C{ktype} bound to the user's address. + + @param ktype: The type of the key. + @type ktype: KeyType + + @return: The generated key. + @rtype: EncryptionKey + """ + return self._wrapper_map[ktype].gen_key(self._address) + + # + # Token setter/getter + # + + def _get_token(self): + return self._token + + def _set_token(self, token): + self._token = token + + token = property( + _get_token, _set_token, doc='The auth token.') diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/errors.py b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/errors.py new file mode 100644 index 0000000..1cf506e --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/errors.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# errors.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +""" +Errors and exceptions used by the Key Manager. +""" + + +class KeyNotFound(Exception): + """ + Raised when key was no found on keyserver. + """ + + +class KeyAlreadyExists(Exception): + """ + Raised when attempted to create a key that already exists. + """ + + +class KeyAttributesDiffer(Exception): + """ + Raised when trying to delete a key but the stored key differs from the key + passed to the delete_key() method. + """ + +class NoPasswordGiven(Exception): + """ + Raised when trying to perform some action that needs a password without + providing one. + """ diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/gpg.py b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/gpg.py new file mode 100644 index 0000000..f3e6453 --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/gpg.py @@ -0,0 +1,397 @@ +# -*- coding: utf-8 -*- +# gpgwrapper.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +""" +A GPG wrapper used to handle OpenPGP keys. + +This is a temporary class that will be superseded by the a revised version of +python-gnupg. +""" + + +import os +import gnupg +import re +from gnupg import ( + logger, + _is_sequence, + _make_binary_stream, +) + + +class ListPackets(): + """ + Handle status messages for --list-packets. + """ + + def __init__(self, gpg): + """ + Initialize the packet listing handling class. + + @param gpg: GPG object instance. + @type gpg: gnupg.GPG + """ + self.gpg = gpg + self.nodata = None + self.key = None + self.need_passphrase = None + self.need_passphrase_sym = None + self.userid_hint = None + + def handle_status(self, key, value): + """ + Handle one line of the --list-packets status message. + + @param key: The status message key. + @type key: str + @param value: The status message value. + @type value: str + """ + # TODO: write tests for handle_status + if key == 'NODATA': + self.nodata = True + if key == 'ENC_TO': + # This will only capture keys in our keyring. In the future we + # may want to include multiple unknown keys in this list. + self.key, _, _ = value.split() + if key == 'NEED_PASSPHRASE': + self.need_passphrase = True + if key == 'NEED_PASSPHRASE_SYM': + self.need_passphrase_sym = True + if key == 'USERID_HINT': + self.userid_hint = value.strip().split() + + +class GPGWrapper(gnupg.GPG): + """ + This is a temporary class for handling GPG requests, and should be + replaced by a more general class used throughout the project. + """ + + GNUPG_HOME = os.environ['HOME'] + "/.config/leap/gnupg" + GNUPG_BINARY = "/usr/bin/gpg" # this has to be changed based on OS + + def __init__(self, gpgbinary=GNUPG_BINARY, gnupghome=GNUPG_HOME, + verbose=False, use_agent=False, keyring=None, options=None): + """ + Initialize a GnuPG process wrapper. + + @param gpgbinary: Name for GnuPG binary executable. + @type gpgbinary: C{str} + @param gpghome: Full pathname to directory containing the public and + private keyrings. + @type gpghome: C{str} + @param keyring: Name of alternative keyring file to use. If specified, + the default keyring is not used. + @param verbose: Should some verbose info be output? + @type verbose: bool + @param use_agent: Should pass `--use-agent` to GPG binary? + @type use_agent: bool + @param keyring: Path for the keyring to use. + @type keyring: str + @options: A list of additional options to pass to the GPG binary. + @type options: list + + @raise: RuntimeError with explanation message if there is a problem + invoking gpg. + """ + gnupg.GPG.__init__(self, gnupghome=gnupghome, gpgbinary=gpgbinary, + verbose=verbose, use_agent=use_agent, + keyring=keyring, options=options) + self.result_map['list-packets'] = ListPackets + + def find_key_by_email(self, email, secret=False): + """ + Find user's key based on their email. + + @param email: Email address of key being searched for. + @type email: str + @param secret: Should we search for a secret key? + @type secret: bool + + @return: The fingerprint of the found key. + @rtype: str + """ + for key in self.list_keys(secret=secret): + for uid in key['uids']: + if re.search(email, uid): + return key + raise LookupError("GnuPG public key for email %s not found!" % email) + + def find_key_by_subkey(self, subkey, secret=False): + """ + Find user's key based on a subkey fingerprint. + + @param email: Subkey fingerprint of the key being searched for. + @type email: str + @param secret: Should we search for a secret key? + @type secret: bool + + @return: The fingerprint of the found key. + @rtype: str + """ + for key in self.list_keys(secret=secret): + for sub in key['subkeys']: + if sub[0] == subkey: + return key + raise LookupError( + "GnuPG public key for subkey %s not found!" % subkey) + + def find_key_by_keyid(self, keyid, secret=False): + """ + Find user's key based on the key ID. + + @param email: The key ID of the key being searched for. + @type email: str + @param secret: Should we search for a secret key? + @type secret: bool + + @return: The fingerprint of the found key. + @rtype: str + """ + for key in self.list_keys(secret=secret): + if keyid == key['keyid']: + return key + raise LookupError( + "GnuPG public key for keyid %s not found!" % keyid) + + def find_key_by_fingerprint(self, fingerprint, secret=False): + """ + Find user's key based on the key fingerprint. + + @param email: The fingerprint of the key being searched for. + @type email: str + @param secret: Should we search for a secret key? + @type secret: bool + + @return: The fingerprint of the found key. + @rtype: str + """ + for key in self.list_keys(secret=secret): + if fingerprint == key['fingerprint']: + return key + raise LookupError( + "GnuPG public key for fingerprint %s not found!" % fingerprint) + + def encrypt(self, data, recipient, sign=None, always_trust=True, + passphrase=None, symmetric=False): + """ + Encrypt data using GPG. + + @param data: The data to be encrypted. + @type data: str + @param recipient: The address of the public key to be used. + @type recipient: str + @param sign: Should the encrypted content be signed? + @type sign: bool + @param always_trust: Skip key validation and assume that used keys + are always fully trusted? + @type always_trust: bool + @param passphrase: The passphrase to be used if symmetric encryption + is desired. + @type passphrase: str + @param symmetric: Should we encrypt to a password? + @type symmetric: bool + + @return: An object with encrypted result in the `data` field. + @rtype: gnupg.Crypt + """ + # TODO: devise a way so we don't need to "always trust". + return gnupg.GPG.encrypt(self, data, recipient, sign=sign, + always_trust=always_trust, + passphrase=passphrase, + symmetric=symmetric, + cipher_algo='AES256') + + def decrypt(self, data, always_trust=True, passphrase=None): + """ + Decrypt data using GPG. + + @param data: The data to be decrypted. + @type data: str + @param always_trust: Skip key validation and assume that used keys + are always fully trusted? + @type always_trust: bool + @param passphrase: The passphrase to be used if symmetric encryption + is desired. + @type passphrase: str + + @return: An object with decrypted result in the `data` field. + @rtype: gnupg.Crypt + """ + # TODO: devise a way so we don't need to "always trust". + return gnupg.GPG.decrypt(self, data, always_trust=always_trust, + passphrase=passphrase) + + def send_keys(self, keyserver, *keyids): + """ + Send keys to a keyserver + + @param keyserver: The keyserver to send the keys to. + @type keyserver: str + @param keyids: The key ids to send. + @type keyids: list + + @return: A list of keys sent to server. + @rtype: gnupg.ListKeys + """ + # TODO: write tests for this. + # TODO: write a SendKeys class to handle status for this. + result = self.result_map['list'](self) + gnupg.logger.debug('send_keys: %r', keyids) + data = gnupg._make_binary_stream("", self.encoding) + args = ['--keyserver', keyserver, '--send-keys'] + args.extend(keyids) + self._handle_io(args, data, result, binary=True) + gnupg.logger.debug('send_keys result: %r', result.__dict__) + data.close() + return result + + def encrypt_file(self, file, recipients, sign=None, + always_trust=False, passphrase=None, + armor=True, output=None, symmetric=False, + cipher_algo=None): + """ + Encrypt the message read from the file-like object 'file'. + + @param file: The file to be encrypted. + @type data: file + @param recipient: The address of the public key to be used. + @type recipient: str + @param sign: Should the encrypted content be signed? + @type sign: bool + @param always_trust: Skip key validation and assume that used keys + are always fully trusted? + @type always_trust: bool + @param passphrase: The passphrase to be used if symmetric encryption + is desired. + @type passphrase: str + @param armor: Create ASCII armored output? + @type armor: bool + @param output: Path of file to write results in. + @type output: str + @param symmetric: Should we encrypt to a password? + @type symmetric: bool + @param cipher_algo: Algorithm to use. + @type cipher_algo: str + + @return: An object with encrypted result in the `data` field. + @rtype: gnupg.Crypt + """ + args = ['--encrypt'] + if symmetric: + args = ['--symmetric'] + if cipher_algo: + args.append('--cipher-algo %s' % cipher_algo) + else: + args = ['--encrypt'] + if not _is_sequence(recipients): + recipients = (recipients,) + for recipient in recipients: + args.append('--recipient "%s"' % recipient) + if armor: # create ascii-armored output - set to False for binary + args.append('--armor') + if output: # write the output to a file with the specified name + if os.path.exists(output): + os.remove(output) # to avoid overwrite confirmation message + args.append('--output "%s"' % output) + if sign: + args.append('--sign --default-key "%s"' % sign) + if always_trust: + args.append("--always-trust") + result = self.result_map['crypt'](self) + self._handle_io(args, file, result, passphrase=passphrase, binary=True) + logger.debug('encrypt result: %r', result.data) + return result + + def list_packets(self, data): + """ + List the sequence of packets. + + @param data: The data to extract packets from. + @type data: str + + @return: An object with packet info. + @rtype ListPackets + """ + args = ["--list-packets"] + result = self.result_map['list-packets'](self) + self._handle_io( + args, + _make_binary_stream(data, self.encoding), + result, + ) + return result + + def encrypted_to(self, data): + """ + Return the key to which data is encrypted to. + + @param data: The data to be examined. + @type data: str + + @return: The fingerprint of the key to which data is encrypted to. + @rtype: str + """ + # TODO: make this support multiple keys. + result = self.list_packets(data) + if not result.key: + raise LookupError( + "Content is not encrypted to a GnuPG key!") + try: + return self.find_key_by_keyid(result.key) + except: + return self.find_key_by_subkey(result.key) + + def is_encrypted_sym(self, data): + """ + Say whether some chunk of data is encrypted to a symmetric key. + + @param data: The data to be examined. + @type data: str + + @return: Whether data is encrypted to a symmetric key. + @rtype: bool + """ + result = self.list_packets(data) + return bool(result.need_passphrase_sym) + + def is_encrypted_asym(self, data): + """ + Say whether some chunk of data is encrypted to a private key. + + @param data: The data to be examined. + @type data: str + + @return: Whether data is encrypted to a private key. + @rtype: bool + """ + result = self.list_packets(data) + return bool(result.key) + + def is_encrypted(self, data): + """ + Say whether some chunk of data is encrypted to a key. + + @param data: The data to be examined. + @type data: str + + @return: Whether data is encrypted to a key. + @rtype: bool + """ + return self.is_encrypted_asym(data) or self.is_encrypted_sym(data) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/keys.py b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/keys.py new file mode 100644 index 0000000..2e6bfe9 --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/keys.py @@ -0,0 +1,230 @@ +# -*- coding: utf-8 -*- +# keys.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +""" +Abstact key type and encryption scheme representations. +""" + + +try: + import simplejson as json +except ImportError: + import json # noqa +import re + + +from hashlib import sha256 +from abc import ABCMeta, abstractmethod +from leap.common.check import leap_assert + + +# +# Key handling utilities +# + +def is_address(address): + """ + Return whether the given C{address} is in the form user@provider. + + @param address: The address to be tested. + @type address: str + @return: Whether C{address} is in the form user@provider. + @rtype: bool + """ + return bool(re.match('[\w.-]+@[\w.-]+', address)) + + +def build_key_from_dict(kClass, address, kdict): + """ + Build an C{kClass} key bound to C{address} based on info in C{kdict}. + + @param address: The address bound to the key. + @type address: str + @param kdict: Dictionary with key data. + @type kdict: dict + @return: An instance of the key. + @rtype: C{kClass} + """ + leap_assert(address == kdict['address'], 'Wrong address in key data.') + return kClass( + address, + key_id=kdict['key_id'], + fingerprint=kdict['fingerprint'], + key_data=kdict['key_data'], + private=kdict['private'], + length=kdict['length'], + expiry_date=kdict['expiry_date'], + first_seen_at=kdict['first_seen_at'], + last_audited_at=kdict['last_audited_at'], + validation=kdict['validation'], # TODO: verify for validation. + ) + + +def keymanager_doc_id(ktype, address, private=False): + """ + Return the document id for the document containing a key for + C{address}. + + @param address: The type of the key. + @type address: KeyType + @param address: The address bound to the key. + @type address: str + @param private: Whether the key is private or not. + @type private: bool + @return: The document id for the document that stores a key bound to + C{address}. + @rtype: str + """ + leap_assert(is_address(address), "Wrong address format: %s" % address) + ktype = str(ktype) + visibility = 'private' if private else 'public' + return sha256('keymanager-'+address+'-'+ktype+'-'+visibility).hexdigest() + + +# +# Abstraction for encryption keys +# + +class EncryptionKey(object): + """ + Abstract class for encryption keys. + + A key is "validated" if the nicknym agent has bound the user address to a + public key. Nicknym supports three different levels of key validation: + + * Level 3 - path trusted: A path of cryptographic signatures can be traced + from a trusted key to the key under evaluation. By default, only the + provider key from the user's provider is a "trusted key". + * level 2 - provider signed: The key has been signed by a provider key for + the same domain, but the provider key is not validated using a trust + path (i.e. it is only registered) + * level 1 - registered: The key has been encountered and saved, it has no + signatures (that are meaningful to the nicknym agent). + """ + + __metaclass__ = ABCMeta + + def __init__(self, address, key_id=None, fingerprint=None, + key_data=None, private=None, length=None, expiry_date=None, + validation=None, first_seen_at=None, last_audited_at=None): + self.address = address + self.key_id = key_id + self.fingerprint = fingerprint + self.key_data = key_data + self.private = private + self.length = length + self.expiry_date = expiry_date + self.validation = validation + self.first_seen_at = first_seen_at + self.last_audited_at = last_audited_at + + def get_json(self): + """ + Return a JSON string describing this key. + + @return: The JSON string describing this key. + @rtype: str + """ + return json.dumps({ + 'address': self.address, + 'type': str(self.__class__), + 'key_id': self.key_id, + 'fingerprint': self.fingerprint, + 'key_data': self.key_data, + 'private': self.private, + 'length': self.length, + 'expiry_date': self.expiry_date, + 'validation': self.validation, + 'first_seen_at': self.first_seen_at, + 'last_audited_at': self.last_audited_at, + 'tags': ['keymanager-key'], + }) + + +# +# Encryption schemes +# + +class EncryptionScheme(object): + """ + Abstract class for Encryption Schemes. + + A wrapper for a certain encryption schemes should know how to get and put + keys in local storage using Soledad, how to generate new keys and how to + find out about possibly encrypted content. + """ + + __metaclass__ = ABCMeta + + def __init__(self, soledad): + """ + Initialize this Encryption Scheme. + + @param soledad: A Soledad instance for local storage of keys. + @type soledad: leap.soledad.Soledad + """ + self._soledad = soledad + + @abstractmethod + def get_key(self, address, private=False): + """ + Get key from local storage. + + @param address: The address bound to the key. + @type address: str + @param private: Look for a private key instead of a public one? + @type private: bool + + @return: The key bound to C{address}. + @rtype: EncryptionKey + @raise KeyNotFound: If the key was not found on local storage. + """ + pass + + @abstractmethod + def put_key(self, key): + """ + Put a key in local storage. + + @param key: The key to be stored. + @type key: EncryptionKey + """ + pass + + @abstractmethod + def gen_key(self, address): + """ + Generate a new key. + + @param address: The address bound to the key. + @type address: str + + @return: The key bound to C{address}. + @rtype: EncryptionKey + """ + pass + + @abstractmethod + def delete_key(self, key): + """ + Remove C{key} from storage. + + @param key: The key to be removed. + @type key: EncryptionKey + """ + pass diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/openpgp.py b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/openpgp.py new file mode 100644 index 0000000..e2ffe76 --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/openpgp.py @@ -0,0 +1,459 @@ +# -*- coding: utf-8 -*- +# openpgp.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +""" +Infrastructure for using OpenPGP keys in Key Manager. +""" + + +import re +import tempfile +import shutil + +from leap.common.check import leap_assert +from leap.common.keymanager.errors import ( + KeyNotFound, + KeyAlreadyExists, + KeyAttributesDiffer +) +from leap.common.keymanager.keys import ( + EncryptionKey, + EncryptionScheme, + is_address, + keymanager_doc_id, + build_key_from_dict, +) +from leap.common.keymanager.gpg import GPGWrapper + + +# +# Utility functions +# + +def encrypt_sym(data, passphrase): + """ + Encrypt C{data} with C{passphrase}. + + @param data: The data to be encrypted. + @type data: str + @param passphrase: The passphrase used to encrypt C{data}. + @type passphrase: str + + @return: The encrypted data. + @rtype: str + """ + + def _encrypt_cb(gpg): + return gpg.encrypt( + data, None, passphrase=passphrase, symmetric=True).data + + return _safe_call(_encrypt_cb) + + +def decrypt_sym(data, passphrase): + """ + Decrypt C{data} with C{passphrase}. + + @param data: The data to be decrypted. + @type data: str + @param passphrase: The passphrase used to decrypt C{data}. + @type passphrase: str + + @return: The decrypted data. + @rtype: str + """ + + def _decrypt_cb(gpg): + return gpg.decrypt(data, passphrase=passphrase).data + + return _safe_call(_decrypt_cb) + + +def encrypt_asym(data, key): + """ + Encrypt C{data} using public @{key}. + + @param data: The data to be encrypted. + @type data: str + @param key: The key used to encrypt. + @type key: OpenPGPKey + + @return: The encrypted data. + @rtype: str + """ + leap_assert(key.private is False, 'Key is not public.') + + def _encrypt_cb(gpg): + return gpg.encrypt( + data, key.fingerprint, symmetric=False).data + + return _safe_call(_encrypt_cb, key.key_data) + + +def decrypt_asym(data, key): + """ + Decrypt C{data} using private @{key}. + + @param data: The data to be decrypted. + @type data: str + @param key: The key used to decrypt. + @type key: OpenPGPKey + + @return: The decrypted data. + @rtype: str + """ + leap_assert(key.private is True, 'Key is not private.') + + def _decrypt_cb(gpg): + return gpg.decrypt(data).data + + return _safe_call(_decrypt_cb, key.key_data) + + +def is_encrypted(data): + """ + Return whether C{data} was encrypted using OpenPGP. + + @param data: The data we want to know about. + @type data: str + + @return: Whether C{data} was encrypted using this wrapper. + @rtype: bool + """ + + def _is_encrypted_cb(gpg): + return gpg.is_encrypted(data) + + return _safe_call(_is_encrypted_cb) + + +def is_encrypted_sym(data): + """ + Return whether C{data} was encrypted using a public OpenPGP key. + + @param data: The data we want to know about. + @type data: str + + @return: Whether C{data} was encrypted using this wrapper. + @rtype: bool + """ + + def _is_encrypted_cb(gpg): + return gpg.is_encrypted_sym(data) + + return _safe_call(_is_encrypted_cb) + + +def is_encrypted_asym(data): + """ + Return whether C{data} was asymmetrically encrypted using OpenPGP. + + @param data: The data we want to know about. + @type data: str + + @return: Whether C{data} was encrypted using this wrapper. + @rtype: bool + """ + + def _is_encrypted_cb(gpg): + return gpg.is_encrypted_asym(data) + + return _safe_call(_is_encrypted_cb) + + +def _build_key_from_gpg(address, key, key_data): + """ + Build an OpenPGPKey for C{address} based on C{key} from + local gpg storage. + + ASCII armored GPG key data has to be queried independently in this + wrapper, so we receive it in C{key_data}. + + @param address: The address bound to the key. + @type address: str + @param key: Key obtained from GPG storage. + @type key: dict + @param key_data: Key data obtained from GPG storage. + @type key_data: str + @return: An instance of the key. + @rtype: OpenPGPKey + """ + return OpenPGPKey( + address, + key_id=key['keyid'], + fingerprint=key['fingerprint'], + key_data=key_data, + private=True if key['type'] == 'sec' else False, + length=key['length'], + expiry_date=key['expires'], + validation=None, # TODO: verify for validation. + ) + + +def _build_unitary_gpgwrapper(key_data=None): + """ + Return a temporary GPG wrapper keyring containing exactly zero or one + keys. + + Temporary unitary keyrings allow the to use GPG's facilities for exactly + one key. This function creates an empty temporary keyring and imports + C{key_data} if it is not None. + + @param key_data: ASCII armored key data. + @type key_data: str + @return: A GPG wrapper with a unitary keyring. + @rtype: gnupg.GPG + """ + tmpdir = tempfile.mkdtemp() + gpg = GPGWrapper(gnupghome=tmpdir) + leap_assert(len(gpg.list_keys()) is 0, 'Keyring not empty.') + if key_data: + gpg.import_keys(key_data) + leap_assert( + len(gpg.list_keys()) is 1, + 'Unitary keyring has wrong number of keys: %d.' + % len(gpg.list_keys())) + return gpg + + +def _destroy_unitary_gpgwrapper(gpg): + """ + Securely erase a unitary keyring. + + @param gpg: A GPG wrapper instance. + @type gpg: gnupg.GPG + """ + for secret in [True, False]: + for key in gpg.list_keys(secret=secret): + gpg.delete_keys( + key['fingerprint'], + secret=secret) + leap_assert(len(gpg.list_keys()) is 0, 'Keyring not empty!') + # TODO: implement some kind of wiping of data or a more secure way that + # does not write to disk. + shutil.rmtree(gpg.gnupghome) + + +def _safe_call(callback, key_data=None, **kwargs): + """ + Run C{callback} in an unitary keyring containing C{key_data}. + + @param callback: Function whose first argument is the gpg keyring. + @type callback: function(gnupg.GPG) + @param key_data: ASCII armored key data. + @type key_data: str + @param **kwargs: Other eventual parameters for the callback. + @type **kwargs: **dict + + @return: The results of the callback. + @rtype: str or bool + """ + gpg = _build_unitary_gpgwrapper(key_data) + val = callback(gpg, **kwargs) + _destroy_unitary_gpgwrapper(gpg) + return val + + +# +# The OpenPGP wrapper +# + +class OpenPGPKey(EncryptionKey): + """ + Base class for OpenPGP keys. + """ + + +class OpenPGPScheme(EncryptionScheme): + """ + A wrapper for OpenPGP keys. + """ + + def __init__(self, soledad): + """ + Initialize the OpenPGP wrapper. + + @param soledad: A Soledad instance for key storage. + @type soledad: leap.soledad.Soledad + """ + EncryptionScheme.__init__(self, soledad) + + def gen_key(self, address): + """ + Generate an OpenPGP keypair bound to C{address}. + + @param address: The address bound to the key. + @type address: str + @return: The key bound to C{address}. + @rtype: OpenPGPKey + @raise KeyAlreadyExists: If key already exists in local database. + """ + # make sure the key does not already exist + leap_assert(is_address(address), 'Not an user address: %s' % address) + try: + self.get_key(address) + raise KeyAlreadyExists(address) + except KeyNotFound: + pass + + def _gen_key_cb(gpg): + params = gpg.gen_key_input( + key_type='RSA', + key_length=4096, + name_real=address, + name_email=address, + name_comment='Generated by LEAP Key Manager.') + gpg.gen_key(params) + pubkeys = gpg.list_keys() + # assert for new key characteristics + leap_assert( + len(pubkeys) is 1, # a unitary keyring! + 'Keyring has wrong number of keys: %d.' % len(pubkeys)) + key = gpg.list_keys(secret=True).pop() + leap_assert( + len(key['uids']) is 1, # with just one uid! + 'Wrong number of uids for key: %d.' % len(key['uids'])) + leap_assert( + re.match('.*<%s>$' % address, key['uids'][0]) is not None, + 'Key not correctly bound to address.') + # insert both public and private keys in storage + for secret in [True, False]: + key = gpg.list_keys(secret=secret).pop() + openpgp_key = _build_key_from_gpg( + address, key, + gpg.export_keys(key['fingerprint'], secret=secret)) + self.put_key(openpgp_key) + + _safe_call(_gen_key_cb) + return self.get_key(address, private=True) + + def get_key(self, address, private=False): + """ + Get key bound to C{address} from local storage. + + @param address: The address bound to the key. + @type address: str + @param private: Look for a private key instead of a public one? + @type private: bool + + @return: The key bound to C{address}. + @rtype: OpenPGPKey + @raise KeyNotFound: If the key was not found on local storage. + """ + leap_assert(is_address(address), 'Not an user address: %s' % address) + doc = self._get_key_doc(address, private) + if doc is None: + raise KeyNotFound(address) + return build_key_from_dict(OpenPGPKey, address, doc.content) + + def put_key_raw(self, data): + """ + Put key contained in raw C{data} in local storage. + + @param data: The key data to be stored. + @type data: str + """ + # TODO: add more checks for correct key data. + leap_assert(data is not None, 'Data does not represent a key.') + + def _put_key_raw_cb(gpg): + + privkey = None + pubkey = None + try: + privkey = gpg.list_keys(secret=True).pop() + except IndexError: + pass + pubkey = gpg.list_keys(secret=False).pop() # unitary keyring + # extract adress from first uid on key + match = re.match('.*<([\w.-]+@[\w.-]+)>.*', pubkey['uids'].pop()) + leap_assert(match is not None, 'No user address in key data.') + address = match.group(1) + if privkey is not None: + match = re.match( + '.*<([\w.-]+@[\w.-]+)>.*', privkey['uids'].pop()) + leap_assert(match is not None, 'No user address in key data.') + privaddress = match.group(1) + leap_assert( + address == privaddress, + 'Addresses in pub and priv key differ.') + leap_assert( + pubkey['fingerprint'] == privkey['fingerprint'], + 'Fingerprints for pub and priv key differ.') + # insert private key in storage + openpgp_privkey = _build_key_from_gpg( + address, privkey, + gpg.export_keys(privkey['fingerprint'], secret=True)) + self.put_key(openpgp_privkey) + # insert public key in storage + openpgp_pubkey = _build_key_from_gpg( + address, pubkey, + gpg.export_keys(pubkey['fingerprint'], secret=False)) + self.put_key(openpgp_pubkey) + + _safe_call(_put_key_raw_cb, data) + + def put_key(self, key): + """ + Put C{key} in local storage. + + @param key: The key to be stored. + @type key: OpenPGPKey + """ + doc = self._get_key_doc(key.address, private=key.private) + if doc is None: + self._soledad.create_doc_from_json( + key.get_json(), + doc_id=keymanager_doc_id( + OpenPGPKey, key.address, key.private)) + else: + doc.set_json(key.get_json()) + self._soledad.put_doc(doc) + + def _get_key_doc(self, address, private=False): + """ + Get the document with a key (public, by default) bound to C{address}. + + If C{private} is True, looks for a private key instead of a public. + + @param address: The address bound to the key. + @type address: str + @param private: Whether to look for a private key. + @type private: bool + @return: The document with the key or None if it does not exist. + @rtype: leap.soledad.backends.leap_backend.LeapDocument + """ + return self._soledad.get_doc( + keymanager_doc_id(OpenPGPKey, address, private)) + + def delete_key(self, key): + """ + Remove C{key} from storage. + + @param key: The key to be removed. + @type key: EncryptionKey + """ + leap_assert(key.__class__ is OpenPGPKey, 'Wrong key type.') + stored_key = self.get_key(key.address, private=key.private) + if stored_key is None: + raise KeyNotFound(key) + if stored_key.__dict__ != key.__dict__: + raise KeyAttributesDiffer(key) + doc = self._soledad.get_doc( + keymanager_doc_id(OpenPGPKey, key.address, key.private)) + self._soledad.delete_doc(doc) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/testing/__init__.py b/debian/python-leap.common/usr/share/pyshared/leap/common/testing/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/testing/basetest.py b/debian/python-leap.common/usr/share/pyshared/leap/common/testing/basetest.py new file mode 100644 index 0000000..65e23a9 --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/testing/basetest.py @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- +# basetest.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Common testing facilities +""" +import os +import platform +import shutil +import tempfile + +try: + import unittest2 as unittest +except ImportError: + import unittest + +from leap.common.check import leap_assert +from leap.common.files import mkdir_p, check_and_fix_urw_only + + +class BaseLeapTest(unittest.TestCase): + """ + Base Leap TestCase + """ + __name__ = "leap_test" + _system = platform.system() + + @classmethod + def setUpClass(cls): + """ + Sets up common facilities for testing this TestCase: + - custom PATH and HOME environmental variables + - creates a temporal folder to which those point. + It saves the old path and home vars so they can be restored later. + """ + cls.old_path = os.environ['PATH'] + cls.old_home = os.environ['HOME'] + cls.tempdir = tempfile.mkdtemp(prefix="leap_tests-") + cls.home = cls.tempdir + bin_tdir = os.path.join( + cls.tempdir, + 'bin') + os.environ["PATH"] = bin_tdir + os.environ["HOME"] = cls.tempdir + + @classmethod + def tearDownClass(cls): + """ + Cleanup common facilities used for testing this TestCase: + - restores the default PATH and HOME variables + - removes the temporal folder + """ + os.environ["PATH"] = cls.old_path + os.environ["HOME"] = cls.old_home + # safety check! please do not wipe my home... + # XXX needs to adapt to non-linuces + leap_assert( + cls.tempdir.startswith('/tmp/leap_tests-'), + "beware! tried to remove a dir which does not " + "live in temporal folder!") + shutil.rmtree(cls.tempdir) + + # you have to override these methods + # this way we ensure we did not put anything + # here that you can forget to call. + + def setUp(self): + """not implemented""" + raise NotImplementedError("abstract base class") + + def tearDown(self): + """not implemented""" + raise NotImplementedError("abstract base class") + + # + # helper methods + # + + def _missing_test_for_plat(self, do_raise=False): + """ + Raises NotImplementedError for this platform + if do_raise is True + + @param do_raise: flag to actually raise exception + @type do_raise: bool + """ + if do_raise: + raise NotImplementedError( + "This test is not implemented " + "for the running platform: %s" % + self._system) + + def get_tempfile(self, filename): + """ + Returns the path of a given filename + prepending the temporal dir associated with this + TestCase + + @param filename: the filename + @type filename: str + """ + return os.path.join(self.tempdir, filename) + + def touch(self, filepath): + """ + Touches a filepath, creating folders along + the way if needed. + + @param filepath: path to be touched + @type filepath: str + """ + folder, filename = os.path.split(filepath) + if not os.path.isdir(folder): + mkdir_p(folder) + self.assertTrue(os.path.isdir(folder)) + with open(filepath, 'w') as fp: + fp.write(' ') + self.assertTrue(os.path.isfile(filepath)) + + def chmod600(self, filepath): + """ + Chmods 600 a file + + @param filepath: filepath to be chmodded + @type filepath: str + """ + check_and_fix_urw_only(filepath) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/testing/https_server.py b/debian/python-leap.common/usr/share/pyshared/leap/common/testing/https_server.py new file mode 100644 index 0000000..08d5089 --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/testing/https_server.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +# leap.common.testing.https_server.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +A simple HTTPS server to be used in tests +""" +from BaseHTTPServer import HTTPServer +import os +import ssl +import SocketServer +import threading +import unittest + +_where = os.path.split(__file__)[0] + + +def where(filename): + return os.path.join(_where, filename) + + +class HTTPSServer(HTTPServer): + def server_bind(self): + SocketServer.TCPServer.server_bind(self) + self.socket = ssl.wrap_socket( + self.socket, server_side=True, + certfile=where("leaptestscert.pem"), + keyfile=where("leaptestskey.pem"), + ca_certs=where("cacert.pem"), + ssl_version=ssl.PROTOCOL_SSLv23) + + +class TestServerThread(threading.Thread): + def __init__(self, test_object, request_handler): + threading.Thread.__init__(self) + self.request_handler = request_handler + self.test_object = test_object + + def run(self): + self.server = HTTPSServer(('localhost', 0), self.request_handler) + host, port = self.server.socket.getsockname() + self.test_object.HOST, self.test_object.PORT = host, port + self.test_object.server_started.set() + self.test_object = None + try: + self.server.serve_forever(0.05) + finally: + self.server.server_close() + + def stop(self): + self.server.shutdown() + + +class BaseHTTPSServerTestCase(unittest.TestCase): + """ + derived classes need to implement a request_handler + """ + def setUp(self): + self.server_started = threading.Event() + self.thread = TestServerThread(self, self.request_handler) + self.thread.start() + self.server_started.wait() + + def tearDown(self): + self.thread.stop() + + def get_server(self): + host, port = self.HOST, self.PORT + if host == "127.0.0.1": + host = "localhost" + return "%s:%s" % (host, port) + + +if __name__ == "__main__": + unittest.main() diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/testing/test_basetest.py b/debian/python-leap.common/usr/share/pyshared/leap/common/testing/test_basetest.py new file mode 100644 index 0000000..220e28d --- /dev/null +++ b/debian/python-leap.common/usr/share/pyshared/leap/common/testing/test_basetest.py @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- +# test_basetest.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Unittests for BaseLeapTest ...becase it's oh so meta +""" +try: + import unittest2 as unittest +except ImportError: + import unittest + +import os +import StringIO + +from leap.common.testing.basetest import BaseLeapTest + +_tempdir = None # global for tempdir checking + + +class _TestCaseRunner(object): + """ + TestCaseRunner used to run BaseLeapTest + """ + def run_testcase(self, testcase=None): + """ + Runs a given TestCase + + @param testcase: the testcase + @type testcase: unittest.TestCase + """ + if not testcase: + return None + loader = unittest.TestLoader() + suite = loader.loadTestsFromTestCase(testcase) + + # Create runner, and run testcase + io = StringIO.StringIO() + runner = unittest.TextTestRunner(stream=io) + results = runner.run(suite) + return results + + +class TestAbstractBaseLeapTest(unittest.TestCase, _TestCaseRunner): + """ + TestCase for BaseLeapTest abs + """ + def test_abstract_base_class(self): + """ + Test errors raised when setup/teardown not overloaded + """ + class _BaseTest(BaseLeapTest): + def test_dummy_method(self): + pass + + def test_tautology(self): + assert True + + results = self.run_testcase(_BaseTest) + + # should be 2 errors: NotImplemented + # raised for setUp/tearDown + self.assertEquals(results.testsRun, 2) + self.assertEquals(len(results.failures), 0) + self.assertEquals(len(results.errors), 2) + + +class TestInitBaseLeapTest(BaseLeapTest): + """ + TestCase for testing initialization of BaseLeapTest + """ + + def setUp(self): + """nuke it""" + pass + + def tearDown(self): + """nuke it""" + pass + + def test_path_is_changed(self): + """tests whether we have changed the PATH env var""" + os_path = os.environ['PATH'] + self.assertTrue(os_path.startswith(self.tempdir)) + + def test_old_path_is_saved(self): + """tests whether we restore the PATH env var""" + self.assertTrue(len(self.old_path) > 1) + + +class TestCleanedBaseLeapTest(unittest.TestCase, _TestCaseRunner): + """ + TestCase for testing tempdir creation and cleanup + """ + + def test_tempdir_is_cleaned_after_tests(self): + """ + test if a TestCase derived from BaseLeapTest creates and cleans the + temporal dir + """ + class _BaseTest(BaseLeapTest): + def setUp(self): + """set global _tempdir to this instance tempdir""" + global _tempdir + _tempdir = self.tempdir + + def tearDown(self): + """nothing""" + pass + + def test_tempdir_created(self): + """test if tempdir was created""" + self.assertTrue(os.path.isdir(self.tempdir)) + + def test_tempdir_created_on_setupclass(self): + """test if tempdir is the one created by setupclass""" + self.assertEqual(_tempdir, self.tempdir) + + results = self.run_testcase(_BaseTest) + self.assertEquals(results.testsRun, 2) + self.assertEquals(len(results.failures), 0) + self.assertEquals(len(results.errors), 0) + + # did we cleaned the tempdir? + self.assertFalse(os.path.isdir(_tempdir)) + +if __name__ == "__main__": + unittest.main() diff --git a/debian/python-leap.common/usr/share/python/ns/python-leap.common b/debian/python-leap.common/usr/share/python/ns/python-leap.common new file mode 100644 index 0000000..2905ed7 --- /dev/null +++ b/debian/python-leap.common/usr/share/python/ns/python-leap.common @@ -0,0 +1 @@ +leap diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..bcebae9 --- /dev/null +++ b/debian/rules @@ -0,0 +1,6 @@ +#!/usr/bin/make -f + +%: + dh $@ --with python2 --buildsystem=python_distutils + + diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..861a9f5 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + -- cgit v1.2.3 From ac477de207b132f479efbaad28b1db985f908bb6 Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Thu, 30 May 2013 22:45:35 -0400 Subject: remove unnecessary files from repository --- debian/#control# | 14 -------------- debian/.#control | 1 - pkg/requirements-testing.pip | 1 - pkg/requirements.pip | 10 ---------- 4 files changed, 26 deletions(-) delete mode 100644 debian/#control# delete mode 120000 debian/.#control delete mode 100644 pkg/requirements-testing.pip delete mode 100644 pkg/requirements.pip diff --git a/debian/#control# b/debian/#control# deleted file mode 100644 index 9f79cc3..0000000 --- a/debian/#control# +++ /dev/null @@ -1,14 +0,0 @@ -Source: leap-common -Maintainer: Micah Anderson -Section: python -Priority: optional -Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 9), python-protobuf, python-protobuf.socketrpc, protobuf-c-compiler -Standards-Version: 3.9.4 - -Package: python-leap.common -Architecture: all -Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-xdg, python-protobuf, python-protobuf.socketrpc, - python-dateutil, python-autopep8, python-openssl, python-gnupg -Description: Common python files needed by LEAP projects. - This package contains common python functions that are needed - for the LEAP Client project diff --git a/debian/.#control b/debian/.#control deleted file mode 120000 index cedfffd..0000000 --- a/debian/.#control +++ /dev/null @@ -1 +0,0 @@ -micah@muck.riseup.net.4743:1369862767 \ No newline at end of file diff --git a/pkg/requirements-testing.pip b/pkg/requirements-testing.pip deleted file mode 100644 index 932a895..0000000 --- a/pkg/requirements-testing.pip +++ /dev/null @@ -1 +0,0 @@ -mock diff --git a/pkg/requirements.pip b/pkg/requirements.pip deleted file mode 100644 index 141c325..0000000 --- a/pkg/requirements.pip +++ /dev/null @@ -1,10 +0,0 @@ -jsonschema<=0.8 -pyxdg -protobuf -pyopenssl -python-dateutil -autopep8 -python-gnupg -PyCrypto - -https://protobuf-socket-rpc.googlecode.com/files/protobuf.socketrpc-1.3.2.tar.gz -- cgit v1.2.3 From 281ff0552855f00cf7af3eeef23c82dd64f35ed3 Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Thu, 30 May 2013 22:45:47 -0400 Subject: change name to python-leap-common to be more standardized --- debian/control | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/control b/debian/control index f5bb89e..81e8ecf 100644 --- a/debian/control +++ b/debian/control @@ -2,10 +2,10 @@ Source: leap-common Maintainer: Micah Anderson Section: python Priority: optional -Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 9), python-protobuf, python-protobuf.socketrpc +Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 9), python-protobuf, python-protobuf.socketrpc, protobuf-c-compiler Standards-Version: 3.9.4 -Package: python-leap.common +Package: python-leap-common Architecture: all Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-xdg, python-protobuf, python-protobuf.socketrpc, python-dateutil, python-autopep8, python-openssl, python-gnupg -- cgit v1.2.3 From 07fe170c895252a46dba664c910ce35e64fb6076 Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Thu, 30 May 2013 22:49:25 -0400 Subject: add FIXME to rules so we can remember to resolve the protobuf issue --- debian/control | 2 +- debian/rules | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 81e8ecf..02f1b79 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: leap-common Maintainer: Micah Anderson Section: python Priority: optional -Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 9), python-protobuf, python-protobuf.socketrpc, protobuf-c-compiler +Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 9), python-protobuf, python-protobuf.socketrpc Standards-Version: 3.9.4 Package: python-leap-common diff --git a/debian/rules b/debian/rules index bcebae9..e7c975e 100755 --- a/debian/rules +++ b/debian/rules @@ -3,4 +3,5 @@ %: dh $@ --with python2 --buildsystem=python_distutils +# FIXME: need to insert an extra build step to generate from the inputs the proto (and build dep on protobuf-c-compiler) -- cgit v1.2.3 From 48c479944a4c7c0a75a8ae44fec39ea734708cad Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Thu, 30 May 2013 22:51:47 -0400 Subject: remove build generated files --- debian/files | 1 - debian/python-leap.common.debhelper.log | 16 - debian/python-leap.common.postinst.debhelper | 7 - debian/python-leap.common.prerm.debhelper | 12 - debian/python-leap.common.substvars | 4 - debian/python-leap.common/DEBIAN/control | 12 - debian/python-leap.common/DEBIAN/md5sums | 33 -- debian/python-leap.common/DEBIAN/postinst | 9 - debian/python-leap.common/DEBIAN/prerm | 14 - .../dist-packages/leap.common-0.2.3_dev-nspkg.pth | 1 - .../dist-packages/leap/common/__init__.py | 1 - .../python2.7/dist-packages/leap/common/certs.py | 1 - .../python2.7/dist-packages/leap/common/check.py | 1 - .../dist-packages/leap/common/config/__init__.py | 1 - .../dist-packages/leap/common/config/baseconfig.py | 1 - .../leap/common/config/pluggableconfig.py | 1 - .../dist-packages/leap/common/config/prefixers.py | 1 - .../dist-packages/leap/common/events/__init__.py | 1 - .../dist-packages/leap/common/events/component.py | 1 - .../dist-packages/leap/common/events/daemon.py | 1 - .../dist-packages/leap/common/events/events_pb2.py | 1 - .../dist-packages/leap/common/events/mac_auth.py | 1 - .../dist-packages/leap/common/events/server.py | 1 - .../python2.7/dist-packages/leap/common/files.py | 1 - .../leap/common/keymanager/__init__.py | 1 - .../dist-packages/leap/common/keymanager/errors.py | 1 - .../dist-packages/leap/common/keymanager/gpg.py | 1 - .../dist-packages/leap/common/keymanager/keys.py | 1 - .../leap/common/keymanager/openpgp.py | 1 - .../dist-packages/leap/common/testing/__init__.py | 1 - .../dist-packages/leap/common/testing/basetest.py | 1 - .../leap/common/testing/https_server.py | 1 - .../leap/common/testing/test_basetest.py | 1 - .../doc/python-leap.common/changelog.Debian.gz | Bin 149 -> 0 bytes .../usr/share/doc/python-leap.common/copyright | 16 - .../share/pyshared/leap.common-0.2.3_dev-nspkg.pth | 1 - .../usr/share/pyshared/leap/common/__init__.py | 19 - .../usr/share/pyshared/leap/common/certs.py | 179 -------- .../usr/share/pyshared/leap/common/check.py | 61 --- .../share/pyshared/leap/common/config/__init__.py | 0 .../pyshared/leap/common/config/baseconfig.py | 185 -------- .../pyshared/leap/common/config/pluggableconfig.py | 475 --------------------- .../share/pyshared/leap/common/config/prefixers.py | 132 ------ .../share/pyshared/leap/common/events/__init__.py | 100 ----- .../share/pyshared/leap/common/events/component.py | 238 ----------- .../share/pyshared/leap/common/events/daemon.py | 208 --------- .../pyshared/leap/common/events/events_pb2.py | 371 ---------------- .../share/pyshared/leap/common/events/mac_auth.py | 31 -- .../share/pyshared/leap/common/events/server.py | 149 ------- .../usr/share/pyshared/leap/common/files.py | 126 ------ .../pyshared/leap/common/keymanager/__init__.py | 286 ------------- .../pyshared/leap/common/keymanager/errors.py | 46 -- .../share/pyshared/leap/common/keymanager/gpg.py | 397 ----------------- .../share/pyshared/leap/common/keymanager/keys.py | 230 ---------- .../pyshared/leap/common/keymanager/openpgp.py | 459 -------------------- .../share/pyshared/leap/common/testing/__init__.py | 0 .../share/pyshared/leap/common/testing/basetest.py | 140 ------ .../pyshared/leap/common/testing/https_server.py | 87 ---- .../pyshared/leap/common/testing/test_basetest.py | 140 ------ .../usr/share/python/ns/python-leap.common | 1 - 60 files changed, 4209 deletions(-) delete mode 100644 debian/files delete mode 100644 debian/python-leap.common.debhelper.log delete mode 100644 debian/python-leap.common.postinst.debhelper delete mode 100644 debian/python-leap.common.prerm.debhelper delete mode 100644 debian/python-leap.common.substvars delete mode 100644 debian/python-leap.common/DEBIAN/control delete mode 100644 debian/python-leap.common/DEBIAN/md5sums delete mode 100755 debian/python-leap.common/DEBIAN/postinst delete mode 100755 debian/python-leap.common/DEBIAN/prerm delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap.common-0.2.3_dev-nspkg.pth delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/__init__.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/certs.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/check.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/__init__.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/baseconfig.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/pluggableconfig.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/prefixers.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/__init__.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/component.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/daemon.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/events_pb2.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/mac_auth.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/server.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/files.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/__init__.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/errors.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/gpg.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/keys.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/openpgp.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/__init__.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/basetest.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/https_server.py delete mode 120000 debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/test_basetest.py delete mode 100644 debian/python-leap.common/usr/share/doc/python-leap.common/changelog.Debian.gz delete mode 100644 debian/python-leap.common/usr/share/doc/python-leap.common/copyright delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap.common-0.2.3_dev-nspkg.pth delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/__init__.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/certs.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/check.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/config/__init__.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/config/baseconfig.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/config/pluggableconfig.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/config/prefixers.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/events/__init__.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/events/component.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/events/daemon.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/events/events_pb2.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/events/mac_auth.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/events/server.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/files.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/__init__.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/errors.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/gpg.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/keys.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/openpgp.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/testing/__init__.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/testing/basetest.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/testing/https_server.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/testing/test_basetest.py delete mode 100644 debian/python-leap.common/usr/share/python/ns/python-leap.common diff --git a/debian/files b/debian/files deleted file mode 100644 index 0fce32e..0000000 --- a/debian/files +++ /dev/null @@ -1 +0,0 @@ -python-leap.common_0.2.3-dev1_all.deb python optional diff --git a/debian/python-leap.common.debhelper.log b/debian/python-leap.common.debhelper.log deleted file mode 100644 index b57ac90..0000000 --- a/debian/python-leap.common.debhelper.log +++ /dev/null @@ -1,16 +0,0 @@ -dh_auto_configure -dh_auto_build -dh_auto_test -dh_prep -dh_auto_install -dh_installdocs -dh_installchangelogs -dh_perl -dh_link -dh_compress -dh_fixperms -dh_installdeb -dh_gencontrol -dh_md5sums -dh_builddeb -dh_builddeb diff --git a/debian/python-leap.common.postinst.debhelper b/debian/python-leap.common.postinst.debhelper deleted file mode 100644 index f15a4c9..0000000 --- a/debian/python-leap.common.postinst.debhelper +++ /dev/null @@ -1,7 +0,0 @@ - -# Automatically added by dh_python2: -if which pycompile >/dev/null 2>&1; then - pycompile -p python-leap.common -fi - -# End automatically added section diff --git a/debian/python-leap.common.prerm.debhelper b/debian/python-leap.common.prerm.debhelper deleted file mode 100644 index 285a1b2..0000000 --- a/debian/python-leap.common.prerm.debhelper +++ /dev/null @@ -1,12 +0,0 @@ - -# Automatically added by dh_python2: -if which pyclean >/dev/null 2>&1; then - pyclean -p python-leap.common -else - dpkg -L python-leap.common | grep \.py$ | while read file - do - rm -f "${file}"[co] >/dev/null - done -fi - -# End automatically added section diff --git a/debian/python-leap.common.substvars b/debian/python-leap.common.substvars deleted file mode 100644 index c2bab2c..0000000 --- a/debian/python-leap.common.substvars +++ /dev/null @@ -1,4 +0,0 @@ -python:Versions=2.7 -python:Provides=python2.7-leap.common -python:Depends=python (>= 2.7), python (<< 2.8), python (>= 2.6.6-7~), python-jsonschema, python-xdg, python-protobuf, python-protobuf.socketrpc, python-openssl, python-dateutil -misc:Depends= diff --git a/debian/python-leap.common/DEBIAN/control b/debian/python-leap.common/DEBIAN/control deleted file mode 100644 index 7ec433d..0000000 --- a/debian/python-leap.common/DEBIAN/control +++ /dev/null @@ -1,12 +0,0 @@ -Package: python-leap.common -Source: leap-common -Version: 0.2.3-dev1 -Architecture: all -Maintainer: Micah Anderson -Installed-Size: 234 -Depends: python (>= 2.7), python (<< 2.8), python-jsonschema, python-xdg, python-protobuf, python-protobuf.socketrpc, python-openssl, python-dateutil, python-autopep8, python-gnupg -Section: python -Priority: optional -Description: Common python files needed by LEAP projects. - This package contains common python functions that are needed - for the LEAP Client project diff --git a/debian/python-leap.common/DEBIAN/md5sums b/debian/python-leap.common/DEBIAN/md5sums deleted file mode 100644 index 1bfa617..0000000 --- a/debian/python-leap.common/DEBIAN/md5sums +++ /dev/null @@ -1,33 +0,0 @@ -31fd2c104cd4c1d193ec9aa4f20e6001 usr/share/doc/python-leap.common/changelog.Debian.gz -21d310049c19f5d33ba5ea6c60e5dca8 usr/share/doc/python-leap.common/copyright -d22e9cf7bd13e4ede09bac84421f142d usr/share/pyshared/leap.common-0.2.3_dev-nspkg.pth -990b94e8f6be8bb2739002e88ab33b2b usr/share/pyshared/leap.common-0.2.3_dev.egg-info/PKG-INFO -37c1614b30fee94ef7e62c0eb4045f65 usr/share/pyshared/leap.common-0.2.3_dev.egg-info/SOURCES.txt -e8d5c7c249b5856392d6448fd5899f5e usr/share/pyshared/leap.common-0.2.3_dev.egg-info/dependency_links.txt -15efee23227e2dd46960cbb8a3b1a9f0 usr/share/pyshared/leap.common-0.2.3_dev.egg-info/namespace_packages.txt -613023c3d0b371605285222494503930 usr/share/pyshared/leap.common-0.2.3_dev.egg-info/requires.txt -15efee23227e2dd46960cbb8a3b1a9f0 usr/share/pyshared/leap.common-0.2.3_dev.egg-info/top_level.txt -533cda9d3cb16103fbcc42de8fac6a34 usr/share/pyshared/leap/common/__init__.py -4435367cd0c437d5b2d4a191b26dabd1 usr/share/pyshared/leap/common/certs.py -6c883a1051129c65f75c53378f0e7a1d usr/share/pyshared/leap/common/check.py -d41d8cd98f00b204e9800998ecf8427e usr/share/pyshared/leap/common/config/__init__.py -877407026ce9d42d0da03d3b72301740 usr/share/pyshared/leap/common/config/baseconfig.py -ff1eb89cf37e042e1b572e9138ae5269 usr/share/pyshared/leap/common/config/pluggableconfig.py -f5284b4e8d3aaae6de58ad42043e1162 usr/share/pyshared/leap/common/config/prefixers.py -33eb1eeb41079ad0234afba2edbf0a9d usr/share/pyshared/leap/common/events/__init__.py -732e2b7ca5f22d151c42ce64c736b82e usr/share/pyshared/leap/common/events/component.py -a861976eef97e7ed95bb33f6dbeb05f4 usr/share/pyshared/leap/common/events/daemon.py -cb7221fa887ff5e2e08c47bf13f6a23e usr/share/pyshared/leap/common/events/events_pb2.py -c8dbde173372f58602fe6dd19fc30cf1 usr/share/pyshared/leap/common/events/mac_auth.py -bd6ebdc4f0441b689d6f0101f3b9fa80 usr/share/pyshared/leap/common/events/server.py -8a9d3a856b42c4a5c5e69dcfe8982307 usr/share/pyshared/leap/common/files.py -77bbd4d03995b6f4652958516d710efa usr/share/pyshared/leap/common/keymanager/__init__.py -ddef5455c3b7964365267184a3fa2c0c usr/share/pyshared/leap/common/keymanager/errors.py -05828143ff4d2b9da92227da4bc12c4d usr/share/pyshared/leap/common/keymanager/gpg.py -b85230945b9a597bed40bfadb5f01178 usr/share/pyshared/leap/common/keymanager/keys.py -0f7ea32cb387a08d9a043decd51d0f03 usr/share/pyshared/leap/common/keymanager/openpgp.py -d41d8cd98f00b204e9800998ecf8427e usr/share/pyshared/leap/common/testing/__init__.py -5dd5c2a35014c45510c0cec0874c3cb1 usr/share/pyshared/leap/common/testing/basetest.py -8f232808fe0fa9ab35baee7290a1081b usr/share/pyshared/leap/common/testing/https_server.py -848e9db64230b00a1b4047f1f0ce4e7d usr/share/pyshared/leap/common/testing/test_basetest.py -15efee23227e2dd46960cbb8a3b1a9f0 usr/share/python/ns/python-leap.common diff --git a/debian/python-leap.common/DEBIAN/postinst b/debian/python-leap.common/DEBIAN/postinst deleted file mode 100755 index 5dc39f3..0000000 --- a/debian/python-leap.common/DEBIAN/postinst +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -set -e - -# Automatically added by dh_python2: -if which pycompile >/dev/null 2>&1; then - pycompile -p python-leap.common -fi - -# End automatically added section diff --git a/debian/python-leap.common/DEBIAN/prerm b/debian/python-leap.common/DEBIAN/prerm deleted file mode 100755 index 702eec2..0000000 --- a/debian/python-leap.common/DEBIAN/prerm +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh -set -e - -# Automatically added by dh_python2: -if which pyclean >/dev/null 2>&1; then - pyclean -p python-leap.common -else - dpkg -L python-leap.common | grep \.py$ | while read file - do - rm -f "${file}"[co] >/dev/null - done -fi - -# End automatically added section diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap.common-0.2.3_dev-nspkg.pth b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap.common-0.2.3_dev-nspkg.pth deleted file mode 120000 index 596c637..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap.common-0.2.3_dev-nspkg.pth +++ /dev/null @@ -1 +0,0 @@ -../../../share/pyshared/leap.common-0.2.3_dev-nspkg.pth \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/__init__.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/__init__.py deleted file mode 120000 index 1553bef..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/__init__.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../share/pyshared/leap/common/__init__.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/certs.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/certs.py deleted file mode 120000 index 7585fb4..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/certs.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../share/pyshared/leap/common/certs.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/check.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/check.py deleted file mode 120000 index 6a7db1e..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/check.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../share/pyshared/leap/common/check.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/__init__.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/__init__.py deleted file mode 120000 index 2c55977..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/__init__.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/config/__init__.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/baseconfig.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/baseconfig.py deleted file mode 120000 index 98b7bba..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/baseconfig.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/config/baseconfig.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/pluggableconfig.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/pluggableconfig.py deleted file mode 120000 index 1172cd4..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/pluggableconfig.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/config/pluggableconfig.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/prefixers.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/prefixers.py deleted file mode 120000 index e87c993..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/config/prefixers.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/config/prefixers.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/__init__.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/__init__.py deleted file mode 120000 index 075facb..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/__init__.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/events/__init__.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/component.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/component.py deleted file mode 120000 index 8959c67..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/component.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/events/component.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/daemon.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/daemon.py deleted file mode 120000 index afc5565..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/daemon.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/events/daemon.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/events_pb2.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/events_pb2.py deleted file mode 120000 index 82bbbc2..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/events_pb2.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/events/events_pb2.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/mac_auth.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/mac_auth.py deleted file mode 120000 index 935cc1c..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/mac_auth.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/events/mac_auth.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/server.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/server.py deleted file mode 120000 index 3696d19..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/events/server.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/events/server.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/files.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/files.py deleted file mode 120000 index 3c572b0..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/files.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../share/pyshared/leap/common/files.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/__init__.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/__init__.py deleted file mode 120000 index c34b26b..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/__init__.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/keymanager/__init__.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/errors.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/errors.py deleted file mode 120000 index cbd005a..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/errors.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/keymanager/errors.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/gpg.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/gpg.py deleted file mode 120000 index 35582de..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/gpg.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/keymanager/gpg.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/keys.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/keys.py deleted file mode 120000 index bcb823c..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/keys.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/keymanager/keys.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/openpgp.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/openpgp.py deleted file mode 120000 index a403cb0..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/keymanager/openpgp.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/keymanager/openpgp.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/__init__.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/__init__.py deleted file mode 120000 index 16a0445..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/__init__.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/testing/__init__.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/basetest.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/basetest.py deleted file mode 120000 index 31a3556..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/basetest.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/testing/basetest.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/https_server.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/https_server.py deleted file mode 120000 index 298c389..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/https_server.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/testing/https_server.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/test_basetest.py b/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/test_basetest.py deleted file mode 120000 index a5ff0cd..0000000 --- a/debian/python-leap.common/usr/lib/python2.7/dist-packages/leap/common/testing/test_basetest.py +++ /dev/null @@ -1 +0,0 @@ -../../../../../../share/pyshared/leap/common/testing/test_basetest.py \ No newline at end of file diff --git a/debian/python-leap.common/usr/share/doc/python-leap.common/changelog.Debian.gz b/debian/python-leap.common/usr/share/doc/python-leap.common/changelog.Debian.gz deleted file mode 100644 index 9261f57..0000000 Binary files a/debian/python-leap.common/usr/share/doc/python-leap.common/changelog.Debian.gz and /dev/null differ diff --git a/debian/python-leap.common/usr/share/doc/python-leap.common/copyright b/debian/python-leap.common/usr/share/doc/python-leap.common/copyright deleted file mode 100644 index a132a29..0000000 --- a/debian/python-leap.common/usr/share/doc/python-leap.common/copyright +++ /dev/null @@ -1,16 +0,0 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: leap-common -Upstream-Contact: kali@leap.se -Source: - -Files: * - Copyright (C) 2013 LEAP -License: GPL-3+ - -Files: debian/* -Copyright: Copyright 2013 Micah Anderson -License: GPL-3+ - -License: GPL-3+ - On Debian systems, the complete text of the GNU General - Public License can be found in `/usr/share/common-licenses/GPL'. diff --git a/debian/python-leap.common/usr/share/pyshared/leap.common-0.2.3_dev-nspkg.pth b/debian/python-leap.common/usr/share/pyshared/leap.common-0.2.3_dev-nspkg.pth deleted file mode 100644 index 0f2034f..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap.common-0.2.3_dev-nspkg.pth +++ /dev/null @@ -1 +0,0 @@ -import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('leap',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('leap',types.ModuleType('leap')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/__init__.py b/debian/python-leap.common/usr/share/pyshared/leap/common/__init__.py deleted file mode 100644 index 5bcbb38..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -import logging - -from leap.common import certs -from leap.common import check -from leap.common import files -from leap.common import events - -logger = logging.getLogger(__name__) - -try: - import pygeoip - HAS_GEOIP = True -except ImportError: - #logger.debug('PyGeoIP not found. Disabled Geo support.') - HAS_GEOIP = False - -__all__ = ["certs", "check", "files", "events"] - -__version__ = "0.2.3-dev" diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/certs.py b/debian/python-leap.common/usr/share/pyshared/leap/common/certs.py deleted file mode 100644 index 4cb70dd..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/certs.py +++ /dev/null @@ -1,179 +0,0 @@ -# -*- coding: utf-8 -*- -# certs.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -Implements cert checks and helpers -""" - -import os -import time -import logging - -from OpenSSL import crypto -from dateutil.parser import parse as dateparse - -from leap.common.check import leap_assert - -logger = logging.getLogger(__name__) - - -def get_cert_from_string(string): - """ - Returns the x509 from the contents of this string - - @param string: certificate contents as downloaded - @type string: str - - @return: x509 or None - """ - leap_assert(string, "We need something to load") - - x509 = None - try: - x509 = crypto.load_certificate(crypto.FILETYPE_PEM, string) - except Exception as e: - logger.error("Something went wrong while loading the certificate: %r" - % (e,)) - return x509 - - -def get_privatekey_from_string(string): - """ - Returns the private key from the contents of this string - - @param string: private key contents as downloaded - @type string: str - - @return: private key or None - """ - leap_assert(string, "We need something to load") - - pkey = None - try: - pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, string) - except Exception as e: - logger.error("Something went wrong while loading the certificate: %r" - % (e,)) - return pkey - - -def get_digest(cert_data, method): - """ - Returns the digest for the cert_data using the method specified - - @param cert_data: certificate data in string form - @type cert_data: str - @param method: method to be used for digest - @type method: str - - @rtype: str - """ - x509 = get_cert_from_string(cert_data) - digest = x509.digest(method).replace(":", "").lower() - - return digest - - -def can_load_cert_and_pkey(string): - """ - Loads certificate and private key from a buffer, returns True if - everything went well, False otherwise - - @param string: buffer containing the cert and private key - @type string: str or any kind of buffer - - @rtype: bool - """ - can_load = True - - try: - cert = get_cert_from_string(string) - key = get_privatekey_from_string(string) - - leap_assert(cert, 'The certificate could not be loaded') - leap_assert(key, 'The private key could not be loaded') - except Exception as e: - can_load = False - logger.error("Something went wrong while trying to load " - "the certificate: %r" % (e,)) - - return can_load - - -def is_valid_pemfile(cert): - """ - Checks that the passed string is a valid pem certificate - - @param cert: String containing pem content - @type cert: str - - @rtype: bool - """ - leap_assert(cert, "We need a cert to load") - - return can_load_cert_and_pkey(cert) - - -def get_cert_time_boundaries(certfile): - """ - Returns the time boundaries for the certificate saved in certfile - - @param certfile: path to certificate - @type certfile: str - - @rtype: tuple (from, to) - """ - cert = get_cert_from_string(certfile) - leap_assert(cert, 'There was a problem loading the certificate') - - fromts, tots = (cert.get_notBefore(), cert.get_notAfter()) - from_, to_ = map( - lambda ts: time.gmtime(time.mktime(dateparse(ts).timetuple())), - (fromts, tots)) - return from_, to_ - - -def should_redownload(certfile, now=time.gmtime): - """ - Returns True if any of the checks don't pass, False otherwise - - @param certfile: path to certificate - @type certfile: str - @param now: current date function, ONLY USED FOR TESTING - - @rtype: bool - """ - exists = os.path.isfile(certfile) - - if not exists: - return True - - certdata = None - try: - with open(certfile, "r") as f: - certdata = f.read() - if not is_valid_pemfile(certdata): - return True - except: - return True - - valid_from, valid_to = get_cert_time_boundaries(certdata) - - if not (valid_from < now() < valid_to): - return True - - return False diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/check.py b/debian/python-leap.common/usr/share/pyshared/leap/common/check.py deleted file mode 100644 index 359673b..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/check.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -# check.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -""" -Set of functions to help checking situations -""" - -import inspect -import logging -import traceback - - -logger = logging.getLogger(__name__) - - -def leap_assert(condition, message=""): - """ - Asserts the condition and displays the message if that's not - met. It also logs the error and its backtrace. - - @param condition: condition to check - @type condition: bool - @param message: message to display if the condition isn't met - @type message: str - """ - if not condition: - logger.error("Bug: %s" % (message,)) - try: - frame = inspect.currentframe() - stack_trace = traceback.format_stack(frame) - logger.error(''.join(stack_trace)) - except Exception as e: - logger.error("Bug in leap_assert: %r" % (e,)) - assert condition, message - - -def leap_assert_type(var, expectedType): - """ - Helper assert check for a variable's expected type - - @param var: variable to check - @type var: any - @param expectedType: type to check agains - @type expectedType: type - """ - leap_assert(isinstance(var, expectedType), - "Expected type %r instead of %r" % - (expectedType, type(var))) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/config/__init__.py b/debian/python-leap.common/usr/share/pyshared/leap/common/config/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/config/baseconfig.py b/debian/python-leap.common/usr/share/pyshared/leap/common/config/baseconfig.py deleted file mode 100644 index 146f1e4..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/config/baseconfig.py +++ /dev/null @@ -1,185 +0,0 @@ -# -*- coding: utf-8 -*- -# baseconfig.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -Implements the abstract base class for configuration -""" - -import copy -import logging -import functools -import os - -from abc import ABCMeta, abstractmethod - -from leap.common.check import leap_assert -from leap.common.files import mkdir_p -from leap.common.config.pluggableconfig import PluggableConfig -from leap.common.config.prefixers import get_platform_prefixer - -logger = logging.getLogger(__name__) - - -class BaseConfig: - """ - Abstract base class for any JSON based configuration - """ - - __metaclass__ = ABCMeta - - """ - Standalone is a class wide parameter - - @param standalone: if True it will return the prefix for a - standalone application. Otherwise, it will return the system - default for configuration storage. - @type standalone: bool - """ - standalone = False - - def __init__(self): - self._data = {} - self._config_checker = None - - @abstractmethod - def _get_spec(self): - """ - Returns the spec object for the specific configuration - """ - return None - - def _safe_get_value(self, key): - """ - Tries to return a value only if the config has already been loaded - - @rtype: depends on the config structure, dict, str, array, int - @return: returns the value for the specified key in the config - """ - leap_assert(self._config_checker, "Load the config first") - return self._config_checker.config.get(key, None) - - def get_path_prefix(self): - """ - Returns the platform dependant path prefixer - """ - return get_platform_prefixer().get_path_prefix( - standalone=self.standalone) - - def loaded(self): - """ - Returns True if the configuration has been already - loaded. False otherwise - """ - return self._config_checker is not None - - def save(self, path_list): - """ - Saves the current configuration to disk - - @param path_list: list of components that form the relative - path to configuration. The absolute path will be calculated - depending on the platform. - @type path_list: list - - @return: True if saved to disk correctly, False otherwise - """ - config_path = os.path.join(self.get_path_prefix(), *(path_list[:-1])) - mkdir_p(config_path) - - try: - self._config_checker.serialize(os.path.join(config_path, - path_list[-1])) - except Exception as e: - logger.warning("%s" % (e,)) - raise - return True - - def load(self, path="", data=None, mtime=None): - """ - Loads the configuration from disk - - @type path: str - @param path: relative path to configuration. The absolute path - will be calculated depending on the platform - - @return: True if loaded from disk correctly, False otherwise - """ - - config_path = os.path.join(self.get_path_prefix(), - path) - - self._config_checker = PluggableConfig(format="json") - self._config_checker.options = copy.deepcopy(self._get_spec()) - - try: - if data is None: - self._config_checker.load(fromfile=config_path, mtime=mtime) - else: - self._config_checker.load(data, mtime=mtime) - except Exception as e: - logger.warning("Something went wrong while loading " + - "the config from %s\n%s" % (config_path, e)) - self._config_checker = None - return False - return True - - -class LocalizedKey(object): - """ - Decorator used for keys that are localized in a configuration - """ - - def __init__(self, func, **kwargs): - self._func = func - - def __call__(self, instance, lang="en"): - """ - Tries to return the string for the specified language, otherwise - informs the problem and returns an empty string - - @param lang: language code - @type lang: str - - @return: localized value from the possible values returned by - self._func - """ - descriptions = self._func(instance) - description_lang = "" - config_lang = "en" - for key in descriptions.keys(): - if lang.startswith(key): - config_lang = key - break - - description_lang = descriptions[config_lang] - return description_lang - - def __get__(self, instance, instancetype): - """ - Implement the descriptor protocol to make decorating instance - method possible. - """ - # Return a partial function with the first argument is the instance - # of the class decorated. - return functools.partial(self.__call__, instance) - -if __name__ == "__main__": - try: - config = BaseConfig() # should throw TypeError for _get_spec - except Exception as e: - assert isinstance(e, TypeError), "Something went wrong" - print "Abstract BaseConfig class is working as expected" diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/config/pluggableconfig.py b/debian/python-leap.common/usr/share/pyshared/leap/common/config/pluggableconfig.py deleted file mode 100644 index 8535fa6..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/config/pluggableconfig.py +++ /dev/null @@ -1,475 +0,0 @@ -# -*- coding: utf-8 -*- -# pluggableconfig.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -generic configuration handlers -""" -import copy -import json -import logging -import os -import time -import urlparse - -import jsonschema - -#from leap.base.util.translations import LEAPTranslatable -from leap.common.check import leap_assert - - -logger = logging.getLogger(__name__) - - -__all__ = ['PluggableConfig', - 'adaptors', - 'types', - 'UnknownOptionException', - 'MissingValueException', - 'ConfigurationProviderException', - 'TypeCastException'] - -# exceptions - - -class UnknownOptionException(Exception): - """exception raised when a non-configuration - value is present in the configuration""" - - -class MissingValueException(Exception): - """exception raised when a required value is missing""" - - -class ConfigurationProviderException(Exception): - """exception raised when a configuration provider is missing, etc""" - - -class TypeCastException(Exception): - """exception raised when a - configuration item cannot be coerced to a type""" - - -class ConfigAdaptor(object): - """ - abstract base class for config adaotors for - serialization/deserialization and custom validation - and type casting. - """ - def read(self, filename): - raise NotImplementedError("abstract base class") - - def write(self, config, filename): - with open(filename, 'w') as f: - self._write(f, config) - - def _write(self, fp, config): - raise NotImplementedError("abstract base class") - - def validate(self, config, schema): - raise NotImplementedError("abstract base class") - - -adaptors = {} - - -class JSONSchemaEncoder(json.JSONEncoder): - """ - custom default encoder that - casts python objects to json objects for - the schema validation - """ - def default(self, obj): - if obj is str: - return 'string' - if obj is unicode: - return 'string' - if obj is int: - return 'integer' - if obj is list: - return 'array' - if obj is dict: - return 'object' - if obj is bool: - return 'boolean' - - -class JSONAdaptor(ConfigAdaptor): - indent = 2 - extensions = ['json'] - - def read(self, _from): - if isinstance(_from, file): - _from_string = _from.read() - if isinstance(_from, str): - _from_string = _from - return json.loads(_from_string) - - def _write(self, fp, config): - fp.write(json.dumps(config, - indent=self.indent, - sort_keys=True)) - - def validate(self, config, schema_obj): - schema_json = JSONSchemaEncoder().encode(schema_obj) - schema = json.loads(schema_json) - jsonschema.validate(config, schema) - - -adaptors['json'] = JSONAdaptor() - -# -# Adaptors -# -# Allow to apply a predefined set of types to the -# specs, so it checks the validity of formats and cast it -# to proper python types. - -# TODO: -# - HTTPS uri - - -class DateType(object): - fmt = '%Y-%m-%d' - - def to_python(self, data): - return time.strptime(data, self.fmt) - - def get_prep_value(self, data): - return time.strftime(self.fmt, data) - - -class TranslatableType(object): - """ - a type that casts to LEAPTranslatable objects. - Used for labels we get from providers and stuff. - """ - - def to_python(self, data): - # TODO: add translatable - return data # LEAPTranslatable(data) - - # needed? we already have an extended dict... - #def get_prep_value(self, data): - #return dict(data) - - -class URIType(object): - - def to_python(self, data): - parsed = urlparse.urlparse(data) - if not parsed.scheme: - raise TypeCastException("uri %s has no schema" % data) - return parsed.geturl() - - def get_prep_value(self, data): - return data - - -class HTTPSURIType(object): - - def to_python(self, data): - parsed = urlparse.urlparse(data) - if not parsed.scheme: - raise TypeCastException("uri %s has no schema" % data) - if parsed.scheme != "https": - raise TypeCastException( - "uri %s does not has " - "https schema" % data) - return parsed.geturl() - - def get_prep_value(self, data): - return data - - -types = { - 'date': DateType(), - 'uri': URIType(), - 'https-uri': HTTPSURIType(), - 'translatable': TranslatableType(), -} - - -class PluggableConfig(object): - - options = {} - - def __init__(self, - adaptors=adaptors, - types=types, - format=None): - - self.config = {} - self.adaptors = adaptors - self.types = types - self._format = format - self.mtime = None - self.dirty = False - - @property - def option_dict(self): - if hasattr(self, 'options') and isinstance(self.options, dict): - return self.options.get('properties', None) - - def items(self): - """ - act like an iterator - """ - if isinstance(self.option_dict, dict): - return self.option_dict.items() - return self.options - - def validate(self, config, format=None): - """ - validate config - """ - schema = self.options - if format is None: - format = self._format - - if format: - adaptor = self.get_adaptor(self._format) - adaptor.validate(config, schema) - else: - # we really should make format mandatory... - logger.error('no format passed to validate') - - # first round of validation is ok. - # now we proceed to cast types if any specified. - self.to_python(config) - - def to_python(self, config): - """ - cast types following first type and then format indications. - """ - unseen_options = [i for i in config if i not in self.option_dict] - if unseen_options: - raise UnknownOptionException( - "Unknown options: %s" % ', '.join(unseen_options)) - - for key, value in config.items(): - _type = self.option_dict[key].get('type') - if _type is None and 'default' in self.option_dict[key]: - _type = type(self.option_dict[key]['default']) - if _type is not None: - tocast = True - if not callable(_type) and isinstance(value, _type): - tocast = False - if tocast: - try: - config[key] = _type(value) - except BaseException, e: - raise TypeCastException( - "Could not coerce %s, %s, " - "to type %s: %s" % (key, value, _type.__name__, e)) - _format = self.option_dict[key].get('format', None) - _ftype = self.types.get(_format, None) - if _ftype: - try: - config[key] = _ftype.to_python(value) - except BaseException, e: - raise TypeCastException( - "Could not coerce %s, %s, " - "to format %s: %s" % (key, value, - _ftype.__class__.__name__, - e)) - - return config - - def prep_value(self, config): - """ - the inverse of to_python method, - called just before serialization - """ - for key, value in config.items(): - _format = self.option_dict[key].get('format', None) - _ftype = self.types.get(_format, None) - if _ftype and hasattr(_ftype, 'get_prep_value'): - try: - config[key] = _ftype.get_prep_value(value) - except BaseException, e: - raise TypeCastException( - "Could not serialize %s, %s, " - "by format %s: %s" % (key, value, - _ftype.__class__.__name__, - e)) - else: - config[key] = value - return config - - # methods for adding configuration - - def get_default_values(self): - """ - return a config options from configuration defaults - """ - defaults = {} - for key, value in self.items(): - if 'default' in value: - defaults[key] = value['default'] - return copy.deepcopy(defaults) - - def get_adaptor(self, format): - """ - get specified format adaptor or - guess for a given filename - """ - adaptor = self.adaptors.get(format, None) - if adaptor: - return adaptor - - # not registered in adaptors dict, let's try all - for adaptor in self.adaptors.values(): - if format in adaptor.extensions: - return adaptor - - def filename2format(self, filename): - extension = os.path.splitext(filename)[-1] - return extension.lstrip('.') or None - - def serialize(self, filename, format=None, full=False): - if not format: - format = self._format - if not format: - format = self.filename2format(filename) - if not format: - raise Exception('Please specify a format') - # TODO: more specific exception type - - adaptor = self.get_adaptor(format) - if not adaptor: - raise Exception("Adaptor not found for format: %s" % format) - - config = copy.deepcopy(self.config) - serializable = self.prep_value(config) - adaptor.write(serializable, filename) - - if self.mtime: - self.touch_mtime(filename) - - def touch_mtime(self, filename): - mtime = self.mtime - os.utime(filename, (mtime, mtime)) - - def deserialize(self, string=None, fromfile=None, format=None): - """ - load configuration from a file or string - """ - - def _try_deserialize(): - if fromfile: - with open(fromfile, 'r') as f: - content = adaptor.read(f) - elif string: - content = adaptor.read(string) - return content - - # XXX cleanup this! - - if fromfile: - leap_assert(os.path.exists(fromfile)) - if not format: - format = self.filename2format(fromfile) - - if not format: - format = self._format - if format: - adaptor = self.get_adaptor(format) - else: - adaptor = None - - if adaptor: - content = _try_deserialize() - return content - - # no adaptor, let's try rest of adaptors - - adaptors = self.adaptors[:] - - if format: - adaptors.sort( - key=lambda x: int( - format in x.extensions), - reverse=True) - - for adaptor in adaptors: - content = _try_deserialize() - return content - - def set_dirty(self): - self.dirty = True - - def is_dirty(self): - return self.dirty - - def load(self, *args, **kwargs): - """ - load from string or file - if no string of fromfile option is given, - it will attempt to load from defaults - defined in the schema. - """ - string = args[0] if args else None - fromfile = kwargs.get("fromfile", None) - mtime = kwargs.pop("mtime", None) - self.mtime = mtime - content = None - - # start with defaults, so we can - # have partial values applied. - content = self.get_default_values() - if string and isinstance(string, str): - content = self.deserialize(string) - - if not string and fromfile is not None: - #import ipdb;ipdb.set_trace() - content = self.deserialize(fromfile=fromfile) - - if not content: - logger.error('no content could be loaded') - # XXX raise! - return - - # lazy evaluation until first level of nesting - # to allow lambdas with context-dependant info - # like os.path.expanduser - for k, v in content.iteritems(): - if callable(v): - content[k] = v() - - self.validate(content) - self.config = content - return True - - -def testmain(): # pragma: no cover - - from tests import test_validation as t - import pprint - - config = PluggableConfig(_format="json") - properties = copy.deepcopy(t.sample_spec) - - config.options = properties - config.load(fromfile='data.json') - - print 'config' - pprint.pprint(config.config) - - config.serialize('/tmp/testserial.json') - -if __name__ == "__main__": - testmain() diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/config/prefixers.py b/debian/python-leap.common/usr/share/pyshared/leap/common/config/prefixers.py deleted file mode 100644 index 27274bd..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/config/prefixers.py +++ /dev/null @@ -1,132 +0,0 @@ -# -*- coding: utf-8 -*- -# prefixers.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -Platform dependant configuration path prefixers -""" -import os -import platform - -from abc import ABCMeta, abstractmethod -from xdg import BaseDirectory - -from leap.common.check import leap_assert - - -class Prefixer: - """ - Abstract prefixer class - """ - - __metaclass__ = ABCMeta - - @abstractmethod - def get_path_prefix(self, standalone=False): - """ - Returns the platform dependant path prefixer - - @param standalone: if True it will return the prefix for a - standalone application. Otherwise, it will return the system - default for configuration storage. - @type standalone: bool - """ - return "" - - -def get_platform_prefixer(): - prefixer = globals()[platform.system() + "Prefixer"] - leap_assert(prefixer, "Unimplemented platform prefixer: %s" % - (platform.system(),)) - return prefixer() - - -class LinuxPrefixer(Prefixer): - """ - Config prefixer for the Linux platform - """ - - def get_path_prefix(self, standalone=False): - """ - Returns the platform dependant path prefixer. - This method expects an env variable named LEAP_CLIENT_PATH if - standalone is used. - - @param standalone: if True it will return the prefix for a - standalone application. Otherwise, it will return the system - default for configuration storage. - @type standalone: bool - """ - config_dir = BaseDirectory.xdg_config_home - if not standalone: - return config_dir - return os.path.join(os.getcwd(), "config") - - -class DarwinPrefixer(Prefixer): - """ - Config prefixer for the Darwin platform - """ - - def get_path_prefix(self, standalone=False): - """ - Returns the platform dependant path prefixer. - This method expects an env variable named LEAP_CLIENT_PATH if - standalone is used. - - @param standalone: if True it will return the prefix for a - standalone application. Otherwise, it will return the system - default for configuration storage. - @type standalone: bool - """ - config_dir = BaseDirectory.xdg_config_home - if not standalone: - return config_dir - return os.getenv(os.getcwd(), "config") - - -class WindowsPrefixer(Prefixer): - """ - Config prefixer for the Windows platform - """ - - def get_path_prefix(self, standalone=False): - """ - Returns the platform dependant path prefixer. - This method expects an env variable named LEAP_CLIENT_PATH if - standalone is used. - - @param standalone: if True it will return the prefix for a - standalone application. Otherwise, it will return the system - default for configuration storage. - @type standalone: bool - """ - config_dir = BaseDirectory.xdg_config_home - - if not standalone: - return config_dir - return os.path.join(os.getcwd(), "config") - -if __name__ == "__main__": - try: - abs_prefixer = Prefixer() - except Exception as e: - assert isinstance(e, TypeError), "Something went wrong" - print "Abstract Prefixer class is working as expected" - - linux_prefixer = LinuxPrefixer() - print linux_prefixer.get_path_prefix(standalone=True) - print linux_prefixer.get_path_prefix() diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/events/__init__.py b/debian/python-leap.common/usr/share/pyshared/leap/common/events/__init__.py deleted file mode 100644 index c949080..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/events/__init__.py +++ /dev/null @@ -1,100 +0,0 @@ -# -*- coding: utf-8 -*- -# __init__.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -An events mechanism that allows for signaling of events between components. -""" - -import logging -import socket - - -from leap.common.events import ( - events_pb2, - server, - component, - daemon, -) - - -logger = logging.getLogger(__name__) - - -def register(signal, callback, uid=None, replace=False, reqcbk=None, - timeout=1000): - """ - Register a callback to be called when the given signal is received. - - Will timeout after timeout ms if response has not been received. The - timeout arg is only used for asynch requests. If a reqcbk callback has - been supplied the timeout arg is not used. The response value will be - returned for a synch request but nothing will be returned for an asynch - request. - - @param signal: the signal that causes the callback to be launched - @type signal: int (see the `events.proto` file) - @param callback: the callback to be called when the signal is received - @type callback: function - @param uid: a unique id for the callback - @type uid: int - @param replace: should an existent callback with same uid be replaced? - @type replace: bool - @param reqcbk: a callback to be called when a response from server is - received - @type reqcbk: function - callback(leap.common.events.events_pb2.EventResponse) - @param timeout: the timeout for synch calls - @type timeout: int - - @return: the response from server for synch calls or nothing for asynch - calls - @rtype: leap.common.events.events_pb2.EventsResponse or None - """ - return component.register(signal, callback, uid, replace, reqcbk, timeout) - - -def signal(signal, content="", mac_method="", mac="", reqcbk=None, - timeout=1000): - """ - Send `signal` event to events server. - - Will timeout after timeout ms if response has not been received. The - timeout arg is only used for asynch requests. If a reqcbk callback has - been supplied the timeout arg is not used. The response value will be - returned for a synch request but nothing will be returned for an asynch - request. - - @param signal: the signal that causes the callback to be launched - @type signal: int (see the `events.proto` file) - @param content: the contents of the event signal - @type content: str - @param mac_method: the method used to auth mac - @type mac_method: str - @param mac: the content of the auth mac - @type mac: str - @param reqcbk: a callback to be called when a response from server is - received - @type reqcbk: function - callback(leap.common.events.events_pb2.EventResponse) - @param timeout: the timeout for synch calls - @type timeout: int - - @return: the response from server for synch calls or nothing for asynch - calls - @rtype: leap.common.events.events_pb2.EventsResponse or None - """ - return component.signal(signal, content, mac_method, mac, reqcbk, timeout) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/events/component.py b/debian/python-leap.common/usr/share/pyshared/leap/common/events/component.py deleted file mode 100644 index 0cf0e38..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/events/component.py +++ /dev/null @@ -1,238 +0,0 @@ -# -*- coding: utf-8 -*- -# component.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -The component end point of the events mechanism. - -Components are the communicating parties of the events mechanism. They -communicate by sending messages to a server, which in turn redistributes -messages to other components. - -When a component registers a callback for a given signal, it also tells the -server that it wants to be notified whenever signals of that type are sent by -some other component. -""" - - -import logging -import threading - - -from protobuf.socketrpc import RpcService -from leap.common.events import ( - events_pb2 as proto, - server, - daemon, - mac_auth, -) - - -logger = logging.getLogger(__name__) - - -# the `registered_callbacks` dictionary below should have the following -# format: -# -# { event_signal: [ (uid, callback), ... ], ... } -# -registered_callbacks = {} - - -class CallbackAlreadyRegistered(Exception): - """ - Raised when trying to register an already registered callback. - """ - - -def ensure_component_daemon(): - """ - Ensure the component daemon is running and listening for incoming - messages. - - @return: the daemon instance - @rtype: EventsComponentDaemon - """ - import time - daemon = EventsComponentDaemon.ensure(0) - logger.debug('ensure component daemon') - - # Because we use a random port we want to wait until a port is assigned to - # local component daemon. - - while not (EventsComponentDaemon.get_instance() and - EventsComponentDaemon.get_instance().get_port()): - time.sleep(0.1) - return daemon - - -def register(signal, callback, uid=None, replace=False, reqcbk=None, - timeout=1000): - """ - Registers a callback to be called when a specific signal event is - received. - - Will timeout after timeout ms if response has not been received. The - timeout arg is only used for asynch requests. If a reqcbk callback has - been supplied the timeout arg is not used. The response value will be - returned for a synch request but nothing will be returned for an asynch - request. - - @param signal: the signal that causes the callback to be launched - @type signal: int (see the `events.proto` file) - @param callback: the callback to be called when the signal is received - @type callback: function - callback(leap.common.events.events_pb2.SignalRequest) - @param uid: a unique id for the callback - @type uid: int - @param replace: should an existent callback with same uid be replaced? - @type replace: bool - @param reqcbk: a callback to be called when a response from server is - received - @type reqcbk: function - callback(leap.common.events.events_pb2.EventResponse) - @param timeout: the timeout for synch calls - @type timeout: int - - Might raise a CallbackAlreadyRegistered exception if there's already a - callback identified by the given uid and replace is False. - - @return: the response from server for synch calls or nothing for asynch - calls - @rtype: leap.common.events.events_pb2.EventsResponse or None - """ - ensure_component_daemon() # so we can receive registered signals - # register callback locally - if signal not in registered_callbacks: - registered_callbacks[signal] = [] - cbklist = registered_callbacks[signal] - if uid and filter(lambda (x, y): x == uid, cbklist): - if not replace: - raise CallbackAlreadyRegisteredException() - else: - registered_callbacks[signal] = filter(lambda(x, y): x != uid, - cbklist) - registered_callbacks[signal].append((uid, callback)) - # register callback on server - request = proto.RegisterRequest() - request.event = signal - request.port = EventsComponentDaemon.get_instance().get_port() - request.mac_method = mac_auth.MacMethod.MAC_NONE - request.mac = "" - service = RpcService(proto.EventsServerService_Stub, - server.SERVER_PORT, 'localhost') - logger.info("Sending registration request to server on port %s: %s", - server.SERVER_PORT, - str(request)) - return service.register(request, callback=reqcbk, timeout=timeout) - - -def signal(signal, content="", mac_method="", mac="", reqcbk=None, - timeout=1000): - """ - Send `signal` event to events server. - - Will timeout after timeout ms if response has not been received. The - timeout arg is only used for asynch requests. If a reqcbk callback has - been supplied the timeout arg is not used. The response value will be - returned for a synch request but nothing will be returned for an asynch - request. - - @param signal: the signal that causes the callback to be launched - @type signal: int (see the `events.proto` file) - @param content: the contents of the event signal - @type content: str - @param mac_method: the method used for auth mac - @type mac_method: str - @param mac: the content of the auth mac - @type mac: str - @param reqcbk: a callback to be called when a response from server is - received - @type reqcbk: function - callback(leap.common.events.events_pb2.EventResponse) - @param timeout: the timeout for synch calls - @type timeout: int - - @return: the response from server for synch calls or nothing for asynch - calls - @rtype: leap.common.events.events_pb2.EventsResponse or None - """ - request = proto.SignalRequest() - request.event = signal - request.content = content - request.mac_method = mac_method - request.mac = mac - service = RpcService(proto.EventsServerService_Stub, server.SERVER_PORT, - 'localhost') - logger.info("Sending signal to server: %s", str(request)) - return service.signal(request, callback=reqcbk, timeout=timeout) - - -class EventsComponentService(proto.EventsComponentService): - """ - Service for receiving signal events in components. - """ - - def __init__(self): - proto.EventsComponentService.__init__(self) - - def signal(self, controller, request, done): - """ - Receive a signal and run callbacks registered for that signal. - - This method is called whenever a signal request is received from - server. - - @param controller: used to mediate a single method call - @type controller: protobuf.socketrpc.controller.SocketRpcController - @param request: the request received from the component - @type request: leap.common.events.events_pb2.SignalRequest - @param done: callback to be called when done - @type done: protobuf.socketrpc.server.Callback - """ - logger.info('Received signal from server: %s' % str(request)) - - # run registered callbacks - # TODO: verify authentication using mac in incoming message - if request.event in registered_callbacks: - for (_, cbk) in registered_callbacks[request.event]: - # callbacks should be prepared to receive a - # events_pb2.SignalRequest. - cbk(request) - - # send response back to server - response = proto.EventResponse() - response.status = proto.EventResponse.OK - done.run(response) - - -class EventsComponentDaemon(daemon.EventsSingletonDaemon): - """ - A daemon that listens for incoming events from server. - """ - - @classmethod - def ensure(cls, port): - """ - Make sure the daemon is running on the given port. - - @param port: the port in which the daemon should listen - @type port: int - - @return: a daemon instance - @rtype: EventsComponentDaemon - """ - return cls.ensure_service(port, EventsComponentService()) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/events/daemon.py b/debian/python-leap.common/usr/share/pyshared/leap/common/events/daemon.py deleted file mode 100644 index d2c7b9b..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/events/daemon.py +++ /dev/null @@ -1,208 +0,0 @@ -# -*- coding: utf-8 -*- -# daemon.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -A singleton daemon for running RPC services using protobuf.socketrpc. -""" - - -import logging -import threading - - -from protobuf.socketrpc.server import ( - SocketRpcServer, - ThreadedTCPServer, - SocketHandler, -) - - -logger = logging.getLogger(__name__) - - -class ServiceAlreadyRunningException(Exception): - """ - Raised whenever a service is already running in this process but someone - attemped to start it in a different port. - """ - - -class EventsRpcServer(SocketRpcServer): - """ - RPC server used in server and component interfaces to receive messages. - """ - - def __init__(self, port, host='localhost'): - """ - Initialize a RPC server. - - @param port: the port in which to listen for incoming messages - @type port: int - @param host: the address to bind to - @type host: str - """ - SocketRpcServer.__init__(self, port, host) - self._server = None - - def run(self): - """ - Run the server. - """ - logger.info('Running server on port %d.' % self.port) - # parent implementation does not hold the server instance, so we do it - # here. - self._server = ThreadedTCPServer((self.host, self.port), - SocketHandler, self) - # if we chose to use a random port, fetch the port number info. - if self.port is 0: - self.port = self._server.socket.getsockname()[1] - self._server.serve_forever() - - def stop(self): - """ - Stop the server. - """ - self._server.shutdown() - - -class EventsSingletonDaemon(threading.Thread): - """ - Singleton class for for launching and terminating a daemon. - - This class is used so every part of the mechanism that needs to listen for - messages can launch its own daemon (thread) to do the job. - """ - - # Singleton instance - __instance = None - - def __new__(cls, *args, **kwargs): - """ - Return a singleton instance if it exists or create and initialize one. - """ - if len(args) is not 2: - raise TypeError("__init__() takes exactly 2 arguments (%d given)" - % len(args)) - if cls.__instance is None: - cls.__instance = object.__new__( - EventsSingletonDaemon) - cls.__initialize(cls.__instance, args[0], args[1]) - return cls.__instance - - @staticmethod - def __initialize(self, port, service): - """ - Initialize a singleton daemon. - - This is a static method disguised as instance method that actually - does the initialization of the daemon instance. - - @param port: the port in which to listen for incoming messages - @type port: int - @param service: the service to provide in this daemon - @type service: google.protobuf.service.Service - """ - threading.Thread.__init__(self) - self._port = port - self._service = service - self._server = EventsRpcServer(self._port) - self._server.registerService(self._service) - self.daemon = True - - def __init__(self): - """ - Singleton placeholder initialization method. - - Initialization is made in __new__ so we can always return the same - instance upon object creation. - """ - pass - - @classmethod - def ensure(cls, port): - """ - Make sure the daemon instance is running. - - Each implementation of this method should call `self.ensure_service` - with the appropriate service from the `events.proto` definitions, and - return the daemon instance. - - @param port: the port in which the daemon should be listening - @type port: int - - @return: a daemon instance - @rtype: EventsSingletonDaemon - """ - raise NotImplementedError(self.ensure) - - @classmethod - def ensure_service(cls, port, service): - """ - Start the singleton instance if not already running. - - Might return ServiceAlreadyRunningException - - @param port: the port in which the daemon should be listening - @type port: int - - @return: a daemon instance - @rtype: EventsSingletonDaemon - """ - daemon = cls(port, service) - if not daemon.is_alive(): - daemon.start() - elif port and port != cls.__instance._port: - # service is running in this process but someone is trying to - # start it in another port - raise ServiceAlreadyRunningException( - "Service is already running in this process on port %d." - % self.__instance._port) - return daemon - - @classmethod - def get_instance(cls): - """ - Retrieve singleton instance of this daemon. - - @return: a daemon instance - @rtype: EventsSingletonDaemon - """ - return cls.__instance - - def run(self): - """ - Run the server. - """ - self._server.run() - - def stop(self): - """ - Stop the daemon. - """ - self._server.stop() - - def get_port(self): - """ - Retrieve the value of the port to which the service running in this - daemon is binded to. - - @return: the port to which the daemon is binded to - @rtype: int - """ - if self._port is 0: - self._port = self._server.port - return self._port diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/events/events_pb2.py b/debian/python-leap.common/usr/share/pyshared/leap/common/events/events_pb2.py deleted file mode 100644 index a4f1df4..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/events/events_pb2.py +++ /dev/null @@ -1,371 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! - -from google.protobuf import descriptor -from google.protobuf import message -from google.protobuf import reflection -from google.protobuf import service -from google.protobuf import service_reflection -from google.protobuf import descriptor_pb2 -# @@protoc_insertion_point(imports) - - -DESCRIPTOR = descriptor.FileDescriptor( - name='events.proto', - package='leap.common.events', - serialized_pb='\n\x0c\x65vents.proto\x12\x12leap.common.events\"\x97\x01\n\rSignalRequest\x12(\n\x05\x65vent\x18\x01 \x02(\x0e\x32\x19.leap.common.events.Event\x12\x0f\n\x07\x63ontent\x18\x02 \x02(\t\x12\x12\n\nmac_method\x18\x03 \x02(\t\x12\x0b\n\x03mac\x18\x04 \x02(\x0c\x12\x12\n\nenc_method\x18\x05 \x01(\t\x12\x16\n\x0e\x65rror_occurred\x18\x06 \x01(\x08\"j\n\x0fRegisterRequest\x12(\n\x05\x65vent\x18\x01 \x02(\x0e\x32\x19.leap.common.events.Event\x12\x0c\n\x04port\x18\x02 \x02(\x05\x12\x12\n\nmac_method\x18\x03 \x02(\t\x12\x0b\n\x03mac\x18\x04 \x02(\x0c\"\x82\x01\n\rEventResponse\x12\x38\n\x06status\x18\x01 \x02(\x0e\x32(.leap.common.events.EventResponse.Status\x12\x0e\n\x06result\x18\x02 \x01(\t\"\'\n\x06Status\x12\x06\n\x02OK\x10\x01\x12\n\n\x06UNAUTH\x10\x02\x12\t\n\x05\x45RROR\x10\x03*\xe7\x02\n\x05\x45vent\x12\x15\n\x11\x43LIENT_SESSION_ID\x10\x01\x12\x0e\n\nCLIENT_UID\x10\x02\x12\x19\n\x15SOLEDAD_CREATING_KEYS\x10\x03\x12\x1e\n\x1aSOLEDAD_DONE_CREATING_KEYS\x10\x04\x12\x1a\n\x16SOLEDAD_UPLOADING_KEYS\x10\x05\x12\x1f\n\x1bSOLEDAD_DONE_UPLOADING_KEYS\x10\x06\x12\x1c\n\x18SOLEDAD_DOWNLOADING_KEYS\x10\x07\x12!\n\x1dSOLEDAD_DONE_DOWNLOADING_KEYS\x10\x08\x12\x1c\n\x18SOLEDAD_NEW_DATA_TO_SYNC\x10\t\x12\x1a\n\x16SOLEDAD_DONE_DATA_SYNC\x10\n\x12\x17\n\x13UPDATER_NEW_UPDATES\x10\x0b\x12\x19\n\x15UPDATER_DONE_UPDATING\x10\x0c\x12\x10\n\x0cRAISE_WINDOW\x10\r2\xb9\x01\n\x13\x45ventsServerService\x12R\n\x08register\x12#.leap.common.events.RegisterRequest\x1a!.leap.common.events.EventResponse\x12N\n\x06signal\x12!.leap.common.events.SignalRequest\x1a!.leap.common.events.EventResponse2h\n\x16\x45ventsComponentService\x12N\n\x06signal\x12!.leap.common.events.SignalRequest\x1a!.leap.common.events.EventResponseB\x03\x90\x01\x01') - -_EVENT = descriptor.EnumDescriptor( - name='Event', - full_name='leap.common.events.Event', - filename=None, - file=DESCRIPTOR, - values=[ - descriptor.EnumValueDescriptor( - name='CLIENT_SESSION_ID', index=0, number=1, - options=None, - type=None), - descriptor.EnumValueDescriptor( - name='CLIENT_UID', index=1, number=2, - options=None, - type=None), - descriptor.EnumValueDescriptor( - name='SOLEDAD_CREATING_KEYS', index=2, number=3, - options=None, - type=None), - descriptor.EnumValueDescriptor( - name='SOLEDAD_DONE_CREATING_KEYS', index=3, number=4, - options=None, - type=None), - descriptor.EnumValueDescriptor( - name='SOLEDAD_UPLOADING_KEYS', index=4, number=5, - options=None, - type=None), - descriptor.EnumValueDescriptor( - name='SOLEDAD_DONE_UPLOADING_KEYS', index=5, number=6, - options=None, - type=None), - descriptor.EnumValueDescriptor( - name='SOLEDAD_DOWNLOADING_KEYS', index=6, number=7, - options=None, - type=None), - descriptor.EnumValueDescriptor( - name='SOLEDAD_DONE_DOWNLOADING_KEYS', index=7, number=8, - options=None, - type=None), - descriptor.EnumValueDescriptor( - name='SOLEDAD_NEW_DATA_TO_SYNC', index=8, number=9, - options=None, - type=None), - descriptor.EnumValueDescriptor( - name='SOLEDAD_DONE_DATA_SYNC', index=9, number=10, - options=None, - type=None), - descriptor.EnumValueDescriptor( - name='UPDATER_NEW_UPDATES', index=10, number=11, - options=None, - type=None), - descriptor.EnumValueDescriptor( - name='UPDATER_DONE_UPDATING', index=11, number=12, - options=None, - type=None), - descriptor.EnumValueDescriptor( - name='RAISE_WINDOW', index=12, number=13, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=432, - serialized_end=791, -) - - -CLIENT_SESSION_ID = 1 -CLIENT_UID = 2 -SOLEDAD_CREATING_KEYS = 3 -SOLEDAD_DONE_CREATING_KEYS = 4 -SOLEDAD_UPLOADING_KEYS = 5 -SOLEDAD_DONE_UPLOADING_KEYS = 6 -SOLEDAD_DOWNLOADING_KEYS = 7 -SOLEDAD_DONE_DOWNLOADING_KEYS = 8 -SOLEDAD_NEW_DATA_TO_SYNC = 9 -SOLEDAD_DONE_DATA_SYNC = 10 -UPDATER_NEW_UPDATES = 11 -UPDATER_DONE_UPDATING = 12 -RAISE_WINDOW = 13 - - -_EVENTRESPONSE_STATUS = descriptor.EnumDescriptor( - name='Status', - full_name='leap.common.events.EventResponse.Status', - filename=None, - file=DESCRIPTOR, - values=[ - descriptor.EnumValueDescriptor( - name='OK', index=0, number=1, - options=None, - type=None), - descriptor.EnumValueDescriptor( - name='UNAUTH', index=1, number=2, - options=None, - type=None), - descriptor.EnumValueDescriptor( - name='ERROR', index=2, number=3, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=390, - serialized_end=429, -) - - -_SIGNALREQUEST = descriptor.Descriptor( - name='SignalRequest', - full_name='leap.common.events.SignalRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - descriptor.FieldDescriptor( - name='event', full_name='leap.common.events.SignalRequest.event', index=0, - number=1, type=14, cpp_type=8, label=2, - has_default_value=False, default_value=1, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - descriptor.FieldDescriptor( - name='content', full_name='leap.common.events.SignalRequest.content', index=1, - number=2, type=9, cpp_type=9, label=2, - has_default_value=False, default_value=unicode("", "utf-8"), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - descriptor.FieldDescriptor( - name='mac_method', full_name='leap.common.events.SignalRequest.mac_method', index=2, - number=3, type=9, cpp_type=9, label=2, - has_default_value=False, default_value=unicode("", "utf-8"), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - descriptor.FieldDescriptor( - name='mac', full_name='leap.common.events.SignalRequest.mac', index=3, - number=4, type=12, cpp_type=9, label=2, - has_default_value=False, default_value="", - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - descriptor.FieldDescriptor( - name='enc_method', full_name='leap.common.events.SignalRequest.enc_method', index=4, - number=5, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=unicode("", "utf-8"), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - descriptor.FieldDescriptor( - name='error_occurred', full_name='leap.common.events.SignalRequest.error_occurred', index=5, - number=6, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - serialized_start=37, - serialized_end=188, -) - - -_REGISTERREQUEST = descriptor.Descriptor( - name='RegisterRequest', - full_name='leap.common.events.RegisterRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - descriptor.FieldDescriptor( - name='event', full_name='leap.common.events.RegisterRequest.event', index=0, - number=1, type=14, cpp_type=8, label=2, - has_default_value=False, default_value=1, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - descriptor.FieldDescriptor( - name='port', full_name='leap.common.events.RegisterRequest.port', index=1, - number=2, type=5, cpp_type=1, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - descriptor.FieldDescriptor( - name='mac_method', full_name='leap.common.events.RegisterRequest.mac_method', index=2, - number=3, type=9, cpp_type=9, label=2, - has_default_value=False, default_value=unicode("", "utf-8"), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - descriptor.FieldDescriptor( - name='mac', full_name='leap.common.events.RegisterRequest.mac', index=3, - number=4, type=12, cpp_type=9, label=2, - has_default_value=False, default_value="", - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - serialized_start=190, - serialized_end=296, -) - - -_EVENTRESPONSE = descriptor.Descriptor( - name='EventResponse', - full_name='leap.common.events.EventResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - descriptor.FieldDescriptor( - name='status', full_name='leap.common.events.EventResponse.status', index=0, - number=1, type=14, cpp_type=8, label=2, - has_default_value=False, default_value=1, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - descriptor.FieldDescriptor( - name='result', full_name='leap.common.events.EventResponse.result', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=unicode("", "utf-8"), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _EVENTRESPONSE_STATUS, - ], - options=None, - is_extendable=False, - extension_ranges=[], - serialized_start=299, - serialized_end=429, -) - -_SIGNALREQUEST.fields_by_name['event'].enum_type = _EVENT -_REGISTERREQUEST.fields_by_name['event'].enum_type = _EVENT -_EVENTRESPONSE.fields_by_name['status'].enum_type = _EVENTRESPONSE_STATUS -_EVENTRESPONSE_STATUS.containing_type = _EVENTRESPONSE; -DESCRIPTOR.message_types_by_name['SignalRequest'] = _SIGNALREQUEST -DESCRIPTOR.message_types_by_name['RegisterRequest'] = _REGISTERREQUEST -DESCRIPTOR.message_types_by_name['EventResponse'] = _EVENTRESPONSE - - -class SignalRequest(message.Message): - __metaclass__ = reflection.GeneratedProtocolMessageType - DESCRIPTOR = _SIGNALREQUEST - - # @@protoc_insertion_point(class_scope:leap.common.events.SignalRequest) - - -class RegisterRequest(message.Message): - __metaclass__ = reflection.GeneratedProtocolMessageType - DESCRIPTOR = _REGISTERREQUEST - - # @@protoc_insertion_point(class_scope:leap.common.events.RegisterRequest) - - -class EventResponse(message.Message): - __metaclass__ = reflection.GeneratedProtocolMessageType - DESCRIPTOR = _EVENTRESPONSE - - # @@protoc_insertion_point(class_scope:leap.common.events.EventResponse) - - -_EVENTSSERVERSERVICE = descriptor.ServiceDescriptor( - name='EventsServerService', - full_name='leap.common.events.EventsServerService', - file=DESCRIPTOR, - index=0, - options=None, - serialized_start=794, - serialized_end=979, - methods=[ - descriptor.MethodDescriptor( - name='register', - full_name='leap.common.events.EventsServerService.register', - index=0, - containing_service=None, - input_type=_REGISTERREQUEST, - output_type=_EVENTRESPONSE, - options=None, - ), - descriptor.MethodDescriptor( - name='signal', - full_name='leap.common.events.EventsServerService.signal', - index=1, - containing_service=None, - input_type=_SIGNALREQUEST, - output_type=_EVENTRESPONSE, - options=None, - ), -]) - - -class EventsServerService(service.Service): - __metaclass__ = service_reflection.GeneratedServiceType - DESCRIPTOR = _EVENTSSERVERSERVICE - - -class EventsServerService_Stub(EventsServerService): - __metaclass__ = service_reflection.GeneratedServiceStubType - DESCRIPTOR = _EVENTSSERVERSERVICE - - -_EVENTSCOMPONENTSERVICE = descriptor.ServiceDescriptor( - name='EventsComponentService', - full_name='leap.common.events.EventsComponentService', - file=DESCRIPTOR, - index=1, - options=None, - serialized_start=981, - serialized_end=1085, - methods=[ - descriptor.MethodDescriptor( - name='signal', - full_name='leap.common.events.EventsComponentService.signal', - index=0, - containing_service=None, - input_type=_SIGNALREQUEST, - output_type=_EVENTRESPONSE, - options=None, - ), -]) - - -class EventsComponentService(service.Service): - __metaclass__ = service_reflection.GeneratedServiceType - DESCRIPTOR = _EVENTSCOMPONENTSERVICE - - -class EventsComponentService_Stub(EventsComponentService): - __metaclass__ = service_reflection.GeneratedServiceStubType - DESCRIPTOR = _EVENTSCOMPONENTSERVICE - -# @@protoc_insertion_point(module_scope) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/events/mac_auth.py b/debian/python-leap.common/usr/share/pyshared/leap/common/events/mac_auth.py deleted file mode 100644 index 49d48f7..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/events/mac_auth.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -# mac_auth.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -Authentication system for events. - -This is not implemented yet. -""" - - -class MacMethod(object): - """ - Representation of possible MAC authentication methods. - """ - - MAC_NONE = 'none' - MAC_HMAC = 'hmac' diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/events/server.py b/debian/python-leap.common/usr/share/pyshared/leap/common/events/server.py deleted file mode 100644 index 16c6513..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/events/server.py +++ /dev/null @@ -1,149 +0,0 @@ -# -*- coding: utf-8 -*- -# server.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -""" -A server for the events mechanism. - -A server can receive different kinds of requests from components: - - 1. Registration request: store component port number to be notified when - a specific signal arrives. - - 2. Signal request: redistribute the signal to registered components. -""" -import logging -import socket - - -from protobuf.socketrpc import RpcService -from leap.common.events import ( - events_pb2 as proto, - daemon, -) - - -logger = logging.getLogger(__name__) - - -SERVER_PORT = 8090 - -# the `registered_components` dictionary below should have the following -# format: -# -# { event_signal: [ port, ... ], ... } -# -registered_components = {} - - -def ensure_server(port=SERVER_PORT): - """ - Make sure the server is running on the given port. - - Attempt to connect to given local port. Upon success, assume that the - events server has already been started. Upon failure, start events server. - - @param port: the port in which server should be listening - @type port: int - - @return: the daemon instance or nothing - @rtype: EventsServerDaemon or None - """ - try: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect(('localhost', port)) - s.close() - logger.info('Server is already running on port %d.', port) - return None - except socket.error: - logger.info('Launching server on port %d.', port) - return EventsServerDaemon.ensure(port) - - -class EventsServerService(proto.EventsServerService): - """ - Service for receiving events in components. - """ - - def register(self, controller, request, done): - """ - Register a component port to be signaled when specific events come in. - - @param controller: used to mediate a single method call - @type controller: protobuf.socketrpc.controller.SocketRpcController - @param request: the request received from the component - @type request: leap.common.events.events_pb2.RegisterRequest - @param done: callback to be called when done - @type done: protobuf.socketrpc.server.Callback - """ - logger.info("Received registration request: %s" % str(request)) - # add component port to signal list - if request.event not in registered_components: - registered_components[request.event] = set([]) - registered_components[request.event].add(request.port) - # send response back to component - - logger.debug('sending response back') - response = proto.EventResponse() - response.status = proto.EventResponse.OK - done.run(response) - - def signal(self, controller, request, done): - """ - Perform an RPC call to signal all components registered to receive a - specific signal. - - @param controller: used to mediate a single method call - @type controller: protobuf.socketrpc.controller.SocketRpcController - @param request: the request received from the component - @type request: leap.common.events.events_pb2.SignalRequest - @param done: callback to be called when done - @type done: protobuf.socketrpc.server.Callback - """ - logger.info('Received signal from component: %s', str(request)) - # send signal to all registered components - # TODO: verify signal auth - if request.event in registered_components: - for port in registered_components[request.event]: - - def callback(req, resp): - logger.info("Signal received by " + str(port)) - - service = RpcService(proto.EventsComponentService_Stub, - port, 'localhost') - service.signal(request, callback=callback) - # send response back to component - response = proto.EventResponse() - response.status = proto.EventResponse.OK - done.run(response) - - -class EventsServerDaemon(daemon.EventsSingletonDaemon): - """ - Singleton class for starting an events server daemon. - """ - - @classmethod - def ensure(cls, port): - """ - Make sure the daemon is running on the given port. - - @param port: the port in which the daemon should listen - @type port: int - - @return: a daemon instance - @rtype: EventsServerDaemon - """ - return cls.ensure_service(port, EventsServerService()) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/files.py b/debian/python-leap.common/usr/share/pyshared/leap/common/files.py deleted file mode 100644 index 4c443dd..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/files.py +++ /dev/null @@ -1,126 +0,0 @@ -# -*- coding: utf-8 -*- -# files.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -""" -Implements file helper methods -""" - -import errno -import logging -import os -import stat -import time - -logger = logging.getLogger(__name__) - - -def check_and_fix_urw_only(cert): - """ - Test for 600 mode and try to set it if anything different found - - Might raise OSError - - @param cert: Certificate path - @type cert: str - """ - mode = stat.S_IMODE(os.stat(cert).st_mode) - - if mode != int('600', 8): - try: - logger.warning('Bad permission on %s attempting to set 600' % - (cert,)) - os.chmod(cert, stat.S_IRUSR | stat.S_IWUSR) - except OSError: - logger.error('Error while trying to chmod 600 %s' % - cert) - raise - - -def get_mtime(filename): - """ - Returns the modified time or None if the file doesn't exist - - @param filename: path to check - @type filename: str - - @rtype: str - """ - try: - mtime = time.ctime(os.path.getmtime(filename)) + " GMT" - return mtime - except OSError: - return None - - -def mkdir_p(path): - """ - Creates the path and all the intermediate directories that don't - exist - - Might raise OSError - - @param path: path to create - @type path: str - """ - try: - os.makedirs(path) - except OSError as exc: - if exc.errno == errno.EEXIST and os.path.isdir(path): - pass - else: - raise - - -# Twisted implementation of which -def which(name, flags=os.X_OK, path_extension="/usr/sbin:/sbin"): - """ - Search PATH for executable files with the given name. - - On newer versions of MS-Windows, the PATHEXT environment variable will be - set to the list of file extensions for files considered executable. This - will normally include things like ".EXE". This fuction will also find files - with the given name ending with any of these extensions. - - On MS-Windows the only flag that has any meaning is os.F_OK. Any other - flags will be ignored. - - @type name: C{str} - @param name: The name for which to search. - - @type flags: C{int} - @param flags: Arguments to L{os.access}. - - @rtype: C{list} - @param: A list of the full paths to files found, in the - order in which they were found. - """ - - result = [] - exts = filter(None, os.environ.get('PATHEXT', '').split(os.pathsep)) - path = os.environ.get('PATH', None) - path = path_extension + os.pathsep + path - if path is None: - return [] - parts = path.split(os.pathsep) - for p in parts: - p = os.path.join(p, name) - if os.access(p, flags): - result.append(p) - for e in exts: - pext = p + e - if os.access(pext, flags): - result.append(pext) - return result diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/__init__.py b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/__init__.py deleted file mode 100644 index d6dbb8a..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/__init__.py +++ /dev/null @@ -1,286 +0,0 @@ -# -*- coding: utf-8 -*- -# __init__.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - - -""" -Key Manager is a Nicknym agent for LEAP client. -""" - -import requests - -try: - import simplejson as json -except ImportError: - import json # noqa - -from leap.common.check import leap_assert -from leap.common.keymanager.errors import ( - KeyNotFound, - NoPasswordGiven, -) -from leap.common.keymanager.keys import ( - build_key_from_dict, -) -from leap.common.keymanager.openpgp import ( - OpenPGPKey, - OpenPGPScheme, - encrypt_sym, -) - - -TAGS_INDEX = 'by-tags' -TAGS_AND_PRIVATE_INDEX = 'by-tags-and-private' -INDEXES = { - TAGS_INDEX: ['tags'], - TAGS_AND_PRIVATE_INDEX: ['tags', 'bool(private)'], -} - - -class KeyManager(object): - - def __init__(self, address, nickserver_url, soledad, token=None): - """ - Initialize a Key Manager for user's C{address} with provider's - nickserver reachable in C{url}. - - @param address: The address of the user of this Key Manager. - @type address: str - @param url: The URL of the nickserver. - @type url: str - @param soledad: A Soledad instance for local storage of keys. - @type soledad: leap.soledad.Soledad - """ - self._address = address - self._nickserver_url = nickserver_url - self._soledad = soledad - self.token = token - self._wrapper_map = { - OpenPGPKey: OpenPGPScheme(soledad), - # other types of key will be added to this mapper. - } - self._init_indexes() - self._fetcher = requests - - # - # utilities - # - - def _key_class_from_type(self, ktype): - """ - Return key class from string representation of key type. - """ - return filter( - lambda klass: str(klass) == ktype, - self._wrapper_map).pop() - - def _init_indexes(self): - """ - Initialize the database indexes. - """ - # Ask the database for currently existing indexes. - db_indexes = dict(self._soledad.list_indexes()) - # Loop through the indexes we expect to find. - for name, expression in INDEXES.items(): - if name not in db_indexes: - # The index does not yet exist. - self._soledad.create_index(name, *expression) - continue - if expression == db_indexes[name]: - # The index exists and is up to date. - continue - # The index exists but the definition is not what expected, so we - # delete it and add the proper index expression. - self._soledad.delete_index(name) - self._soledad.create_index(name, *expression) - - def _get_dict_from_http_json(self, path): - """ - Make a GET HTTP request and return a dictionary containing the - response. - """ - response = self._fetcher.get(self._nickserver_url+path) - leap_assert(response.status_code == 200, 'Invalid response.') - leap_assert( - response.headers['content-type'].startswith('application/json') - is True, - 'Content-type is not JSON.') - return response.json() - - # - # key management - # - - def send_key(self, ktype, send_private=False, password=None): - """ - Send user's key of type C{ktype} to provider. - - Public key bound to user's is sent to provider, which will sign it and - replace any prior keys for the same address in its database. - - If C{send_private} is True, then the private key is encrypted with - C{password} and sent to server in the same request, together with a - hash string of user's address and password. The encrypted private key - will be saved in the server in a way it is publicly retrievable - through the hash string. - - @param ktype: The type of the key. - @type ktype: KeyType - - @raise httplib.HTTPException: - @raise KeyNotFound: If the key was not found both locally and in - keyserver. - """ - # prepare the public key bound to address - pubkey = self.get_key( - self._address, ktype, private=False, fetch_remote=False) - data = { - 'address': self._address, - 'keys': [ - json.loads(pubkey.get_json()), - ] - } - # prepare the private key bound to address - if send_private: - if password is None or password == '': - raise NoPasswordGiven('Can\'t send unencrypted private keys!') - privkey = self.get_key( - self._address, ktype, private=True, fetch_remote=False) - privkey = json.loads(privkey.get_json()) - privkey.key_data = encrypt_sym(privkey.key_data, password) - data['keys'].append(privkey) - self._fetcher.put( - self._nickserver_url + '/key/' + self._address, - data=data, - auth=(self._address, self._token)) - - def get_key(self, address, ktype, private=False, fetch_remote=True): - """ - Return a key of type C{ktype} bound to C{address}. - - First, search for the key in local storage. If it is not available, - then try to fetch from nickserver. - - @param address: The address bound to the key. - @type address: str - @param ktype: The type of the key. - @type ktype: KeyType - @param private: Look for a private key instead of a public one? - @type private: bool - - @return: A key of type C{ktype} bound to C{address}. - @rtype: EncryptionKey - @raise KeyNotFound: If the key was not found both locally and in - keyserver. - """ - leap_assert( - ktype in self._wrapper_map, - 'Unkown key type: %s.' % str(ktype)) - try: - return self._wrapper_map[ktype].get_key(address, private=private) - except KeyNotFound: - if fetch_remote is False: - raise - # fetch keys from server and discard unwanted types. - keys = filter(lambda k: isinstance(k, ktype), - self.fetch_keys_from_server(address)) - if len(keys) is 0: - raise KeyNotFound() - leap_assert( - len(keys) == 1, - 'Got more than one key of type %s for %s.' % - (str(ktype), address)) - self._wrapper_map[ktype].put_key(keys[0]) - return self._wrapper_map[ktype].get_key(address, private=private) - - def fetch_keys_from_server(self, address): - """ - Fetch keys bound to C{address} from nickserver. - - @param address: The address bound to the keys. - @type address: str - - @return: A list of keys bound to C{address}. - @rtype: list of EncryptionKey - @raise KeyNotFound: If the key was not found on nickserver. - @raise httplib.HTTPException: - """ - keydata = self._get_dict_from_http_json('/key/%s' % address) - leap_assert( - keydata['address'] == address, - "Fetched key for wrong address.") - keys = [] - for key in keydata['keys']: - keys.append( - build_key_from_dict( - self._key_class_from_type(key['type']), - address, - key)) - return keys - - def get_all_keys_in_local_db(self, private=False): - """ - Return all keys stored in local database. - - @return: A list with all keys in local db. - @rtype: list - """ - return map( - lambda doc: build_key_from_dict( - self._key_class_from_type(doc.content['type']), - doc.content['address'], - doc.content), - self._soledad.get_from_index( - TAGS_AND_PRIVATE_INDEX, - 'keymanager-key', - '1' if private else '0')) - - def refresh_keys(self): - """ - Fetch keys from nickserver and update them locally. - """ - addresses = set(map( - lambda doc: doc.address, - self.get_all_keys_in_local_db(private=False))) - # TODO: maybe we should not attempt to refresh our own public key? - for address in addresses: - for key in self.fetch_keys_from_server(address): - self._wrapper_map[key.__class__].put_key(key) - - def gen_key(self, ktype): - """ - Generate a key of type C{ktype} bound to the user's address. - - @param ktype: The type of the key. - @type ktype: KeyType - - @return: The generated key. - @rtype: EncryptionKey - """ - return self._wrapper_map[ktype].gen_key(self._address) - - # - # Token setter/getter - # - - def _get_token(self): - return self._token - - def _set_token(self, token): - self._token = token - - token = property( - _get_token, _set_token, doc='The auth token.') diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/errors.py b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/errors.py deleted file mode 100644 index 1cf506e..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/errors.py +++ /dev/null @@ -1,46 +0,0 @@ -# -*- coding: utf-8 -*- -# errors.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - - -""" -Errors and exceptions used by the Key Manager. -""" - - -class KeyNotFound(Exception): - """ - Raised when key was no found on keyserver. - """ - - -class KeyAlreadyExists(Exception): - """ - Raised when attempted to create a key that already exists. - """ - - -class KeyAttributesDiffer(Exception): - """ - Raised when trying to delete a key but the stored key differs from the key - passed to the delete_key() method. - """ - -class NoPasswordGiven(Exception): - """ - Raised when trying to perform some action that needs a password without - providing one. - """ diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/gpg.py b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/gpg.py deleted file mode 100644 index f3e6453..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/gpg.py +++ /dev/null @@ -1,397 +0,0 @@ -# -*- coding: utf-8 -*- -# gpgwrapper.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - - -""" -A GPG wrapper used to handle OpenPGP keys. - -This is a temporary class that will be superseded by the a revised version of -python-gnupg. -""" - - -import os -import gnupg -import re -from gnupg import ( - logger, - _is_sequence, - _make_binary_stream, -) - - -class ListPackets(): - """ - Handle status messages for --list-packets. - """ - - def __init__(self, gpg): - """ - Initialize the packet listing handling class. - - @param gpg: GPG object instance. - @type gpg: gnupg.GPG - """ - self.gpg = gpg - self.nodata = None - self.key = None - self.need_passphrase = None - self.need_passphrase_sym = None - self.userid_hint = None - - def handle_status(self, key, value): - """ - Handle one line of the --list-packets status message. - - @param key: The status message key. - @type key: str - @param value: The status message value. - @type value: str - """ - # TODO: write tests for handle_status - if key == 'NODATA': - self.nodata = True - if key == 'ENC_TO': - # This will only capture keys in our keyring. In the future we - # may want to include multiple unknown keys in this list. - self.key, _, _ = value.split() - if key == 'NEED_PASSPHRASE': - self.need_passphrase = True - if key == 'NEED_PASSPHRASE_SYM': - self.need_passphrase_sym = True - if key == 'USERID_HINT': - self.userid_hint = value.strip().split() - - -class GPGWrapper(gnupg.GPG): - """ - This is a temporary class for handling GPG requests, and should be - replaced by a more general class used throughout the project. - """ - - GNUPG_HOME = os.environ['HOME'] + "/.config/leap/gnupg" - GNUPG_BINARY = "/usr/bin/gpg" # this has to be changed based on OS - - def __init__(self, gpgbinary=GNUPG_BINARY, gnupghome=GNUPG_HOME, - verbose=False, use_agent=False, keyring=None, options=None): - """ - Initialize a GnuPG process wrapper. - - @param gpgbinary: Name for GnuPG binary executable. - @type gpgbinary: C{str} - @param gpghome: Full pathname to directory containing the public and - private keyrings. - @type gpghome: C{str} - @param keyring: Name of alternative keyring file to use. If specified, - the default keyring is not used. - @param verbose: Should some verbose info be output? - @type verbose: bool - @param use_agent: Should pass `--use-agent` to GPG binary? - @type use_agent: bool - @param keyring: Path for the keyring to use. - @type keyring: str - @options: A list of additional options to pass to the GPG binary. - @type options: list - - @raise: RuntimeError with explanation message if there is a problem - invoking gpg. - """ - gnupg.GPG.__init__(self, gnupghome=gnupghome, gpgbinary=gpgbinary, - verbose=verbose, use_agent=use_agent, - keyring=keyring, options=options) - self.result_map['list-packets'] = ListPackets - - def find_key_by_email(self, email, secret=False): - """ - Find user's key based on their email. - - @param email: Email address of key being searched for. - @type email: str - @param secret: Should we search for a secret key? - @type secret: bool - - @return: The fingerprint of the found key. - @rtype: str - """ - for key in self.list_keys(secret=secret): - for uid in key['uids']: - if re.search(email, uid): - return key - raise LookupError("GnuPG public key for email %s not found!" % email) - - def find_key_by_subkey(self, subkey, secret=False): - """ - Find user's key based on a subkey fingerprint. - - @param email: Subkey fingerprint of the key being searched for. - @type email: str - @param secret: Should we search for a secret key? - @type secret: bool - - @return: The fingerprint of the found key. - @rtype: str - """ - for key in self.list_keys(secret=secret): - for sub in key['subkeys']: - if sub[0] == subkey: - return key - raise LookupError( - "GnuPG public key for subkey %s not found!" % subkey) - - def find_key_by_keyid(self, keyid, secret=False): - """ - Find user's key based on the key ID. - - @param email: The key ID of the key being searched for. - @type email: str - @param secret: Should we search for a secret key? - @type secret: bool - - @return: The fingerprint of the found key. - @rtype: str - """ - for key in self.list_keys(secret=secret): - if keyid == key['keyid']: - return key - raise LookupError( - "GnuPG public key for keyid %s not found!" % keyid) - - def find_key_by_fingerprint(self, fingerprint, secret=False): - """ - Find user's key based on the key fingerprint. - - @param email: The fingerprint of the key being searched for. - @type email: str - @param secret: Should we search for a secret key? - @type secret: bool - - @return: The fingerprint of the found key. - @rtype: str - """ - for key in self.list_keys(secret=secret): - if fingerprint == key['fingerprint']: - return key - raise LookupError( - "GnuPG public key for fingerprint %s not found!" % fingerprint) - - def encrypt(self, data, recipient, sign=None, always_trust=True, - passphrase=None, symmetric=False): - """ - Encrypt data using GPG. - - @param data: The data to be encrypted. - @type data: str - @param recipient: The address of the public key to be used. - @type recipient: str - @param sign: Should the encrypted content be signed? - @type sign: bool - @param always_trust: Skip key validation and assume that used keys - are always fully trusted? - @type always_trust: bool - @param passphrase: The passphrase to be used if symmetric encryption - is desired. - @type passphrase: str - @param symmetric: Should we encrypt to a password? - @type symmetric: bool - - @return: An object with encrypted result in the `data` field. - @rtype: gnupg.Crypt - """ - # TODO: devise a way so we don't need to "always trust". - return gnupg.GPG.encrypt(self, data, recipient, sign=sign, - always_trust=always_trust, - passphrase=passphrase, - symmetric=symmetric, - cipher_algo='AES256') - - def decrypt(self, data, always_trust=True, passphrase=None): - """ - Decrypt data using GPG. - - @param data: The data to be decrypted. - @type data: str - @param always_trust: Skip key validation and assume that used keys - are always fully trusted? - @type always_trust: bool - @param passphrase: The passphrase to be used if symmetric encryption - is desired. - @type passphrase: str - - @return: An object with decrypted result in the `data` field. - @rtype: gnupg.Crypt - """ - # TODO: devise a way so we don't need to "always trust". - return gnupg.GPG.decrypt(self, data, always_trust=always_trust, - passphrase=passphrase) - - def send_keys(self, keyserver, *keyids): - """ - Send keys to a keyserver - - @param keyserver: The keyserver to send the keys to. - @type keyserver: str - @param keyids: The key ids to send. - @type keyids: list - - @return: A list of keys sent to server. - @rtype: gnupg.ListKeys - """ - # TODO: write tests for this. - # TODO: write a SendKeys class to handle status for this. - result = self.result_map['list'](self) - gnupg.logger.debug('send_keys: %r', keyids) - data = gnupg._make_binary_stream("", self.encoding) - args = ['--keyserver', keyserver, '--send-keys'] - args.extend(keyids) - self._handle_io(args, data, result, binary=True) - gnupg.logger.debug('send_keys result: %r', result.__dict__) - data.close() - return result - - def encrypt_file(self, file, recipients, sign=None, - always_trust=False, passphrase=None, - armor=True, output=None, symmetric=False, - cipher_algo=None): - """ - Encrypt the message read from the file-like object 'file'. - - @param file: The file to be encrypted. - @type data: file - @param recipient: The address of the public key to be used. - @type recipient: str - @param sign: Should the encrypted content be signed? - @type sign: bool - @param always_trust: Skip key validation and assume that used keys - are always fully trusted? - @type always_trust: bool - @param passphrase: The passphrase to be used if symmetric encryption - is desired. - @type passphrase: str - @param armor: Create ASCII armored output? - @type armor: bool - @param output: Path of file to write results in. - @type output: str - @param symmetric: Should we encrypt to a password? - @type symmetric: bool - @param cipher_algo: Algorithm to use. - @type cipher_algo: str - - @return: An object with encrypted result in the `data` field. - @rtype: gnupg.Crypt - """ - args = ['--encrypt'] - if symmetric: - args = ['--symmetric'] - if cipher_algo: - args.append('--cipher-algo %s' % cipher_algo) - else: - args = ['--encrypt'] - if not _is_sequence(recipients): - recipients = (recipients,) - for recipient in recipients: - args.append('--recipient "%s"' % recipient) - if armor: # create ascii-armored output - set to False for binary - args.append('--armor') - if output: # write the output to a file with the specified name - if os.path.exists(output): - os.remove(output) # to avoid overwrite confirmation message - args.append('--output "%s"' % output) - if sign: - args.append('--sign --default-key "%s"' % sign) - if always_trust: - args.append("--always-trust") - result = self.result_map['crypt'](self) - self._handle_io(args, file, result, passphrase=passphrase, binary=True) - logger.debug('encrypt result: %r', result.data) - return result - - def list_packets(self, data): - """ - List the sequence of packets. - - @param data: The data to extract packets from. - @type data: str - - @return: An object with packet info. - @rtype ListPackets - """ - args = ["--list-packets"] - result = self.result_map['list-packets'](self) - self._handle_io( - args, - _make_binary_stream(data, self.encoding), - result, - ) - return result - - def encrypted_to(self, data): - """ - Return the key to which data is encrypted to. - - @param data: The data to be examined. - @type data: str - - @return: The fingerprint of the key to which data is encrypted to. - @rtype: str - """ - # TODO: make this support multiple keys. - result = self.list_packets(data) - if not result.key: - raise LookupError( - "Content is not encrypted to a GnuPG key!") - try: - return self.find_key_by_keyid(result.key) - except: - return self.find_key_by_subkey(result.key) - - def is_encrypted_sym(self, data): - """ - Say whether some chunk of data is encrypted to a symmetric key. - - @param data: The data to be examined. - @type data: str - - @return: Whether data is encrypted to a symmetric key. - @rtype: bool - """ - result = self.list_packets(data) - return bool(result.need_passphrase_sym) - - def is_encrypted_asym(self, data): - """ - Say whether some chunk of data is encrypted to a private key. - - @param data: The data to be examined. - @type data: str - - @return: Whether data is encrypted to a private key. - @rtype: bool - """ - result = self.list_packets(data) - return bool(result.key) - - def is_encrypted(self, data): - """ - Say whether some chunk of data is encrypted to a key. - - @param data: The data to be examined. - @type data: str - - @return: Whether data is encrypted to a key. - @rtype: bool - """ - return self.is_encrypted_asym(data) or self.is_encrypted_sym(data) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/keys.py b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/keys.py deleted file mode 100644 index 2e6bfe9..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/keys.py +++ /dev/null @@ -1,230 +0,0 @@ -# -*- coding: utf-8 -*- -# keys.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - - -""" -Abstact key type and encryption scheme representations. -""" - - -try: - import simplejson as json -except ImportError: - import json # noqa -import re - - -from hashlib import sha256 -from abc import ABCMeta, abstractmethod -from leap.common.check import leap_assert - - -# -# Key handling utilities -# - -def is_address(address): - """ - Return whether the given C{address} is in the form user@provider. - - @param address: The address to be tested. - @type address: str - @return: Whether C{address} is in the form user@provider. - @rtype: bool - """ - return bool(re.match('[\w.-]+@[\w.-]+', address)) - - -def build_key_from_dict(kClass, address, kdict): - """ - Build an C{kClass} key bound to C{address} based on info in C{kdict}. - - @param address: The address bound to the key. - @type address: str - @param kdict: Dictionary with key data. - @type kdict: dict - @return: An instance of the key. - @rtype: C{kClass} - """ - leap_assert(address == kdict['address'], 'Wrong address in key data.') - return kClass( - address, - key_id=kdict['key_id'], - fingerprint=kdict['fingerprint'], - key_data=kdict['key_data'], - private=kdict['private'], - length=kdict['length'], - expiry_date=kdict['expiry_date'], - first_seen_at=kdict['first_seen_at'], - last_audited_at=kdict['last_audited_at'], - validation=kdict['validation'], # TODO: verify for validation. - ) - - -def keymanager_doc_id(ktype, address, private=False): - """ - Return the document id for the document containing a key for - C{address}. - - @param address: The type of the key. - @type address: KeyType - @param address: The address bound to the key. - @type address: str - @param private: Whether the key is private or not. - @type private: bool - @return: The document id for the document that stores a key bound to - C{address}. - @rtype: str - """ - leap_assert(is_address(address), "Wrong address format: %s" % address) - ktype = str(ktype) - visibility = 'private' if private else 'public' - return sha256('keymanager-'+address+'-'+ktype+'-'+visibility).hexdigest() - - -# -# Abstraction for encryption keys -# - -class EncryptionKey(object): - """ - Abstract class for encryption keys. - - A key is "validated" if the nicknym agent has bound the user address to a - public key. Nicknym supports three different levels of key validation: - - * Level 3 - path trusted: A path of cryptographic signatures can be traced - from a trusted key to the key under evaluation. By default, only the - provider key from the user's provider is a "trusted key". - * level 2 - provider signed: The key has been signed by a provider key for - the same domain, but the provider key is not validated using a trust - path (i.e. it is only registered) - * level 1 - registered: The key has been encountered and saved, it has no - signatures (that are meaningful to the nicknym agent). - """ - - __metaclass__ = ABCMeta - - def __init__(self, address, key_id=None, fingerprint=None, - key_data=None, private=None, length=None, expiry_date=None, - validation=None, first_seen_at=None, last_audited_at=None): - self.address = address - self.key_id = key_id - self.fingerprint = fingerprint - self.key_data = key_data - self.private = private - self.length = length - self.expiry_date = expiry_date - self.validation = validation - self.first_seen_at = first_seen_at - self.last_audited_at = last_audited_at - - def get_json(self): - """ - Return a JSON string describing this key. - - @return: The JSON string describing this key. - @rtype: str - """ - return json.dumps({ - 'address': self.address, - 'type': str(self.__class__), - 'key_id': self.key_id, - 'fingerprint': self.fingerprint, - 'key_data': self.key_data, - 'private': self.private, - 'length': self.length, - 'expiry_date': self.expiry_date, - 'validation': self.validation, - 'first_seen_at': self.first_seen_at, - 'last_audited_at': self.last_audited_at, - 'tags': ['keymanager-key'], - }) - - -# -# Encryption schemes -# - -class EncryptionScheme(object): - """ - Abstract class for Encryption Schemes. - - A wrapper for a certain encryption schemes should know how to get and put - keys in local storage using Soledad, how to generate new keys and how to - find out about possibly encrypted content. - """ - - __metaclass__ = ABCMeta - - def __init__(self, soledad): - """ - Initialize this Encryption Scheme. - - @param soledad: A Soledad instance for local storage of keys. - @type soledad: leap.soledad.Soledad - """ - self._soledad = soledad - - @abstractmethod - def get_key(self, address, private=False): - """ - Get key from local storage. - - @param address: The address bound to the key. - @type address: str - @param private: Look for a private key instead of a public one? - @type private: bool - - @return: The key bound to C{address}. - @rtype: EncryptionKey - @raise KeyNotFound: If the key was not found on local storage. - """ - pass - - @abstractmethod - def put_key(self, key): - """ - Put a key in local storage. - - @param key: The key to be stored. - @type key: EncryptionKey - """ - pass - - @abstractmethod - def gen_key(self, address): - """ - Generate a new key. - - @param address: The address bound to the key. - @type address: str - - @return: The key bound to C{address}. - @rtype: EncryptionKey - """ - pass - - @abstractmethod - def delete_key(self, key): - """ - Remove C{key} from storage. - - @param key: The key to be removed. - @type key: EncryptionKey - """ - pass diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/openpgp.py b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/openpgp.py deleted file mode 100644 index e2ffe76..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/openpgp.py +++ /dev/null @@ -1,459 +0,0 @@ -# -*- coding: utf-8 -*- -# openpgp.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - - -""" -Infrastructure for using OpenPGP keys in Key Manager. -""" - - -import re -import tempfile -import shutil - -from leap.common.check import leap_assert -from leap.common.keymanager.errors import ( - KeyNotFound, - KeyAlreadyExists, - KeyAttributesDiffer -) -from leap.common.keymanager.keys import ( - EncryptionKey, - EncryptionScheme, - is_address, - keymanager_doc_id, - build_key_from_dict, -) -from leap.common.keymanager.gpg import GPGWrapper - - -# -# Utility functions -# - -def encrypt_sym(data, passphrase): - """ - Encrypt C{data} with C{passphrase}. - - @param data: The data to be encrypted. - @type data: str - @param passphrase: The passphrase used to encrypt C{data}. - @type passphrase: str - - @return: The encrypted data. - @rtype: str - """ - - def _encrypt_cb(gpg): - return gpg.encrypt( - data, None, passphrase=passphrase, symmetric=True).data - - return _safe_call(_encrypt_cb) - - -def decrypt_sym(data, passphrase): - """ - Decrypt C{data} with C{passphrase}. - - @param data: The data to be decrypted. - @type data: str - @param passphrase: The passphrase used to decrypt C{data}. - @type passphrase: str - - @return: The decrypted data. - @rtype: str - """ - - def _decrypt_cb(gpg): - return gpg.decrypt(data, passphrase=passphrase).data - - return _safe_call(_decrypt_cb) - - -def encrypt_asym(data, key): - """ - Encrypt C{data} using public @{key}. - - @param data: The data to be encrypted. - @type data: str - @param key: The key used to encrypt. - @type key: OpenPGPKey - - @return: The encrypted data. - @rtype: str - """ - leap_assert(key.private is False, 'Key is not public.') - - def _encrypt_cb(gpg): - return gpg.encrypt( - data, key.fingerprint, symmetric=False).data - - return _safe_call(_encrypt_cb, key.key_data) - - -def decrypt_asym(data, key): - """ - Decrypt C{data} using private @{key}. - - @param data: The data to be decrypted. - @type data: str - @param key: The key used to decrypt. - @type key: OpenPGPKey - - @return: The decrypted data. - @rtype: str - """ - leap_assert(key.private is True, 'Key is not private.') - - def _decrypt_cb(gpg): - return gpg.decrypt(data).data - - return _safe_call(_decrypt_cb, key.key_data) - - -def is_encrypted(data): - """ - Return whether C{data} was encrypted using OpenPGP. - - @param data: The data we want to know about. - @type data: str - - @return: Whether C{data} was encrypted using this wrapper. - @rtype: bool - """ - - def _is_encrypted_cb(gpg): - return gpg.is_encrypted(data) - - return _safe_call(_is_encrypted_cb) - - -def is_encrypted_sym(data): - """ - Return whether C{data} was encrypted using a public OpenPGP key. - - @param data: The data we want to know about. - @type data: str - - @return: Whether C{data} was encrypted using this wrapper. - @rtype: bool - """ - - def _is_encrypted_cb(gpg): - return gpg.is_encrypted_sym(data) - - return _safe_call(_is_encrypted_cb) - - -def is_encrypted_asym(data): - """ - Return whether C{data} was asymmetrically encrypted using OpenPGP. - - @param data: The data we want to know about. - @type data: str - - @return: Whether C{data} was encrypted using this wrapper. - @rtype: bool - """ - - def _is_encrypted_cb(gpg): - return gpg.is_encrypted_asym(data) - - return _safe_call(_is_encrypted_cb) - - -def _build_key_from_gpg(address, key, key_data): - """ - Build an OpenPGPKey for C{address} based on C{key} from - local gpg storage. - - ASCII armored GPG key data has to be queried independently in this - wrapper, so we receive it in C{key_data}. - - @param address: The address bound to the key. - @type address: str - @param key: Key obtained from GPG storage. - @type key: dict - @param key_data: Key data obtained from GPG storage. - @type key_data: str - @return: An instance of the key. - @rtype: OpenPGPKey - """ - return OpenPGPKey( - address, - key_id=key['keyid'], - fingerprint=key['fingerprint'], - key_data=key_data, - private=True if key['type'] == 'sec' else False, - length=key['length'], - expiry_date=key['expires'], - validation=None, # TODO: verify for validation. - ) - - -def _build_unitary_gpgwrapper(key_data=None): - """ - Return a temporary GPG wrapper keyring containing exactly zero or one - keys. - - Temporary unitary keyrings allow the to use GPG's facilities for exactly - one key. This function creates an empty temporary keyring and imports - C{key_data} if it is not None. - - @param key_data: ASCII armored key data. - @type key_data: str - @return: A GPG wrapper with a unitary keyring. - @rtype: gnupg.GPG - """ - tmpdir = tempfile.mkdtemp() - gpg = GPGWrapper(gnupghome=tmpdir) - leap_assert(len(gpg.list_keys()) is 0, 'Keyring not empty.') - if key_data: - gpg.import_keys(key_data) - leap_assert( - len(gpg.list_keys()) is 1, - 'Unitary keyring has wrong number of keys: %d.' - % len(gpg.list_keys())) - return gpg - - -def _destroy_unitary_gpgwrapper(gpg): - """ - Securely erase a unitary keyring. - - @param gpg: A GPG wrapper instance. - @type gpg: gnupg.GPG - """ - for secret in [True, False]: - for key in gpg.list_keys(secret=secret): - gpg.delete_keys( - key['fingerprint'], - secret=secret) - leap_assert(len(gpg.list_keys()) is 0, 'Keyring not empty!') - # TODO: implement some kind of wiping of data or a more secure way that - # does not write to disk. - shutil.rmtree(gpg.gnupghome) - - -def _safe_call(callback, key_data=None, **kwargs): - """ - Run C{callback} in an unitary keyring containing C{key_data}. - - @param callback: Function whose first argument is the gpg keyring. - @type callback: function(gnupg.GPG) - @param key_data: ASCII armored key data. - @type key_data: str - @param **kwargs: Other eventual parameters for the callback. - @type **kwargs: **dict - - @return: The results of the callback. - @rtype: str or bool - """ - gpg = _build_unitary_gpgwrapper(key_data) - val = callback(gpg, **kwargs) - _destroy_unitary_gpgwrapper(gpg) - return val - - -# -# The OpenPGP wrapper -# - -class OpenPGPKey(EncryptionKey): - """ - Base class for OpenPGP keys. - """ - - -class OpenPGPScheme(EncryptionScheme): - """ - A wrapper for OpenPGP keys. - """ - - def __init__(self, soledad): - """ - Initialize the OpenPGP wrapper. - - @param soledad: A Soledad instance for key storage. - @type soledad: leap.soledad.Soledad - """ - EncryptionScheme.__init__(self, soledad) - - def gen_key(self, address): - """ - Generate an OpenPGP keypair bound to C{address}. - - @param address: The address bound to the key. - @type address: str - @return: The key bound to C{address}. - @rtype: OpenPGPKey - @raise KeyAlreadyExists: If key already exists in local database. - """ - # make sure the key does not already exist - leap_assert(is_address(address), 'Not an user address: %s' % address) - try: - self.get_key(address) - raise KeyAlreadyExists(address) - except KeyNotFound: - pass - - def _gen_key_cb(gpg): - params = gpg.gen_key_input( - key_type='RSA', - key_length=4096, - name_real=address, - name_email=address, - name_comment='Generated by LEAP Key Manager.') - gpg.gen_key(params) - pubkeys = gpg.list_keys() - # assert for new key characteristics - leap_assert( - len(pubkeys) is 1, # a unitary keyring! - 'Keyring has wrong number of keys: %d.' % len(pubkeys)) - key = gpg.list_keys(secret=True).pop() - leap_assert( - len(key['uids']) is 1, # with just one uid! - 'Wrong number of uids for key: %d.' % len(key['uids'])) - leap_assert( - re.match('.*<%s>$' % address, key['uids'][0]) is not None, - 'Key not correctly bound to address.') - # insert both public and private keys in storage - for secret in [True, False]: - key = gpg.list_keys(secret=secret).pop() - openpgp_key = _build_key_from_gpg( - address, key, - gpg.export_keys(key['fingerprint'], secret=secret)) - self.put_key(openpgp_key) - - _safe_call(_gen_key_cb) - return self.get_key(address, private=True) - - def get_key(self, address, private=False): - """ - Get key bound to C{address} from local storage. - - @param address: The address bound to the key. - @type address: str - @param private: Look for a private key instead of a public one? - @type private: bool - - @return: The key bound to C{address}. - @rtype: OpenPGPKey - @raise KeyNotFound: If the key was not found on local storage. - """ - leap_assert(is_address(address), 'Not an user address: %s' % address) - doc = self._get_key_doc(address, private) - if doc is None: - raise KeyNotFound(address) - return build_key_from_dict(OpenPGPKey, address, doc.content) - - def put_key_raw(self, data): - """ - Put key contained in raw C{data} in local storage. - - @param data: The key data to be stored. - @type data: str - """ - # TODO: add more checks for correct key data. - leap_assert(data is not None, 'Data does not represent a key.') - - def _put_key_raw_cb(gpg): - - privkey = None - pubkey = None - try: - privkey = gpg.list_keys(secret=True).pop() - except IndexError: - pass - pubkey = gpg.list_keys(secret=False).pop() # unitary keyring - # extract adress from first uid on key - match = re.match('.*<([\w.-]+@[\w.-]+)>.*', pubkey['uids'].pop()) - leap_assert(match is not None, 'No user address in key data.') - address = match.group(1) - if privkey is not None: - match = re.match( - '.*<([\w.-]+@[\w.-]+)>.*', privkey['uids'].pop()) - leap_assert(match is not None, 'No user address in key data.') - privaddress = match.group(1) - leap_assert( - address == privaddress, - 'Addresses in pub and priv key differ.') - leap_assert( - pubkey['fingerprint'] == privkey['fingerprint'], - 'Fingerprints for pub and priv key differ.') - # insert private key in storage - openpgp_privkey = _build_key_from_gpg( - address, privkey, - gpg.export_keys(privkey['fingerprint'], secret=True)) - self.put_key(openpgp_privkey) - # insert public key in storage - openpgp_pubkey = _build_key_from_gpg( - address, pubkey, - gpg.export_keys(pubkey['fingerprint'], secret=False)) - self.put_key(openpgp_pubkey) - - _safe_call(_put_key_raw_cb, data) - - def put_key(self, key): - """ - Put C{key} in local storage. - - @param key: The key to be stored. - @type key: OpenPGPKey - """ - doc = self._get_key_doc(key.address, private=key.private) - if doc is None: - self._soledad.create_doc_from_json( - key.get_json(), - doc_id=keymanager_doc_id( - OpenPGPKey, key.address, key.private)) - else: - doc.set_json(key.get_json()) - self._soledad.put_doc(doc) - - def _get_key_doc(self, address, private=False): - """ - Get the document with a key (public, by default) bound to C{address}. - - If C{private} is True, looks for a private key instead of a public. - - @param address: The address bound to the key. - @type address: str - @param private: Whether to look for a private key. - @type private: bool - @return: The document with the key or None if it does not exist. - @rtype: leap.soledad.backends.leap_backend.LeapDocument - """ - return self._soledad.get_doc( - keymanager_doc_id(OpenPGPKey, address, private)) - - def delete_key(self, key): - """ - Remove C{key} from storage. - - @param key: The key to be removed. - @type key: EncryptionKey - """ - leap_assert(key.__class__ is OpenPGPKey, 'Wrong key type.') - stored_key = self.get_key(key.address, private=key.private) - if stored_key is None: - raise KeyNotFound(key) - if stored_key.__dict__ != key.__dict__: - raise KeyAttributesDiffer(key) - doc = self._soledad.get_doc( - keymanager_doc_id(OpenPGPKey, key.address, key.private)) - self._soledad.delete_doc(doc) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/testing/__init__.py b/debian/python-leap.common/usr/share/pyshared/leap/common/testing/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/testing/basetest.py b/debian/python-leap.common/usr/share/pyshared/leap/common/testing/basetest.py deleted file mode 100644 index 65e23a9..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/testing/basetest.py +++ /dev/null @@ -1,140 +0,0 @@ -# -*- coding: utf-8 -*- -# basetest.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -""" -Common testing facilities -""" -import os -import platform -import shutil -import tempfile - -try: - import unittest2 as unittest -except ImportError: - import unittest - -from leap.common.check import leap_assert -from leap.common.files import mkdir_p, check_and_fix_urw_only - - -class BaseLeapTest(unittest.TestCase): - """ - Base Leap TestCase - """ - __name__ = "leap_test" - _system = platform.system() - - @classmethod - def setUpClass(cls): - """ - Sets up common facilities for testing this TestCase: - - custom PATH and HOME environmental variables - - creates a temporal folder to which those point. - It saves the old path and home vars so they can be restored later. - """ - cls.old_path = os.environ['PATH'] - cls.old_home = os.environ['HOME'] - cls.tempdir = tempfile.mkdtemp(prefix="leap_tests-") - cls.home = cls.tempdir - bin_tdir = os.path.join( - cls.tempdir, - 'bin') - os.environ["PATH"] = bin_tdir - os.environ["HOME"] = cls.tempdir - - @classmethod - def tearDownClass(cls): - """ - Cleanup common facilities used for testing this TestCase: - - restores the default PATH and HOME variables - - removes the temporal folder - """ - os.environ["PATH"] = cls.old_path - os.environ["HOME"] = cls.old_home - # safety check! please do not wipe my home... - # XXX needs to adapt to non-linuces - leap_assert( - cls.tempdir.startswith('/tmp/leap_tests-'), - "beware! tried to remove a dir which does not " - "live in temporal folder!") - shutil.rmtree(cls.tempdir) - - # you have to override these methods - # this way we ensure we did not put anything - # here that you can forget to call. - - def setUp(self): - """not implemented""" - raise NotImplementedError("abstract base class") - - def tearDown(self): - """not implemented""" - raise NotImplementedError("abstract base class") - - # - # helper methods - # - - def _missing_test_for_plat(self, do_raise=False): - """ - Raises NotImplementedError for this platform - if do_raise is True - - @param do_raise: flag to actually raise exception - @type do_raise: bool - """ - if do_raise: - raise NotImplementedError( - "This test is not implemented " - "for the running platform: %s" % - self._system) - - def get_tempfile(self, filename): - """ - Returns the path of a given filename - prepending the temporal dir associated with this - TestCase - - @param filename: the filename - @type filename: str - """ - return os.path.join(self.tempdir, filename) - - def touch(self, filepath): - """ - Touches a filepath, creating folders along - the way if needed. - - @param filepath: path to be touched - @type filepath: str - """ - folder, filename = os.path.split(filepath) - if not os.path.isdir(folder): - mkdir_p(folder) - self.assertTrue(os.path.isdir(folder)) - with open(filepath, 'w') as fp: - fp.write(' ') - self.assertTrue(os.path.isfile(filepath)) - - def chmod600(self, filepath): - """ - Chmods 600 a file - - @param filepath: filepath to be chmodded - @type filepath: str - """ - check_and_fix_urw_only(filepath) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/testing/https_server.py b/debian/python-leap.common/usr/share/pyshared/leap/common/testing/https_server.py deleted file mode 100644 index 08d5089..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/testing/https_server.py +++ /dev/null @@ -1,87 +0,0 @@ -# -*- coding: utf-8 -*- -# leap.common.testing.https_server.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -""" -A simple HTTPS server to be used in tests -""" -from BaseHTTPServer import HTTPServer -import os -import ssl -import SocketServer -import threading -import unittest - -_where = os.path.split(__file__)[0] - - -def where(filename): - return os.path.join(_where, filename) - - -class HTTPSServer(HTTPServer): - def server_bind(self): - SocketServer.TCPServer.server_bind(self) - self.socket = ssl.wrap_socket( - self.socket, server_side=True, - certfile=where("leaptestscert.pem"), - keyfile=where("leaptestskey.pem"), - ca_certs=where("cacert.pem"), - ssl_version=ssl.PROTOCOL_SSLv23) - - -class TestServerThread(threading.Thread): - def __init__(self, test_object, request_handler): - threading.Thread.__init__(self) - self.request_handler = request_handler - self.test_object = test_object - - def run(self): - self.server = HTTPSServer(('localhost', 0), self.request_handler) - host, port = self.server.socket.getsockname() - self.test_object.HOST, self.test_object.PORT = host, port - self.test_object.server_started.set() - self.test_object = None - try: - self.server.serve_forever(0.05) - finally: - self.server.server_close() - - def stop(self): - self.server.shutdown() - - -class BaseHTTPSServerTestCase(unittest.TestCase): - """ - derived classes need to implement a request_handler - """ - def setUp(self): - self.server_started = threading.Event() - self.thread = TestServerThread(self, self.request_handler) - self.thread.start() - self.server_started.wait() - - def tearDown(self): - self.thread.stop() - - def get_server(self): - host, port = self.HOST, self.PORT - if host == "127.0.0.1": - host = "localhost" - return "%s:%s" % (host, port) - - -if __name__ == "__main__": - unittest.main() diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/testing/test_basetest.py b/debian/python-leap.common/usr/share/pyshared/leap/common/testing/test_basetest.py deleted file mode 100644 index 220e28d..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/testing/test_basetest.py +++ /dev/null @@ -1,140 +0,0 @@ -# -*- coding: utf-8 -*- -# test_basetest.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -""" -Unittests for BaseLeapTest ...becase it's oh so meta -""" -try: - import unittest2 as unittest -except ImportError: - import unittest - -import os -import StringIO - -from leap.common.testing.basetest import BaseLeapTest - -_tempdir = None # global for tempdir checking - - -class _TestCaseRunner(object): - """ - TestCaseRunner used to run BaseLeapTest - """ - def run_testcase(self, testcase=None): - """ - Runs a given TestCase - - @param testcase: the testcase - @type testcase: unittest.TestCase - """ - if not testcase: - return None - loader = unittest.TestLoader() - suite = loader.loadTestsFromTestCase(testcase) - - # Create runner, and run testcase - io = StringIO.StringIO() - runner = unittest.TextTestRunner(stream=io) - results = runner.run(suite) - return results - - -class TestAbstractBaseLeapTest(unittest.TestCase, _TestCaseRunner): - """ - TestCase for BaseLeapTest abs - """ - def test_abstract_base_class(self): - """ - Test errors raised when setup/teardown not overloaded - """ - class _BaseTest(BaseLeapTest): - def test_dummy_method(self): - pass - - def test_tautology(self): - assert True - - results = self.run_testcase(_BaseTest) - - # should be 2 errors: NotImplemented - # raised for setUp/tearDown - self.assertEquals(results.testsRun, 2) - self.assertEquals(len(results.failures), 0) - self.assertEquals(len(results.errors), 2) - - -class TestInitBaseLeapTest(BaseLeapTest): - """ - TestCase for testing initialization of BaseLeapTest - """ - - def setUp(self): - """nuke it""" - pass - - def tearDown(self): - """nuke it""" - pass - - def test_path_is_changed(self): - """tests whether we have changed the PATH env var""" - os_path = os.environ['PATH'] - self.assertTrue(os_path.startswith(self.tempdir)) - - def test_old_path_is_saved(self): - """tests whether we restore the PATH env var""" - self.assertTrue(len(self.old_path) > 1) - - -class TestCleanedBaseLeapTest(unittest.TestCase, _TestCaseRunner): - """ - TestCase for testing tempdir creation and cleanup - """ - - def test_tempdir_is_cleaned_after_tests(self): - """ - test if a TestCase derived from BaseLeapTest creates and cleans the - temporal dir - """ - class _BaseTest(BaseLeapTest): - def setUp(self): - """set global _tempdir to this instance tempdir""" - global _tempdir - _tempdir = self.tempdir - - def tearDown(self): - """nothing""" - pass - - def test_tempdir_created(self): - """test if tempdir was created""" - self.assertTrue(os.path.isdir(self.tempdir)) - - def test_tempdir_created_on_setupclass(self): - """test if tempdir is the one created by setupclass""" - self.assertEqual(_tempdir, self.tempdir) - - results = self.run_testcase(_BaseTest) - self.assertEquals(results.testsRun, 2) - self.assertEquals(len(results.failures), 0) - self.assertEquals(len(results.errors), 0) - - # did we cleaned the tempdir? - self.assertFalse(os.path.isdir(_tempdir)) - -if __name__ == "__main__": - unittest.main() diff --git a/debian/python-leap.common/usr/share/python/ns/python-leap.common b/debian/python-leap.common/usr/share/python/ns/python-leap.common deleted file mode 100644 index 2905ed7..0000000 --- a/debian/python-leap.common/usr/share/python/ns/python-leap.common +++ /dev/null @@ -1 +0,0 @@ -leap -- cgit v1.2.3 From c790ebedc391b4727cdac5785050514cecb81d33 Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Thu, 30 May 2013 23:00:54 -0400 Subject: fix field in copyright --- debian/copyright | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/copyright b/debian/copyright index a132a29..353207a 100644 --- a/debian/copyright +++ b/debian/copyright @@ -4,7 +4,7 @@ Upstream-Contact: kali@leap.se Source: Files: * - Copyright (C) 2013 LEAP +Copyright: Copyright (C) 2013 LEAP License: GPL-3+ Files: debian/* -- cgit v1.2.3 From cc8dd1e7f3d64ae6d7d69ed08a4346bfabfe7b22 Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Tue, 4 Jun 2013 16:20:14 -0400 Subject: prepare to upgrade to 0.2.5 --- debian/changelog | 7 +++++++ debian/rules | 2 -- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index f448d18..098528e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +leap-common (0.2.5) unstable; urgency=low + + * Upgrade to 0.2.5 + * Change to native package + + -- Micah Anderson Tue, 04 Jun 2013 16:16:29 -0400 + leap-common (0.2.3-dev1) unstable; urgency=low * Initial debianization diff --git a/debian/rules b/debian/rules index e7c975e..7bf6077 100755 --- a/debian/rules +++ b/debian/rules @@ -3,5 +3,3 @@ %: dh $@ --with python2 --buildsystem=python_distutils -# FIXME: need to insert an extra build step to generate from the inputs the proto (and build dep on protobuf-c-compiler) - -- cgit v1.2.3 From 9070d74a47158f5749c5e16b8d9d9e62a55d07df Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Thu, 15 Aug 2013 10:12:25 -0400 Subject: regenerate things with protobuf 2.4.1 --- debian/changelog | 6 ++ src/leap/common/events/events_pb2.py | 136 +++++++++++++++++------------------ 2 files changed, 71 insertions(+), 71 deletions(-) diff --git a/debian/changelog b/debian/changelog index 20f0576..7a26298 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +leap-common (0.3.0.1) unstable; urgency=low + + * Regenerate events_pb2.py with protobuf 2.4.1 + + -- Micah Anderson Thu, 15 Aug 2013 10:11:43 -0400 + leap-common (0.3.0) unstable; urgency=low * Update to 0.3.0 diff --git a/src/leap/common/events/events_pb2.py b/src/leap/common/events/events_pb2.py index 274514c..e25c7da 100644 --- a/src/leap/common/events/events_pb2.py +++ b/src/leap/common/events/events_pb2.py @@ -1,76 +1,74 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! -# source: events.proto -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import service as _service +from google.protobuf import descriptor +from google.protobuf import message +from google.protobuf import reflection +from google.protobuf import service from google.protobuf import service_reflection from google.protobuf import descriptor_pb2 # @@protoc_insertion_point(imports) -DESCRIPTOR = _descriptor.FileDescriptor( +DESCRIPTOR = descriptor.FileDescriptor( name='events.proto', package='leap.common.events', serialized_pb='\n\x0c\x65vents.proto\x12\x12leap.common.events\"\x97\x01\n\rSignalRequest\x12(\n\x05\x65vent\x18\x01 \x02(\x0e\x32\x19.leap.common.events.Event\x12\x0f\n\x07\x63ontent\x18\x02 \x02(\t\x12\x12\n\nmac_method\x18\x03 \x02(\t\x12\x0b\n\x03mac\x18\x04 \x02(\x0c\x12\x12\n\nenc_method\x18\x05 \x01(\t\x12\x16\n\x0e\x65rror_occurred\x18\x06 \x01(\x08\"j\n\x0fRegisterRequest\x12(\n\x05\x65vent\x18\x01 \x02(\x0e\x32\x19.leap.common.events.Event\x12\x0c\n\x04port\x18\x02 \x02(\x05\x12\x12\n\nmac_method\x18\x03 \x02(\t\x12\x0b\n\x03mac\x18\x04 \x02(\x0c\"l\n\x11UnregisterRequest\x12(\n\x05\x65vent\x18\x01 \x02(\x0e\x32\x19.leap.common.events.Event\x12\x0c\n\x04port\x18\x02 \x02(\x05\x12\x12\n\nmac_method\x18\x03 \x02(\t\x12\x0b\n\x03mac\x18\x04 \x02(\x0c\"\r\n\x0bPingRequest\"\x82\x01\n\rEventResponse\x12\x38\n\x06status\x18\x01 \x02(\x0e\x32(.leap.common.events.EventResponse.Status\x12\x0e\n\x06result\x18\x02 \x01(\t\"\'\n\x06Status\x12\x06\n\x02OK\x10\x01\x12\n\n\x06UNAUTH\x10\x02\x12\t\n\x05\x45RROR\x10\x03*\xe7\x02\n\x05\x45vent\x12\x15\n\x11\x43LIENT_SESSION_ID\x10\x01\x12\x0e\n\nCLIENT_UID\x10\x02\x12\x19\n\x15SOLEDAD_CREATING_KEYS\x10\x03\x12\x1e\n\x1aSOLEDAD_DONE_CREATING_KEYS\x10\x04\x12\x1a\n\x16SOLEDAD_UPLOADING_KEYS\x10\x05\x12\x1f\n\x1bSOLEDAD_DONE_UPLOADING_KEYS\x10\x06\x12\x1c\n\x18SOLEDAD_DOWNLOADING_KEYS\x10\x07\x12!\n\x1dSOLEDAD_DONE_DOWNLOADING_KEYS\x10\x08\x12\x1c\n\x18SOLEDAD_NEW_DATA_TO_SYNC\x10\t\x12\x1a\n\x16SOLEDAD_DONE_DATA_SYNC\x10\n\x12\x17\n\x13UPDATER_NEW_UPDATES\x10\x0b\x12\x19\n\x15UPDATER_DONE_UPDATING\x10\x0c\x12\x10\n\x0cRAISE_WINDOW\x10\r2\xdd\x02\n\x13\x45ventsServerService\x12J\n\x04ping\x12\x1f.leap.common.events.PingRequest\x1a!.leap.common.events.EventResponse\x12R\n\x08register\x12#.leap.common.events.RegisterRequest\x1a!.leap.common.events.EventResponse\x12V\n\nunregister\x12%.leap.common.events.UnregisterRequest\x1a!.leap.common.events.EventResponse\x12N\n\x06signal\x12!.leap.common.events.SignalRequest\x1a!.leap.common.events.EventResponse2\xb1\x01\n\x13\x45ventsClientService\x12J\n\x04ping\x12\x1f.leap.common.events.PingRequest\x1a!.leap.common.events.EventResponse\x12N\n\x06signal\x12!.leap.common.events.SignalRequest\x1a!.leap.common.events.EventResponseB\x03\x90\x01\x01') -_EVENT = _descriptor.EnumDescriptor( +_EVENT = descriptor.EnumDescriptor( name='Event', full_name='leap.common.events.Event', filename=None, file=DESCRIPTOR, values=[ - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='CLIENT_SESSION_ID', index=0, number=1, options=None, type=None), - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='CLIENT_UID', index=1, number=2, options=None, type=None), - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='SOLEDAD_CREATING_KEYS', index=2, number=3, options=None, type=None), - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='SOLEDAD_DONE_CREATING_KEYS', index=3, number=4, options=None, type=None), - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='SOLEDAD_UPLOADING_KEYS', index=4, number=5, options=None, type=None), - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='SOLEDAD_DONE_UPLOADING_KEYS', index=5, number=6, options=None, type=None), - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='SOLEDAD_DOWNLOADING_KEYS', index=6, number=7, options=None, type=None), - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='SOLEDAD_DONE_DOWNLOADING_KEYS', index=7, number=8, options=None, type=None), - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='SOLEDAD_NEW_DATA_TO_SYNC', index=8, number=9, options=None, type=None), - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='SOLEDAD_DONE_DATA_SYNC', index=9, number=10, options=None, type=None), - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='UPDATER_NEW_UPDATES', index=10, number=11, options=None, type=None), - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='UPDATER_DONE_UPDATING', index=11, number=12, options=None, type=None), - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='RAISE_WINDOW', index=12, number=13, options=None, type=None), @@ -81,7 +79,7 @@ _EVENT = _descriptor.EnumDescriptor( serialized_end=916, ) -Event = enum_type_wrapper.EnumTypeWrapper(_EVENT) + CLIENT_SESSION_ID = 1 CLIENT_UID = 2 SOLEDAD_CREATING_KEYS = 3 @@ -97,21 +95,21 @@ UPDATER_DONE_UPDATING = 12 RAISE_WINDOW = 13 -_EVENTRESPONSE_STATUS = _descriptor.EnumDescriptor( +_EVENTRESPONSE_STATUS = descriptor.EnumDescriptor( name='Status', full_name='leap.common.events.EventResponse.Status', filename=None, file=DESCRIPTOR, values=[ - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='OK', index=0, number=1, options=None, type=None), - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='UNAUTH', index=1, number=2, options=None, type=None), - _descriptor.EnumValueDescriptor( + descriptor.EnumValueDescriptor( name='ERROR', index=2, number=3, options=None, type=None), @@ -123,49 +121,49 @@ _EVENTRESPONSE_STATUS = _descriptor.EnumDescriptor( ) -_SIGNALREQUEST = _descriptor.Descriptor( +_SIGNALREQUEST = descriptor.Descriptor( name='SignalRequest', full_name='leap.common.events.SignalRequest', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='event', full_name='leap.common.events.SignalRequest.event', index=0, number=1, type=14, cpp_type=8, label=2, has_default_value=False, default_value=1, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='content', full_name='leap.common.events.SignalRequest.content', index=1, number=2, type=9, cpp_type=9, label=2, has_default_value=False, default_value=unicode("", "utf-8"), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='mac_method', full_name='leap.common.events.SignalRequest.mac_method', index=2, number=3, type=9, cpp_type=9, label=2, has_default_value=False, default_value=unicode("", "utf-8"), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='mac', full_name='leap.common.events.SignalRequest.mac', index=3, number=4, type=12, cpp_type=9, label=2, has_default_value=False, default_value="", message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='enc_method', full_name='leap.common.events.SignalRequest.enc_method', index=4, number=5, type=9, cpp_type=9, label=1, has_default_value=False, default_value=unicode("", "utf-8"), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='error_occurred', full_name='leap.common.events.SignalRequest.error_occurred', index=5, number=6, type=8, cpp_type=7, label=1, has_default_value=False, default_value=False, @@ -186,35 +184,35 @@ _SIGNALREQUEST = _descriptor.Descriptor( ) -_REGISTERREQUEST = _descriptor.Descriptor( +_REGISTERREQUEST = descriptor.Descriptor( name='RegisterRequest', full_name='leap.common.events.RegisterRequest', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='event', full_name='leap.common.events.RegisterRequest.event', index=0, number=1, type=14, cpp_type=8, label=2, has_default_value=False, default_value=1, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='port', full_name='leap.common.events.RegisterRequest.port', index=1, number=2, type=5, cpp_type=1, label=2, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='mac_method', full_name='leap.common.events.RegisterRequest.mac_method', index=2, number=3, type=9, cpp_type=9, label=2, has_default_value=False, default_value=unicode("", "utf-8"), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='mac', full_name='leap.common.events.RegisterRequest.mac', index=3, number=4, type=12, cpp_type=9, label=2, has_default_value=False, default_value="", @@ -235,35 +233,35 @@ _REGISTERREQUEST = _descriptor.Descriptor( ) -_UNREGISTERREQUEST = _descriptor.Descriptor( +_UNREGISTERREQUEST = descriptor.Descriptor( name='UnregisterRequest', full_name='leap.common.events.UnregisterRequest', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='event', full_name='leap.common.events.UnregisterRequest.event', index=0, number=1, type=14, cpp_type=8, label=2, has_default_value=False, default_value=1, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='port', full_name='leap.common.events.UnregisterRequest.port', index=1, number=2, type=5, cpp_type=1, label=2, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='mac_method', full_name='leap.common.events.UnregisterRequest.mac_method', index=2, number=3, type=9, cpp_type=9, label=2, has_default_value=False, default_value=unicode("", "utf-8"), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='mac', full_name='leap.common.events.UnregisterRequest.mac', index=3, number=4, type=12, cpp_type=9, label=2, has_default_value=False, default_value="", @@ -284,7 +282,7 @@ _UNREGISTERREQUEST = _descriptor.Descriptor( ) -_PINGREQUEST = _descriptor.Descriptor( +_PINGREQUEST = descriptor.Descriptor( name='PingRequest', full_name='leap.common.events.PingRequest', filename=None, @@ -305,21 +303,21 @@ _PINGREQUEST = _descriptor.Descriptor( ) -_EVENTRESPONSE = _descriptor.Descriptor( +_EVENTRESPONSE = descriptor.Descriptor( name='EventResponse', full_name='leap.common.events.EventResponse', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='status', full_name='leap.common.events.EventResponse.status', index=0, number=1, type=14, cpp_type=8, label=2, has_default_value=False, default_value=1, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), - _descriptor.FieldDescriptor( + descriptor.FieldDescriptor( name='result', full_name='leap.common.events.EventResponse.result', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=unicode("", "utf-8"), @@ -352,46 +350,42 @@ DESCRIPTOR.message_types_by_name['PingRequest'] = _PINGREQUEST DESCRIPTOR.message_types_by_name['EventResponse'] = _EVENTRESPONSE -class SignalRequest(_message.Message): - __metaclass__ = _reflection.GeneratedProtocolMessageType +class SignalRequest(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType DESCRIPTOR = _SIGNALREQUEST # @@protoc_insertion_point(class_scope:leap.common.events.SignalRequest) -class RegisterRequest(_message.Message): - __metaclass__ = _reflection.GeneratedProtocolMessageType +class RegisterRequest(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType DESCRIPTOR = _REGISTERREQUEST # @@protoc_insertion_point(class_scope:leap.common.events.RegisterRequest) -class UnregisterRequest(_message.Message): - __metaclass__ = _reflection.GeneratedProtocolMessageType +class UnregisterRequest(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType DESCRIPTOR = _UNREGISTERREQUEST # @@protoc_insertion_point(class_scope:leap.common.events.UnregisterRequest) -class PingRequest(_message.Message): - __metaclass__ = _reflection.GeneratedProtocolMessageType +class PingRequest(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType DESCRIPTOR = _PINGREQUEST # @@protoc_insertion_point(class_scope:leap.common.events.PingRequest) -class EventResponse(_message.Message): - __metaclass__ = _reflection.GeneratedProtocolMessageType +class EventResponse(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType DESCRIPTOR = _EVENTRESPONSE # @@protoc_insertion_point(class_scope:leap.common.events.EventResponse) -DESCRIPTOR.has_options = True -DESCRIPTOR._options = _descriptor._ParseOptions( - descriptor_pb2.FileOptions(), '\220\001\001') - -_EVENTSSERVERSERVICE = _descriptor.ServiceDescriptor( +_EVENTSSERVERSERVICE = descriptor.ServiceDescriptor( name='EventsServerService', full_name='leap.common.events.EventsServerService', file=DESCRIPTOR, @@ -400,7 +394,7 @@ _EVENTSSERVERSERVICE = _descriptor.ServiceDescriptor( serialized_start=919, serialized_end=1268, methods=[ - _descriptor.MethodDescriptor( + descriptor.MethodDescriptor( name='ping', full_name='leap.common.events.EventsServerService.ping', index=0, @@ -409,7 +403,7 @@ _EVENTSSERVERSERVICE = _descriptor.ServiceDescriptor( output_type=_EVENTRESPONSE, options=None, ), - _descriptor.MethodDescriptor( + descriptor.MethodDescriptor( name='register', full_name='leap.common.events.EventsServerService.register', index=1, @@ -418,7 +412,7 @@ _EVENTSSERVERSERVICE = _descriptor.ServiceDescriptor( output_type=_EVENTRESPONSE, options=None, ), - _descriptor.MethodDescriptor( + descriptor.MethodDescriptor( name='unregister', full_name='leap.common.events.EventsServerService.unregister', index=2, @@ -427,7 +421,7 @@ _EVENTSSERVERSERVICE = _descriptor.ServiceDescriptor( output_type=_EVENTRESPONSE, options=None, ), - _descriptor.MethodDescriptor( + descriptor.MethodDescriptor( name='signal', full_name='leap.common.events.EventsServerService.signal', index=3, @@ -439,7 +433,7 @@ _EVENTSSERVERSERVICE = _descriptor.ServiceDescriptor( ]) -class EventsServerService(_service.Service): +class EventsServerService(service.Service): __metaclass__ = service_reflection.GeneratedServiceType DESCRIPTOR = _EVENTSSERVERSERVICE @@ -449,7 +443,7 @@ class EventsServerService_Stub(EventsServerService): DESCRIPTOR = _EVENTSSERVERSERVICE -_EVENTSCLIENTSERVICE = _descriptor.ServiceDescriptor( +_EVENTSCLIENTSERVICE = descriptor.ServiceDescriptor( name='EventsClientService', full_name='leap.common.events.EventsClientService', file=DESCRIPTOR, @@ -458,7 +452,7 @@ _EVENTSCLIENTSERVICE = _descriptor.ServiceDescriptor( serialized_start=1271, serialized_end=1448, methods=[ - _descriptor.MethodDescriptor( + descriptor.MethodDescriptor( name='ping', full_name='leap.common.events.EventsClientService.ping', index=0, @@ -467,7 +461,7 @@ _EVENTSCLIENTSERVICE = _descriptor.ServiceDescriptor( output_type=_EVENTRESPONSE, options=None, ), - _descriptor.MethodDescriptor( + descriptor.MethodDescriptor( name='signal', full_name='leap.common.events.EventsClientService.signal', index=1, @@ -479,7 +473,7 @@ _EVENTSCLIENTSERVICE = _descriptor.ServiceDescriptor( ]) -class EventsClientService(_service.Service): +class EventsClientService(service.Service): __metaclass__ = service_reflection.GeneratedServiceType DESCRIPTOR = _EVENTSCLIENTSERVICE -- cgit v1.2.3 From 0d5efa5c84531b740f5985f7b90144e66f3279f4 Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Thu, 22 Aug 2013 15:18:02 -0400 Subject: prepare for new version --- debian/changelog | 7 +++++++ debian/control | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 7a26298..6dd7a9f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +leap-common (0.3.1) unstable; urgency=low + + * Merge changes for new version + * Update protobuf dependency to be more accurate + + -- Micah Anderson Thu, 22 Aug 2013 15:16:23 -0400 + leap-common (0.3.0.1) unstable; urgency=low * Regenerate events_pb2.py with protobuf 2.4.1 diff --git a/debian/control b/debian/control index 02f1b79..734525d 100644 --- a/debian/control +++ b/debian/control @@ -7,7 +7,7 @@ Standards-Version: 3.9.4 Package: python-leap-common Architecture: all -Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-xdg, python-protobuf, python-protobuf.socketrpc, +Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-xdg, python-protobuf (>=2.4.1), python-protobuf.socketrpc, python-dateutil, python-autopep8, python-openssl, python-gnupg Description: Common python files needed by LEAP projects. This package contains common python functions that are needed -- cgit v1.2.3 From aef677558b3aab7e0c69ed7bc318cabad841e245 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 26 Aug 2013 21:56:17 +0200 Subject: bump changelog --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 7a26298..7a591d6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +leap-common (0.3.1) unstable; urgency=low + + * Update to 0.3.1 + + -- Ben Carrillo Mon, 26 Aug 2013 21:55:19 +0200 + leap-common (0.3.0.1) unstable; urgency=low * Regenerate events_pb2.py with protobuf 2.4.1 -- cgit v1.2.3 From 08d9cd69e8f3e58d38a176ce4983e88fa00d693a Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 12 Sep 2013 13:59:15 +0200 Subject: add MANIFEST to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e0d60cb..25876cf 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ *.swo dist/ build/ +MANIFEST -- cgit v1.2.3 From 5ecf2a24e7273f8b44260a7c4957d161442d51d6 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 12 Sep 2013 14:01:08 +0200 Subject: bump version, add dirspec dependency --- debian/changelog | 6 ++++++ debian/control | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 31244ca..59855bc 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +leap-common (0.3.2) unstable; urgency=low + + * Update to 0.3.2 + + -- Ben Carrillo Thu, 12 Sep 2013 14:00:01 +0200 + leap-common (0.3.1) unstable; urgency=low * Update protobuf dependency to be more accurate diff --git a/debian/control b/debian/control index 734525d..6802aab 100644 --- a/debian/control +++ b/debian/control @@ -7,7 +7,7 @@ Standards-Version: 3.9.4 Package: python-leap-common Architecture: all -Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-xdg, python-protobuf (>=2.4.1), python-protobuf.socketrpc, +Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-dirspec, python-protobuf (>=2.4.1), python-protobuf.socketrpc, python-dateutil, python-autopep8, python-openssl, python-gnupg Description: Common python files needed by LEAP projects. This package contains common python functions that are needed -- cgit v1.2.3 From 9c87f2caba1247a356413610f3fd000ec2c01d1e Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 24 Sep 2013 17:04:23 -0400 Subject: bump version to 0.3.3 --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 59855bc..d7732a0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +leap-common (0.3.3) unstable; urgency=low + + * Update to 0.3.3 release. + + -- Micah Anderson Tue, 24 Sep 2013 17:02:57 -0400 + leap-common (0.3.2) unstable; urgency=low * Update to 0.3.2 -- cgit v1.2.3 From 6d007720b3ac18e7babbea9550d0eeb2745d4c46 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 14 Oct 2013 03:43:33 -0300 Subject: bump changelog to 0.3.4; install LEAP's changelog too --- debian/changelog | 7 +++++++ debian/rules | 2 ++ 2 files changed, 9 insertions(+) diff --git a/debian/changelog b/debian/changelog index d7732a0..a69cb9f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +leap-common (0.3.4) unstable; urgency=low + + * Update to 0.3.4 release. + * Install LEAP changelog too. + + -- Micah Anderson Mon, 14 Oct 2013 03:40:42 -0300 + leap-common (0.3.3) unstable; urgency=low * Update to 0.3.3 release. diff --git a/debian/rules b/debian/rules index 7bf6077..734e561 100755 --- a/debian/rules +++ b/debian/rules @@ -3,3 +3,5 @@ %: dh $@ --with python2 --buildsystem=python_distutils +override_dh_installchangelogs: + dh_installchangelogs CHANGELOG -- cgit v1.2.3 From 34dcb3378c444f7ef195aa8073f01710fd4b7e2b Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 30 Oct 2013 10:55:15 -0200 Subject: add freeze_debianver command to setup.py --- setup.py | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 128861b..519ddb3 100644 --- a/setup.py +++ b/setup.py @@ -17,6 +17,7 @@ """ setup file for leap.common """ +import re from setuptools import setup, find_packages from pkg import utils @@ -46,18 +47,79 @@ trove_classifiers = [ "Topic :: Utilities" ] +DOWNLOAD_BASE = ('https://github.com/leapcode/leap_pycommon/' + 'archive/%s.tar.gz') +_versions = versioneer.get_versions() +VERSION = _versions['version'] +VERSION_FULL = _versions['full'] +DOWNLOAD_URL = "" + +# get the short version for the download url +_version_short = re.findall('\d+\.\d+\.\d+', VERSION) +if len(_version_short) > 0: + VERSION_SHORT = _version_short[0] + DOWNLOAD_URL = DOWNLOAD_BASE % VERSION_SHORT + +cmdclass = versioneer.get_cmdclass() +from setuptools import Command + + +class freeze_debianver(Command): + """ + Freezes the version in a debian branch. + To be used after merging the development branch onto the debian one. + """ + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + proceed = str(raw_input( + "This will overwrite the file _version.py. Continue? [y/N] ")) + if proceed != "y": + print("He. You scared. Aborting.") + return + template = r""" +# This file was generated by the `freeze_debianver` command in setup.py +# Using 'versioneer.py' (0.7+) from +# revision-control system data, or from the parent directory name of an +# unpacked source archive. Distribution tarballs contain a pre-generated copy +# of this file. + +version_version = '{version}' +version_full = '{version_full}' +""" + templatefun = r""" + +def get_versions(default={}, verbose=False): + return {'version': version_version, 'full': version_full} +""" + subst_template = template.format( + version=VERSION_SHORT, + version_full=VERSION_FULL) + templatefun + with open(versioneer.versionfile_source, 'w') as f: + f.write(subst_template) + + +cmdclass["freeze_debianver"] = freeze_debianver setup( name='leap.common', - version=versioneer.get_version(), - cmdclass=versioneer.get_cmdclass(), + version=VERSION, + cmdclass=cmdclass, url='https://leap.se/', + download_url=DOWNLOAD_URL, license='GPLv3+', author='The LEAP Encryption Access Project', author_email='info@leap.se', + maintainer='Kali Kaneko', + maintainer_email='kali@leap.se', description='Common files used by the LEAP project.', - long_description=( - "Common files used by the LEAP Client project." - ), + long_description=open('README.rst').read() + '\n\n\n' + + open('CHANGELOG').read(), classifiers=trove_classifiers, namespace_packages=["leap"], package_dir={'': 'src'}, -- cgit v1.2.3 From 74787c2cf709356804bc880ac34a4dd0868ef05d Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 30 Oct 2013 15:47:21 -0200 Subject: 0.3.4 version freeze --- src/leap/common/_version.py | 208 ++------------------------------------------ 1 file changed, 9 insertions(+), 199 deletions(-) diff --git a/src/leap/common/_version.py b/src/leap/common/_version.py index 597e2e4..d372bfe 100644 --- a/src/leap/common/_version.py +++ b/src/leap/common/_version.py @@ -1,203 +1,13 @@ -IN_LONG_VERSION_PY = True -# This file helps to compute a version number in source trees obtained from -# git-archive tarball (such as those provided by githubs download-from-tag -# feature). Distribution tarballs (build by setup.py sdist) and build -# directories (produced by setup.py build) will contain a much shorter file -# that just contains the computed version number. +# This file was generated by the `freeze_debianver` command in setup.py +# Using 'versioneer.py' (0.7+) from +# revision-control system data, or from the parent directory name of an +# unpacked source archive. Distribution tarballs contain a pre-generated copy +# of this file. -# This file is released into the public domain. Generated by -# versioneer-0.7+ (https://github.com/warner/python-versioneer) +version_version = '0.3.4' +version_full = '34dcb3378c444f7ef195aa8073f01710fd4b7e2b' -# these strings will be replaced by git during git-archive -git_refnames = "$Format:%d$" -git_full = "$Format:%H$" - - -import subprocess -import sys - -def run_command(args, cwd=None, verbose=False): - try: - # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd) - except EnvironmentError: - e = sys.exc_info()[1] - if verbose: - print("unable to run %s" % args[0]) - print(e) - return None - stdout = p.communicate()[0].strip() - if sys.version >= '3': - stdout = stdout.decode() - if p.returncode != 0: - if verbose: - print("unable to run %s (error)" % args[0]) - return None - return stdout - - -import sys -import re -import os.path - -def get_expanded_variables(versionfile_source): - # the code embedded in _version.py can just fetch the value of these - # variables. When used from setup.py, we don't want to import - # _version.py, so we do it with a regexp instead. This function is not - # used from _version.py. - variables = {} - try: - f = open(versionfile_source,"r") - for line in f.readlines(): - if line.strip().startswith("git_refnames ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - variables["refnames"] = mo.group(1) - if line.strip().startswith("git_full ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - variables["full"] = mo.group(1) - f.close() - except EnvironmentError: - pass - return variables - -def versions_from_expanded_variables(variables, tag_prefix, verbose=False): - refnames = variables["refnames"].strip() - if refnames.startswith("$Format"): - if verbose: - print("variables are unexpanded, not using") - return {} # unexpanded, so not in an unpacked git-archive tarball - refs = set([r.strip() for r in refnames.strip("()").split(",")]) - # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of - # just "foo-1.0". If we see a "tag: " prefix, prefer those. - TAG = "tag: " - tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) - if not tags: - # Either we're using git < 1.8.3, or there really are no tags. We use - # a heuristic: assume all version tags have a digit. The old git %d - # expansion behaves like git log --decorate=short and strips out the - # refs/heads/ and refs/tags/ prefixes that would let us distinguish - # between branches and tags. By ignoring refnames without digits, we - # filter out many common branch names like "release" and - # "stabilization", as well as "HEAD" and "master". - tags = set([r for r in refs if re.search(r'\d', r)]) - if verbose: - print("discarding '%s', no digits" % ",".join(refs-tags)) - if verbose: - print("likely tags: %s" % ",".join(sorted(tags))) - for ref in sorted(tags): - # sorting will prefer e.g. "2.0" over "2.0rc1" - if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] - if verbose: - print("picking %s" % r) - return { "version": r, - "full": variables["full"].strip() } - # no suitable tags, so we use the full revision id - if verbose: - print("no suitable tags, using full revision id") - return { "version": variables["full"].strip(), - "full": variables["full"].strip() } - -def versions_from_vcs(tag_prefix, versionfile_source, verbose=False): - # this runs 'git' from the root of the source tree. That either means - # someone ran a setup.py command (and this code is in versioneer.py, so - # IN_LONG_VERSION_PY=False, thus the containing directory is the root of - # the source tree), or someone ran a project-specific entry point (and - # this code is in _version.py, so IN_LONG_VERSION_PY=True, thus the - # containing directory is somewhere deeper in the source tree). This only - # gets called if the git-archive 'subst' variables were *not* expanded, - # and _version.py hasn't already been rewritten with a short version - # string, meaning we're inside a checked out source tree. - - try: - here = os.path.abspath(__file__) - except NameError: - # some py2exe/bbfreeze/non-CPython implementations don't do __file__ - return {} # not always correct - - # versionfile_source is the relative path from the top of the source tree - # (where the .git directory might live) to this file. Invert this to find - # the root from __file__. - root = here - if IN_LONG_VERSION_PY: - for i in range(len(versionfile_source.split("/"))): - root = os.path.dirname(root) - else: - root = os.path.dirname(here) - if not os.path.exists(os.path.join(root, ".git")): - if verbose: - print("no .git in %s" % root) - return {} - - GIT = "git" - if sys.platform == "win32": - GIT = "git.cmd" - stdout = run_command([GIT, "describe", "--tags", "--dirty", "--always"], - cwd=root) - if stdout is None: - return {} - if not stdout.startswith(tag_prefix): - if verbose: - print("tag '%s' doesn't start with prefix '%s'" % (stdout, tag_prefix)) - return {} - tag = stdout[len(tag_prefix):] - stdout = run_command([GIT, "rev-parse", "HEAD"], cwd=root) - if stdout is None: - return {} - full = stdout.strip() - if tag.endswith("-dirty"): - full += "-dirty" - return {"version": tag, "full": full} - - -def versions_from_parentdir(parentdir_prefix, versionfile_source, verbose=False): - if IN_LONG_VERSION_PY: - # We're running from _version.py. If it's from a source tree - # (execute-in-place), we can work upwards to find the root of the - # tree, and then check the parent directory for a version string. If - # it's in an installed application, there's no hope. - try: - here = os.path.abspath(__file__) - except NameError: - # py2exe/bbfreeze/non-CPython don't have __file__ - return {} # without __file__, we have no hope - # versionfile_source is the relative path from the top of the source - # tree to _version.py. Invert this to find the root from __file__. - root = here - for i in range(len(versionfile_source.split("/"))): - root = os.path.dirname(root) - else: - # we're running from versioneer.py, which means we're running from - # the setup.py in a source tree. sys.argv[0] is setup.py in the root. - here = os.path.abspath(sys.argv[0]) - root = os.path.dirname(here) - - # Source tarballs conventionally unpack into a directory that includes - # both the project name and a version string. - dirname = os.path.basename(root) - if not dirname.startswith(parentdir_prefix): - if verbose: - print("guessing rootdir is '%s', but '%s' doesn't start with prefix '%s'" % - (root, dirname, parentdir_prefix)) - return None - return {"version": dirname[len(parentdir_prefix):], "full": ""} - -tag_prefix = "" -parentdir_prefix = "leap.common-" -versionfile_source = "src/leap/common/_version.py" - -def get_versions(default={"version": "unknown", "full": ""}, verbose=False): - variables = { "refnames": git_refnames, "full": git_full } - ver = versions_from_expanded_variables(variables, tag_prefix, verbose) - if not ver: - ver = versions_from_vcs(tag_prefix, versionfile_source, verbose) - if not ver: - ver = versions_from_parentdir(parentdir_prefix, versionfile_source, - verbose) - if not ver: - ver = default - return ver +def get_versions(default={}, verbose=False): + return {'version': version_version, 'full': version_full} -- cgit v1.2.3 From 2cda4e40f022a6f49a338b49067e56276121c78e Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 24 Oct 2013 13:42:11 -0200 Subject: remove autopep8 dep --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 6802aab..845f3c1 100644 --- a/debian/control +++ b/debian/control @@ -8,7 +8,7 @@ Standards-Version: 3.9.4 Package: python-leap-common Architecture: all Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-dirspec, python-protobuf (>=2.4.1), python-protobuf.socketrpc, - python-dateutil, python-autopep8, python-openssl, python-gnupg + python-dateutil, python-openssl, python-gnupg Description: Common python files needed by LEAP projects. This package contains common python functions that are needed for the LEAP Client project -- cgit v1.2.3 From fac6becd50e2f8bb9efc354fa63c65f2c1445ecc Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 5 Nov 2013 11:07:52 -0200 Subject: bump changelog --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index a69cb9f..ee8eb58 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +leap-common (0.3.5) unstable; urgency=low + + * Update to 0.3.5 release. + + -- Micah Anderson Tue, 05 Nov 2013 11:05:40 -0200 + leap-common (0.3.4) unstable; urgency=low * Update to 0.3.4 release. -- cgit v1.2.3 From 10c0e1d3456c3f8ef3950d7e11b4a4548e771073 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 5 Nov 2013 11:09:07 -0200 Subject: freeze debian ver to 0.3.5 --- src/leap/common/_version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/leap/common/_version.py b/src/leap/common/_version.py index d372bfe..940759b 100644 --- a/src/leap/common/_version.py +++ b/src/leap/common/_version.py @@ -5,8 +5,8 @@ # unpacked source archive. Distribution tarballs contain a pre-generated copy # of this file. -version_version = '0.3.4' -version_full = '34dcb3378c444f7ef195aa8073f01710fd4b7e2b' +version_version = '0.3.5' +version_full = 'fac6becd50e2f8bb9efc354fa63c65f2c1445ecc' def get_versions(default={}, verbose=False): -- cgit v1.2.3 From 3ea9dd9c8658f9faafd1f1a9a8c8ce16e6579f4a Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 10 Dec 2013 15:43:47 -0400 Subject: add deb_release script --- deb_release.sh | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100755 deb_release.sh diff --git a/deb_release.sh b/deb_release.sh new file mode 100755 index 0000000..9f0e675 --- /dev/null +++ b/deb_release.sh @@ -0,0 +1,8 @@ +#!/bin/zsh + +VERSION_FILE="src/leap/common/_version.py" +rm ${VERSION_FILE} +python setup.py freeze_debianver +sed -i 's/-dirty//g' ${VERSION_FILE} +git add ${VERSION_FILE} +git ci -m "freeze debian version" -- cgit v1.2.3 From 4e77d18550758e54982b40e32b8f762558504312 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 10 Dec 2013 15:43:54 -0400 Subject: freeze debian version --- src/leap/common/_version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/leap/common/_version.py b/src/leap/common/_version.py index 940759b..acaa91d 100644 --- a/src/leap/common/_version.py +++ b/src/leap/common/_version.py @@ -5,8 +5,8 @@ # unpacked source archive. Distribution tarballs contain a pre-generated copy # of this file. -version_version = '0.3.5' -version_full = 'fac6becd50e2f8bb9efc354fa63c65f2c1445ecc' +version_version = '0.3.6' +version_full = '3ea9dd9c8658f9faafd1f1a9a8c8ce16e6579f4a' def get_versions(default={}, verbose=False): -- cgit v1.2.3 From b57e66cbaddafe88f7e8419208e3a5d7c6d7a7ab Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 10 Dec 2013 16:04:08 -0400 Subject: bump changelog to 0.3.6 --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index ee8eb58..20f06a1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +leap-common (0.3.6) unstable; urgency=low + + * Update to 0.3.6 release + + -- Micah Anderson Tue, 10 Dec 2013 16:02:51 -0400 + leap-common (0.3.5) unstable; urgency=low * Update to 0.3.5 release. -- cgit v1.2.3 From 7afebec82d45765dde145e0e96dc50b4a34c78ec Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 23 Dec 2013 02:32:41 -0400 Subject: update changelog for 0.3.7 --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 20f06a1..5d96510 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +leap-common (0.3.7~rc) unstable; urgency=low + + * Update to 0.3.7 release + + -- Ben Carrillo Mon, 23 Dec 2013 02:31:53 -0400 + leap-common (0.3.6) unstable; urgency=low * Update to 0.3.6 release -- cgit v1.2.3 From bfdad9b9888f62a5849cab3830ebfc040228d270 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 23 Dec 2013 02:32:51 -0400 Subject: freeze debian version to 0.3.7-rc --- src/leap/common/_version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/leap/common/_version.py b/src/leap/common/_version.py index acaa91d..a68d311 100644 --- a/src/leap/common/_version.py +++ b/src/leap/common/_version.py @@ -5,8 +5,8 @@ # unpacked source archive. Distribution tarballs contain a pre-generated copy # of this file. -version_version = '0.3.6' -version_full = '3ea9dd9c8658f9faafd1f1a9a8c8ce16e6579f4a' +version_version = '0.3.6-rc1' +version_full = '7afebec82d45765dde145e0e96dc50b4a34c78ec' def get_versions(default={}, verbose=False): -- cgit v1.2.3 From f15cf332f1fa78f467253666485a0953590289a7 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 23 Dec 2013 11:32:33 -0400 Subject: Fix the reported string under _version (0.3.7-rc) --- debian/changelog | 2 +- src/leap/common/_version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 5d96510..99d8a17 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,6 @@ leap-common (0.3.7~rc) unstable; urgency=low - * Update to 0.3.7 release + * Update to 0.3.7 release candidate -- Ben Carrillo Mon, 23 Dec 2013 02:31:53 -0400 diff --git a/src/leap/common/_version.py b/src/leap/common/_version.py index a68d311..6511e15 100644 --- a/src/leap/common/_version.py +++ b/src/leap/common/_version.py @@ -5,7 +5,7 @@ # unpacked source archive. Distribution tarballs contain a pre-generated copy # of this file. -version_version = '0.3.6-rc1' +version_version = '0.3.7-rc1' version_full = '7afebec82d45765dde145e0e96dc50b4a34c78ec' -- cgit v1.2.3 From 6027c797e5338acb95f7cbf19b0bc1d1c26157cb Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 9 Apr 2014 14:26:49 -0500 Subject: update changelog to 0.3.7 final release --- debian/changelog | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index 99d8a17..7c4e123 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,8 @@ -leap-common (0.3.7~rc) unstable; urgency=low +leap-common (0.3.7) unstable; urgency=low - * Update to 0.3.7 release candidate + * Update to 0.3.7 release - -- Ben Carrillo Mon, 23 Dec 2013 02:31:53 -0400 + -- Ben Carrillo Wed, 09 Apr 2014 14:26:10 -0500 leap-common (0.3.6) unstable; urgency=low -- cgit v1.2.3 From 14500565c0bc2461f45f216373cb66cdb750ac2b Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 9 Apr 2014 14:28:55 -0500 Subject: freeze debian ver to 0.3.7 --- src/leap/common/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/leap/common/_version.py b/src/leap/common/_version.py index 6511e15..81a674f 100644 --- a/src/leap/common/_version.py +++ b/src/leap/common/_version.py @@ -5,7 +5,7 @@ # unpacked source archive. Distribution tarballs contain a pre-generated copy # of this file. -version_version = '0.3.7-rc1' +version_version = '0.3.7' version_full = '7afebec82d45765dde145e0e96dc50b4a34c78ec' -- cgit v1.2.3 From 1ff5b12759f29ea1901c66cd388dad991e32313d Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 9 Apr 2014 14:35:11 -0500 Subject: bump standards-version --- debian/control | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 845f3c1..ce369f3 100644 --- a/debian/control +++ b/debian/control @@ -1,9 +1,10 @@ Source: leap-common Maintainer: Micah Anderson +Uploaders: Ben Carrillo Section: python Priority: optional Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 9), python-protobuf, python-protobuf.socketrpc -Standards-Version: 3.9.4 +Standards-Version: 3.9.5 Package: python-leap-common Architecture: all -- cgit v1.2.3 From 0a87e2bd3c79409ef6b30cef085d90a8ca4f9441 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 10 Jun 2014 09:24:41 -0500 Subject: bump version --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 7c4e123..6b55385 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +leap-common (0.3.8) unstable; urgency=medium + + * Update to 0.3.8 release + + -- Ben Carrillo Tue, 10 Jun 2014 09:24:12 -0500 + leap-common (0.3.7) unstable; urgency=low * Update to 0.3.7 release -- cgit v1.2.3 From a33bff3701c553c7ddaa1521ae1a077e1d3dadca Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 10 Jun 2014 09:24:52 -0500 Subject: freeze debian version --- src/leap/common/_version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/leap/common/_version.py b/src/leap/common/_version.py index 81a674f..8b8926f 100644 --- a/src/leap/common/_version.py +++ b/src/leap/common/_version.py @@ -5,8 +5,8 @@ # unpacked source archive. Distribution tarballs contain a pre-generated copy # of this file. -version_version = '0.3.7' -version_full = '7afebec82d45765dde145e0e96dc50b4a34c78ec' +version_version = '0.3.8' +version_full = '0a87e2bd3c79409ef6b30cef085d90a8ca4f9441' def get_versions(default={}, verbose=False): -- cgit v1.2.3 From 42e1da0732f16592ae13de1b83061e143aec0ad9 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Fri, 7 Nov 2014 18:49:45 +0100 Subject: freeze debian version --- src/leap/common/_version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/leap/common/_version.py b/src/leap/common/_version.py index 8b8926f..e926143 100644 --- a/src/leap/common/_version.py +++ b/src/leap/common/_version.py @@ -5,8 +5,8 @@ # unpacked source archive. Distribution tarballs contain a pre-generated copy # of this file. -version_version = '0.3.8' -version_full = '0a87e2bd3c79409ef6b30cef085d90a8ca4f9441' +version_version = '0.3.9' +version_full = '3de1eeba83d50793283d65ba4566dd1611ee9d4b' def get_versions(default={}, verbose=False): -- cgit v1.2.3 From d272a953a01f5c601e4894a916f7b4d990a03327 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Fri, 7 Nov 2014 18:51:08 +0100 Subject: update changelog --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 6b55385..31e31e2 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +leap-common (0.3.9) unstable; urgency=medium + + * Update to 0.3.9 release + + -- Ben Carrillo Fri, 07 Nov 2014 18:50:11 +0100 + leap-common (0.3.8) unstable; urgency=medium * Update to 0.3.8 release -- cgit v1.2.3 From 447c414f86dd5b8a3b0bd62adaf9d3a1cf03e1ec Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Tue, 13 Jan 2015 17:00:59 -0500 Subject: update to new standards version (no changes) --- debian/changelog | 6 ++++++ debian/control | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 31e31e2..78ef49e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +leap-common (0.3.9.1) unstable; urgency=medium + + * Update standards version to 3.9.6 (no changes) + + -- Micah Anderson Tue, 13 Jan 2015 17:00:11 -0500 + leap-common (0.3.9) unstable; urgency=medium * Update to 0.3.9 release diff --git a/debian/control b/debian/control index ce369f3..410df98 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,7 @@ Uploaders: Ben Carrillo Section: python Priority: optional Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 9), python-protobuf, python-protobuf.socketrpc -Standards-Version: 3.9.5 +Standards-Version: 3.9.6 Package: python-leap-common Architecture: all -- cgit v1.2.3 From 3bb0b76770a124a13226556b60bcd84d3cc99a0b Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Tue, 13 Jan 2015 17:02:23 -0500 Subject: change my email address --- debian/changelog | 3 ++- debian/control | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 78ef49e..0fa8852 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,9 @@ leap-common (0.3.9.1) unstable; urgency=medium * Update standards version to 3.9.6 (no changes) + * Change my email address - -- Micah Anderson Tue, 13 Jan 2015 17:00:11 -0500 + -- Micah Anderson Tue, 13 Jan 2015 17:00:11 -0500 leap-common (0.3.9) unstable; urgency=medium diff --git a/debian/control b/debian/control index 410df98..253b467 100644 --- a/debian/control +++ b/debian/control @@ -1,5 +1,5 @@ Source: leap-common -Maintainer: Micah Anderson +Maintainer: Micah Anderson Uploaders: Ben Carrillo Section: python Priority: optional -- cgit v1.2.3 From c071c69e1b5a0d897674a1f7adc6ff32f19400ff Mon Sep 17 00:00:00 2001 From: Victor Shyba Date: Wed, 27 May 2015 12:49:44 -0300 Subject: [bug] Use BrowserLikePolicyForHTTPS for checking While testing the way that its implemented now, I found out that no check is being made on certificate attributes against the host. I found this simple way of creating a BrowserLikePolicyForHTTPS using a self signed cert and it worked on my test. I used test_https from Soledad for checking this (which we are fixing on another branch). Also, we don't want to depend on twisted for other things than leap.common.http. --- pkg/requirements.pip | 2 -- setup.py | 7 +++++ src/leap/common/certs.py | 17 ++++++++++++ src/leap/common/http.py | 70 +++++++++++++----------------------------------- 4 files changed, 42 insertions(+), 54 deletions(-) diff --git a/pkg/requirements.pip b/pkg/requirements.pip index f875344..1977cc8 100644 --- a/pkg/requirements.pip +++ b/pkg/requirements.pip @@ -2,8 +2,6 @@ jsonschema #<=0.8 -- are we done with this conflict? dirspec pyopenssl python-dateutil -Twisted>=12.1 -zope.interface pyzmq txzmq diff --git a/setup.py b/setup.py index a7de8f9..776a477 100644 --- a/setup.py +++ b/setup.py @@ -138,4 +138,11 @@ setup( tests_require=tests_requirements, include_package_data=True, zip_safe=False, + + extras_require={ + # needed for leap.common.http + # service_identity needed for propper hostname identification, + # see http://twistedmatrix.com/documents/current/core/howto/ssl.html + 'Twisted': ["Twisted>=14.0.2", "service_identity", "zope.insterface"] + }, ) diff --git a/src/leap/common/certs.py b/src/leap/common/certs.py index db513f6..c8e0743 100644 --- a/src/leap/common/certs.py +++ b/src/leap/common/certs.py @@ -178,3 +178,20 @@ def should_redownload(certfile, now=time.gmtime): return True return False + + +def get_compatible_ssl_context_factory(cert_path=None): + import twisted + cert = None + if twisted.version.base() > '14.0.1': + from twisted.web.client import BrowserLikePolicyForHTTPS + from twisted.internet import ssl + if cert_path: + cert = ssl.Certificate.loadPEM(open(cert_path).read()) + policy = BrowserLikePolicyForHTTPS(cert) + return policy + else: + raise Exception((""" + Twisted 14.0.2 is needed in order to have secure Client Web SSL Contexts, not %s + See: http://twistedmatrix.com/trac/ticket/7647 + """) % (twisted.version.base())) diff --git a/src/leap/common/http.py b/src/leap/common/http.py index 39f01ba..1dc5642 100644 --- a/src/leap/common/http.py +++ b/src/leap/common/http.py @@ -18,22 +18,28 @@ Twisted HTTP/HTTPS client. """ -import os +try: + import twisted +except ImportError: + print "*******" + print "Twisted is needed to use leap.common.http module" + print "" + print "Install the extra requirement of the package:" + print "$ pip install leap.common[Twisted]" + import sys + sys.exit(1) -from zope.interface import implements -from OpenSSL.crypto import load_certificate -from OpenSSL.crypto import FILETYPE_PEM +from leap.common.certs import get_compatible_ssl_context_factory + +from zope.interface import implements from twisted.internet import reactor -from twisted.internet.ssl import ClientContextFactory -from twisted.internet.ssl import CertificateOptions from twisted.internet.defer import succeed from twisted.web.client import Agent from twisted.web.client import HTTPConnectionPool from twisted.web.client import readBody -from twisted.web.client import BrowserLikePolicyForHTTPS from twisted.web.http_headers import Headers from twisted.web.iweb import IBodyProducer @@ -55,33 +61,12 @@ class HTTPClient(object): self._pool = HTTPConnectionPool(reactor, persistent=True) self._pool.maxPersistentPerHost = 10 - if cert_file: - cert = self._load_cert(cert_file) - self._agent = Agent( - reactor, - HTTPClient.ClientContextFactory(cert), - pool=self._pool) - else: - # trust the system's CAs - self._agent = Agent( - reactor, - BrowserLikePolicyForHTTPS(), - pool=self._pool) - - def _load_cert(self, cert_file): - """ - Load a X509 certificate from a file. - - :param cert_file: The path to the certificate file. - :type cert_file: str + policy = get_compatible_ssl_context_factory(cert_file) - :return: The X509 certificate. - :rtype: OpenSSL.crypto.X509 - """ - if os.path.exists(cert_file): - with open(cert_file) as f: - data = f.read() - return load_certificate(FILETYPE_PEM, data) + self._agent = Agent( + reactor, + policy, + pool=self._pool) def request(self, url, method='GET', body=None, headers={}): """ @@ -106,25 +91,6 @@ class HTTPClient(object): d.addCallback(readBody) return d - class ClientContextFactory(ClientContextFactory): - """ - A context factory that will verify the server's certificate against a - given CA certificate. - """ - - def __init__(self, cacert): - """ - Initialize the context factory. - - :param cacert: The CA certificate. - :type cacert: OpenSSL.crypto.X509 - """ - self._cacert = cacert - - def getContext(self, hostname, port): - opts = CertificateOptions(verify=True, caCerts=[self._cacert]) - return opts.getContext() - class StringBodyProducer(object): """ A producer that writes the body of a request to a consumer. -- cgit v1.2.3 From bf18c2bc6e3f533187281a3b31febd37ef22f8c0 Mon Sep 17 00:00:00 2001 From: Victor Shyba Date: Wed, 27 May 2015 17:19:13 -0300 Subject: [feat] Make it optional to have a dedicated pool As @meskio pointed out, some cases could need a dedicated pool with different parameters. This is a suggested implementation where the pool is reused by default, creating a dedicated one just if needed/asked. This way we ensure that resources are under control and special cases are still handled. --- changes/bug-browserlikepolicy-https | 1 + src/leap/common/http.py | 14 ++++++--- src/leap/common/tests/test_http.py | 62 +++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 changes/bug-browserlikepolicy-https create mode 100644 src/leap/common/tests/test_http.py diff --git a/changes/bug-browserlikepolicy-https b/changes/bug-browserlikepolicy-https new file mode 100644 index 0000000..bceb129 --- /dev/null +++ b/changes/bug-browserlikepolicy-https @@ -0,0 +1 @@ +- Makes https client use Twisted SSL validation and adds a reuse by default behavior on connection pool diff --git a/src/leap/common/http.py b/src/leap/common/http.py index 1dc5642..1e384e5 100644 --- a/src/leap/common/http.py +++ b/src/leap/common/http.py @@ -44,13 +44,21 @@ from twisted.web.http_headers import Headers from twisted.web.iweb import IBodyProducer +def createPool(maxPersistentPerHost=10, persistent=True): + pool = HTTPConnectionPool(reactor, persistent) + pool.maxPersistentPerHost = maxPersistentPerHost + return pool + +_pool = createPool() + + class HTTPClient(object): """ HTTP client done the twisted way, with a main focus on pinning the SSL certificate. """ - def __init__(self, cert_file=None): + def __init__(self, cert_file=None, pool=_pool): """ Init the HTTP client @@ -58,15 +66,13 @@ class HTTPClient(object): system's CAs will be used. :type cert_file: str """ - self._pool = HTTPConnectionPool(reactor, persistent=True) - self._pool.maxPersistentPerHost = 10 policy = get_compatible_ssl_context_factory(cert_file) self._agent = Agent( reactor, policy, - pool=self._pool) + pool=pool) def request(self, url, method='GET', body=None, headers={}): """ diff --git a/src/leap/common/tests/test_http.py b/src/leap/common/tests/test_http.py new file mode 100644 index 0000000..e240ca3 --- /dev/null +++ b/src/leap/common/tests/test_http.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# test_http.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Tests for: + * leap/common/http.py +""" +import os +try: + import unittest2 as unittest +except ImportError: + import unittest + +from leap.common import http +from leap.common.testing.basetest import BaseLeapTest + +TEST_CERT_PEM = os.path.join( + os.path.split(__file__)[0], + '..', 'testing', "leaptest_combined_keycert.pem") + + +class HTTPClientTest(BaseLeapTest): + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_agents_sharing_pool_by_default(self): + client = http.HTTPClient() + client2 = http.HTTPClient(TEST_CERT_PEM) + self.assertNotEquals(client._agent, client2._agent, "Expected dedicated agents") + self.assertEquals(client._agent._pool, client2._agent._pool, "Pool was not reused by default") + + def test_agent_can_have_dedicated_custom_pool(self): + custom_pool = http.createPool(maxPersistentPerHost=42, persistent=False) + self.assertEquals(custom_pool.maxPersistentPerHost, 42, + "Custom persistent connections limit is not being respected") + self.assertFalse(custom_pool.persistent, + "Custom persistence is not being respected") + default_client = http.HTTPClient() + custom_client = http.HTTPClient(pool=custom_pool) + + self.assertNotEquals(default_client._agent, custom_client._agent, "No agent reuse is expected") + self.assertEquals(custom_pool, custom_client._agent._pool, "Custom pool usage was not respected") + +if __name__ == "__main__": + unittest.main() -- cgit v1.2.3 From e1598b233e6d3e3385edc64d5d4204967c4434fb Mon Sep 17 00:00:00 2001 From: drebs Date: Tue, 2 Jun 2015 15:05:19 -0300 Subject: [pkg] add pyzmq and txzmq dep versions --- pkg/requirements.pip | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/requirements.pip b/pkg/requirements.pip index 1977cc8..02fb189 100644 --- a/pkg/requirements.pip +++ b/pkg/requirements.pip @@ -2,7 +2,7 @@ jsonschema #<=0.8 -- are we done with this conflict? dirspec pyopenssl python-dateutil -pyzmq -txzmq +pyzmq>=14.4.1 +txzmq>=0.7.3 #autopep8 -- ??? -- cgit v1.2.3 From 76436726663971ebd58bf2c758b52abb10f7c242 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 2 Jun 2015 17:27:42 -0400 Subject: [bug] allow ipc socket types previous regex wasn't capturing addresses of type ipc:// Closes: #7089 --- changes/bug-7089-allow_ipc_proto | 1 + src/leap/common/events/tests/__init__.py | 0 .../common/events/tests/test_zmq_components.py | 51 ++++++++++++++++++++++ src/leap/common/events/zmq_components.py | 24 +++++----- 4 files changed, 66 insertions(+), 10 deletions(-) create mode 100644 changes/bug-7089-allow_ipc_proto create mode 100644 src/leap/common/events/tests/__init__.py create mode 100644 src/leap/common/events/tests/test_zmq_components.py diff --git a/changes/bug-7089-allow_ipc_proto b/changes/bug-7089-allow_ipc_proto new file mode 100644 index 0000000..7dce314 --- /dev/null +++ b/changes/bug-7089-allow_ipc_proto @@ -0,0 +1 @@ +o [bug] fix regexp to allow ipc protocol in zmq sockets. Closes: #7089 diff --git a/src/leap/common/events/tests/__init__.py b/src/leap/common/events/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/leap/common/events/tests/test_zmq_components.py b/src/leap/common/events/tests/test_zmq_components.py new file mode 100644 index 0000000..c51e37e --- /dev/null +++ b/src/leap/common/events/tests/test_zmq_components.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# test_zmq_components.py +# Copyright (C) 2014 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Tests for the zmq_components module. +""" +try: + import unittest2 as unittest +except ImportError: + import unittest + +from leap.common.events import zmq_components + + +class AddrParseTestCase(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_addr_parsing(self): + addr_re = zmq_components.ADDRESS_RE + + self.assertEqual( + addr_re.search("ipc:///tmp/foo/bar/baaz-2/foo.0").groups(), + ("ipc", "/tmp/foo/bar/baaz-2/foo.0", None)) + self.assertEqual( + addr_re.search("tcp://localhost:9000").groups(), + ("tcp", "localhost", "9000")) + self.assertEqual( + addr_re.search("tcp://127.0.0.1:9000").groups(), + ("tcp", "127.0.0.1", "9000")) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/leap/common/events/zmq_components.py b/src/leap/common/events/zmq_components.py index 4fb95d3..3b88862 100644 --- a/src/leap/common/events/zmq_components.py +++ b/src/leap/common/events/zmq_components.py @@ -45,7 +45,7 @@ from leap.common.zmq_utils import PUBLIC_KEYS_PREFIX logger = logging.getLogger(__name__) -ADDRESS_RE = re.compile("(.+)://(.+):([0-9]+)") +ADDRESS_RE = re.compile("^([a-z]+)://([^:]+):?(\d+)?$") class TxZmqComponent(object): @@ -63,7 +63,7 @@ class TxZmqComponent(object): """ self._factory = txzmq.ZmqFactory() self._factory.registerForShutdown() - if path_prefix == None: + if path_prefix is None: path_prefix = get_path_prefix() self._config_prefix = os.path.join(path_prefix, "leap", "events") self._connections = [] @@ -125,15 +125,19 @@ class TxZmqComponent(object): socket.curve_publickey = public socket.curve_secretkey = secret self._start_thread_auth(connection.socket) - # check if port was given - protocol, addr, port = ADDRESS_RE.match(address).groups() - if port == "0": - port = socket.bind_to_random_port("%s://%s" % (protocol, addr)) + + proto, addr, port = ADDRESS_RE.search(address).groups() + + if port is None: + params = proto, addr + port = socket.bind("%s://%s" % params) + # XXX this log doesn't appear + logger.debug("Binded %s to %s://%s." % ((connClass,) + params)) else: - socket.bind(address) - port = int(port) - logger.debug("Binded %s to %s://%s:%d." - % (connClass, protocol, addr, port)) + params = proto, addr, int(port) + socket.bind("%s://%s:%d" % params) + # XXX this log doesn't appear + logger.debug("Binded %s to %s://%s:%d." % ((connClass,) + params)) self._connections.append(connection) return connection, port -- cgit v1.2.3 From b67648aa666345b0800b48f6c203538b21c9a201 Mon Sep 17 00:00:00 2001 From: Victor Shyba Date: Mon, 8 Jun 2015 15:18:00 -0300 Subject: [bug] Makes request method respect a hard limit Altough we specify maxPersistentPerHost, Twisted won't stop opening connections after that. This limit is used just to keep the size of persistent connections pool under control. Additional connections will be made as non persistent. So, if we ask 10000 requests, it will open 10000 connections immediately and leave 10 open after all finished. For checking this behavior, see getConnection from Twisted source: http://twistedmatrix.com/trac/browser/tags/releases/twisted-15.2.1/twisted/web/client.py#L1203 I tested this by using http_target from soledad without a local database to download all encrypted docs from one account with 1700 of them. The program just hangs and crashes with 1000+ connections and "Too many files open" warnings. With this fix, it was able to download normally, respecting the maxPersistentPerHost as a limiter. :) --- src/leap/common/http.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/leap/common/http.py b/src/leap/common/http.py index 1e384e5..d4a214c 100644 --- a/src/leap/common/http.py +++ b/src/leap/common/http.py @@ -35,6 +35,7 @@ from leap.common.certs import get_compatible_ssl_context_factory from zope.interface import implements from twisted.internet import reactor +from twisted.internet import defer from twisted.internet.defer import succeed from twisted.web.client import Agent @@ -56,6 +57,13 @@ class HTTPClient(object): """ HTTP client done the twisted way, with a main focus on pinning the SSL certificate. + + By default, it uses a shared connection pool. If you want a dedicated + one, create and pass on __init__ pool parameter. + Please note that this client will limit the maximum amount of connections + by using a DeferredSemaphore. + This limit is equal to the maxPersistentPerHost used on pool and is needed + in order to avoid resource abuse on huge requests batches. """ def __init__(self, cert_file=None, pool=_pool): @@ -65,6 +73,9 @@ class HTTPClient(object): :param cert_file: The path to the certificate file, if None given the system's CAs will be used. :type cert_file: str + :param pool: An optional dedicated connection pool to override the + default shared one. + :type pool: HTTPConnectionPool """ policy = get_compatible_ssl_context_factory(cert_file) @@ -73,6 +84,7 @@ class HTTPClient(object): reactor, policy, pool=pool) + self._semaphore = defer.DeferredSemaphore(pool.maxPersistentPerHost) def request(self, url, method='GET', body=None, headers={}): """ @@ -92,8 +104,9 @@ class HTTPClient(object): """ if body: body = HTTPClient.StringBodyProducer(body) - d = self._agent.request( - method, url, headers=Headers(headers), bodyProducer=body) + d = self._semaphore.run(self._agent.request, + method, url, headers=Headers(headers), + bodyProducer=body) d.addCallback(readBody) return d -- cgit v1.2.3 From b33f0ef3485311f87990409ef8ba2bcb8b26dc5d Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 11 Jun 2015 11:54:10 -0300 Subject: [bug] remove extraneous data from events logs The emission of an event was being logged twice, and the second time was logging the pickled content of the event. This pickled content contained line breaks and other things that caused strange output on the client log. This commit removes the second loggin of the event pickled content. Closes #7130. --- changes/bug_7130_remove-extraneous-data-from-events-logs | 1 + src/leap/common/events/client.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 changes/bug_7130_remove-extraneous-data-from-events-logs diff --git a/changes/bug_7130_remove-extraneous-data-from-events-logs b/changes/bug_7130_remove-extraneous-data-from-events-logs new file mode 100644 index 0000000..6a12ee5 --- /dev/null +++ b/changes/bug_7130_remove-extraneous-data-from-events-logs @@ -0,0 +1 @@ + o Remove extraneous data from events logs. Closes #7130. diff --git a/src/leap/common/events/client.py b/src/leap/common/events/client.py index 0706fe3..4852b5a 100644 --- a/src/leap/common/events/client.py +++ b/src/leap/common/events/client.py @@ -173,7 +173,7 @@ class EventsClient(object): :param content: The content of the event. :type content: list """ - logger.debug("Sending event: (%s, %s)" % (event, content)) + logger.debug("Emitting event: (%s, %s)" % (event, content)) self._send(str(event) + b'\0' + pickle.dumps(content)) def _handle_event(self, event, content): @@ -368,7 +368,6 @@ class EventsClientThread(threading.Thread, EventsClient): :param data: The data to be sent. :type event: str """ - logger.debug("Sending data: %s" % data) # add send() as a callback for ioloop so it works between threads self._loop.add_callback(lambda: self._push.send(data)) -- cgit v1.2.3 From e91c6b2daf15d849de5a5fd72436f9f88505250e Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Fri, 5 Jun 2015 13:11:56 -0400 Subject: [feature] utility collect_plugins to be used in post-sync hooks. Related: #6996 Releases: 0.4.1 --- src/leap/common/plugins.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/leap/common/plugins.py diff --git a/src/leap/common/plugins.py b/src/leap/common/plugins.py new file mode 100644 index 0000000..bf0cd48 --- /dev/null +++ b/src/leap/common/plugins.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# plugins.py +# Copyright (C) 2015 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Twisted plugins leap utilities. +""" +import os.path + +from twisted.plugin import getPlugins + +from leap.common.config import get_path_prefix + +# A whitelist of modules from where to collect plugins dynamically. +# For the moment restricted to leap namespace, but the idea is that we can pass +# other "trusted" modules as options to the initialization of soledad. + +# TODO discover all the namespace automagically + +PLUGGABLE_LEAP_MODULES = ('mail', 'keymanager') + +_preffix = get_path_prefix() +rc_file = os.path.join(_preffix, "leap", "leap.cfg") + + +def _get_extra_pluggable_modules(): + import ConfigParser + config = ConfigParser.RawConfigParser() + config.read(rc_file) + try: + modules = eval( + config.get('plugins', 'extra_pluggable_modules'), {}, {}) + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): + modules = [] + return modules + +if os.path.isfile(rc_file): + # TODO in the case of being called from the standalone client, + # we should pass the flag in some other way. + EXTRA_PLUGGABLE_MODULES = _get_extra_pluggable_modules() +else: + EXTRA_PLUGGABLE_MODULES = [] + + +def collect_plugins(interface): + """ + Traverse a whitelist of modules and collect all the plugins that implement + the passed interface. + """ + plugins = [] + for namespace in PLUGGABLE_LEAP_MODULES: + try: + module = __import__('leap.%s.plugins' % namespace, fromlist='.') + plugins = plugins + list(getPlugins(interface, module)) + except ImportError: + pass + for namespace in EXTRA_PLUGGABLE_MODULES: + try: + module = __import__('%s.plugins' % namespace, fromlist='.') + plugins = plugins + list(getPlugins(interface, module)) + except ImportError: + pass + return plugins -- cgit v1.2.3 From c9f07ff0041fac8b5124a15a8053bb9cc9f03bb2 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 10 Jun 2015 14:52:11 -0400 Subject: [bug] catch missing section header error --- src/leap/common/plugins.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/leap/common/plugins.py b/src/leap/common/plugins.py index bf0cd48..04152f9 100644 --- a/src/leap/common/plugins.py +++ b/src/leap/common/plugins.py @@ -42,7 +42,8 @@ def _get_extra_pluggable_modules(): try: modules = eval( config.get('plugins', 'extra_pluggable_modules'), {}, {}) - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError, + ConfigParser.MissingSectionHeaderError): modules = [] return modules -- cgit v1.2.3 From c6107b88ba1eaf7e7ca97d0444e7444634aa98c2 Mon Sep 17 00:00:00 2001 From: drebs Date: Fri, 26 Jun 2015 14:44:37 -0300 Subject: [bug] allow passing ':0' as port in events address --- src/leap/common/events/zmq_components.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/leap/common/events/zmq_components.py b/src/leap/common/events/zmq_components.py index 3b88862..04f71e0 100644 --- a/src/leap/common/events/zmq_components.py +++ b/src/leap/common/events/zmq_components.py @@ -128,9 +128,9 @@ class TxZmqComponent(object): proto, addr, port = ADDRESS_RE.search(address).groups() - if port is None: + if port is None or port is '0': params = proto, addr - port = socket.bind("%s://%s" % params) + port = socket.bind_to_random_port("%s://%s" % params) # XXX this log doesn't appear logger.debug("Binded %s to %s://%s." % ((connClass,) + params)) else: -- cgit v1.2.3 From 10d7ebdf5226b74c54850d205a807cc650843efe Mon Sep 17 00:00:00 2001 From: drebs Date: Fri, 26 Jun 2015 14:45:19 -0300 Subject: [bug] run callback from thread in events client --- src/leap/common/events/client.py | 36 +++++++++++++++++++++++++++++++++--- src/leap/common/events/txclient.py | 13 +++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/leap/common/events/client.py b/src/leap/common/events/client.py index 4852b5a..1a026ea 100644 --- a/src/leap/common/events/client.py +++ b/src/leap/common/events/client.py @@ -180,14 +180,30 @@ class EventsClient(object): """ Handle an incoming event. - :param msg: The incoming message. - :type msg: list(str) + :param event: The event to be sent. + :type event: Event + :param content: The content of the event. + :type content: list """ logger.debug("Handling event %s..." % event) for uid in self._callbacks[event].keys(): callback = self._callbacks[event][uid] logger.debug("Executing callback %s." % uid) - callback(event, *content) + self._run_callback(callback, event, content) + + @abstractmethod + def _run_callback(self, callback, event, content): + """ + Run a callback. + + :param callback: The callback to be run. + :type callback: callable(event, *content) + :param event: The event to be sent. + :type event: Event + :param content: The content of the event. + :type content: list + """ + pass @abstractmethod def _subscribe(self, tag): @@ -371,6 +387,20 @@ class EventsClientThread(threading.Thread, EventsClient): # add send() as a callback for ioloop so it works between threads self._loop.add_callback(lambda: self._push.send(data)) + def _run_callback(self, callback, event, content): + """ + Run a callback. + + :param callback: The callback to be run. + :type callback: callable(event, *content) + :param event: The event to be sent. + :type event: Event + :param content: The content of the event. + :type content: list + """ + from twisted.internet import reactor + reactor.callFromThread(callback, event, *content) + def register(self, event, callback, uid=None, replace=False): """ Register a callback to be executed when an event is received. diff --git a/src/leap/common/events/txclient.py b/src/leap/common/events/txclient.py index 8206ed5..0dcfc08 100644 --- a/src/leap/common/events/txclient.py +++ b/src/leap/common/events/txclient.py @@ -112,6 +112,19 @@ class EventsTxClient(TxZmqClientComponent, EventsClient): """ self._push.send(data) + def _run_callback(self, callback, event, content): + """ + Run a callback. + + :param callback: The callback to be run. + :type callback: callable(event, *content) + :param event: The event to be sent. + :type event: Event + :param content: The content of the event. + :type content: list + """ + callback(event, *content) + def shutdown(self): TxZmqClientComponent.shutdown(self) EventsClient.shutdown(self) -- cgit v1.2.3 From 2cf0d452ca967114bb98d1d947c5ef5f7d19ccd1 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Fri, 10 Jul 2015 14:17:02 -0300 Subject: [pkg] fold in changes --- CHANGELOG | 6 ++++++ changes/bug-7089-allow_ipc_proto | 1 - changes/bug-browserlikepolicy-https | 1 - changes/bug_7130_remove-extraneous-data-from-events-logs | 1 - 4 files changed, 6 insertions(+), 3 deletions(-) delete mode 100644 changes/bug-7089-allow_ipc_proto delete mode 100644 changes/bug-browserlikepolicy-https delete mode 100644 changes/bug_7130_remove-extraneous-data-from-events-logs diff --git a/CHANGELOG b/CHANGELOG index 02e4297..1c6a871 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +0.4.1 Jul 10, 2015: + o Fix regexp to allow ipc protocol in zmq sockets. Closes: #7089. + o Remove extraneous data from events logs. Closes #7130. + o Make https client use Twisted SSL validation and adds a reuse by default + behavior on connection pool + 0.4.0 Jun 1, 2015: o Modify leap.common.events to use ZMQ. Closes #6359. o Fix time comparison between local and UTC times that caused the VPN diff --git a/changes/bug-7089-allow_ipc_proto b/changes/bug-7089-allow_ipc_proto deleted file mode 100644 index 7dce314..0000000 --- a/changes/bug-7089-allow_ipc_proto +++ /dev/null @@ -1 +0,0 @@ -o [bug] fix regexp to allow ipc protocol in zmq sockets. Closes: #7089 diff --git a/changes/bug-browserlikepolicy-https b/changes/bug-browserlikepolicy-https deleted file mode 100644 index bceb129..0000000 --- a/changes/bug-browserlikepolicy-https +++ /dev/null @@ -1 +0,0 @@ -- Makes https client use Twisted SSL validation and adds a reuse by default behavior on connection pool diff --git a/changes/bug_7130_remove-extraneous-data-from-events-logs b/changes/bug_7130_remove-extraneous-data-from-events-logs deleted file mode 100644 index 6a12ee5..0000000 --- a/changes/bug_7130_remove-extraneous-data-from-events-logs +++ /dev/null @@ -1 +0,0 @@ - o Remove extraneous data from events logs. Closes #7130. -- cgit v1.2.3 From f508d3629686d1ad4574e5a7ec67f8b3283f7c5f Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 20 Jul 2015 15:18:57 -0400 Subject: [refactor] remove use of reactor in threaded version of events client the idea is that we'll be able to use the threaded version of the client, which makes use of the tornado ioloop, in a non-twisted module, like the main graphical client probably will be in the near future. --- src/leap/common/events/client.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/leap/common/events/client.py b/src/leap/common/events/client.py index 1a026ea..5ee617e 100644 --- a/src/leap/common/events/client.py +++ b/src/leap/common/events/client.py @@ -174,7 +174,8 @@ class EventsClient(object): :type content: list """ logger.debug("Emitting event: (%s, %s)" % (event, content)) - self._send(str(event) + b'\0' + pickle.dumps(content)) + payload = str(event) + b'\0' + pickle.dumps(content) + self._send(payload) def _handle_event(self, event, content): """ @@ -186,7 +187,7 @@ class EventsClient(object): :type content: list """ logger.debug("Handling event %s..." % event) - for uid in self._callbacks[event].keys(): + for uid in self._callbacks[event]: callback = self._callbacks[event][uid] logger.debug("Executing callback %s." % uid) self._run_callback(callback, event, content) @@ -398,8 +399,7 @@ class EventsClientThread(threading.Thread, EventsClient): :param content: The content of the event. :type content: list """ - from twisted.internet import reactor - reactor.callFromThread(callback, event, *content) + self._loop.add_callback(lambda: callback(event, *content)) def register(self, event, callback, uid=None, replace=False): """ @@ -422,7 +422,8 @@ class EventsClientThread(threading.Thread, EventsClient): callback identified by the given uid and replace is False. """ self.ensure_client() - return EventsClient.register(self, event, callback, uid=uid, replace=replace) + return EventsClient.register( + self, event, callback, uid=uid, replace=replace) def unregister(self, event, uid=None): """ -- cgit v1.2.3 From 87339921eac261954e39901e3563851830309cc5 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 20 Jul 2015 19:18:03 -0400 Subject: [bug] do not add a port string to non-tcp addresses this, together with the events server registration, was breaking the events signalling on the client whenever it used ipc:// sockets. --- src/leap/common/events/zmq_components.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/leap/common/events/zmq_components.py b/src/leap/common/events/zmq_components.py index 04f71e0..f99c754 100644 --- a/src/leap/common/events/zmq_components.py +++ b/src/leap/common/events/zmq_components.py @@ -128,16 +128,21 @@ class TxZmqComponent(object): proto, addr, port = ADDRESS_RE.search(address).groups() - if port is None or port is '0': - params = proto, addr - port = socket.bind_to_random_port("%s://%s" % params) - # XXX this log doesn't appear - logger.debug("Binded %s to %s://%s." % ((connClass,) + params)) + if proto == "tcp": + if port is None or port is '0': + params = proto, addr + port = socket.bind_to_random_port("%s://%s" % params) + logger.debug("Binded %s to %s://%s." % ((connClass,) + params)) + else: + params = proto, addr, int(port) + socket.bind("%s://%s:%d" % params) + logger.debug( + "Binded %s to %s://%s:%d." % ((connClass,) + params)) else: - params = proto, addr, int(port) - socket.bind("%s://%s:%d" % params) - # XXX this log doesn't appear - logger.debug("Binded %s to %s://%s:%d." % ((connClass,) + params)) + params = proto, addr + socket.bind("%s://%s" % params) + logger.debug( + "Binded %s to %s://%s" % ((connClass,) + params)) self._connections.append(connection) return connection, port -- cgit v1.2.3 From 467b14fa2e29ecd6f41d4834b00593d8c86cddc5 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 13 Jul 2015 11:48:47 -0400 Subject: [feature] add global flag for disabling the events framework this will be used to allow the unittests to disable the events framework. this way, emit() will become a passthrough. note that, until now, the basetest class is making use of the threaded version of the client, which launches a zmq tornado-based ioloop. this is wrong, and will have to be addressed in a future commit. we'll have to make use of the global EVENTS_ENABLED flag in the txclient version when those changes are made. Related: #7259 Relases: 0.4.2 --- changes/feature_7259_disable_events | 1 + src/leap/common/events/__init__.py | 7 +++---- src/leap/common/events/client.py | 12 +++++------- src/leap/common/events/flags.py | 28 ++++++++++++++++++++++++++++ src/leap/common/events/txclient.py | 6 +----- src/leap/common/testing/basetest.py | 21 +++++++++++++-------- 6 files changed, 51 insertions(+), 24 deletions(-) create mode 100644 changes/feature_7259_disable_events create mode 100644 src/leap/common/events/flags.py diff --git a/changes/feature_7259_disable_events b/changes/feature_7259_disable_events new file mode 100644 index 0000000..d08d3c1 --- /dev/null +++ b/changes/feature_7259_disable_events @@ -0,0 +1 @@ +- Add a flag to disable events framework. Closes: #7259 diff --git a/src/leap/common/events/__init__.py b/src/leap/common/events/__init__.py index 9269b9a..87ed8ae 100644 --- a/src/leap/common/events/__init__.py +++ b/src/leap/common/events/__init__.py @@ -14,8 +14,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - - """ This is an events mechanism that uses a server to allow for emitting events between clients. @@ -37,13 +35,13 @@ To emit an event, use leap.common.events.emit(): >>> from leap.common.events import catalog >>> emit(catalog.CLIENT_UID) """ - - import logging import argparse from leap.common.events import client from leap.common.events import server +from leap.common.events.flags import set_events_enabled + from leap.common.events import catalog @@ -52,6 +50,7 @@ __all__ = [ "unregister", "emit", "catalog", + "set_events_enabled" ] diff --git a/src/leap/common/events/client.py b/src/leap/common/events/client.py index 5ee617e..1744341 100644 --- a/src/leap/common/events/client.py +++ b/src/leap/common/events/client.py @@ -14,8 +14,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - - """ The client end point of the events mechanism. @@ -27,8 +25,6 @@ When a client registers a callback for a given event, it also tells the server that it wants to be notified whenever events of that type are sent by some other client. """ - - import logging import collections import uuid @@ -59,6 +55,7 @@ from leap.common.zmq_utils import PUBLIC_KEYS_PREFIX from leap.common.events.errors import CallbackAlreadyRegisteredError from leap.common.events.server import EMIT_ADDR from leap.common.events.server import REG_ADDR +from leap.common.events import flags from leap.common.events import catalog @@ -173,9 +170,10 @@ class EventsClient(object): :param content: The content of the event. :type content: list """ - logger.debug("Emitting event: (%s, %s)" % (event, content)) - payload = str(event) + b'\0' + pickle.dumps(content) - self._send(payload) + if flags.EVENTS_ENABLED: + logger.debug("Emitting event: (%s, %s)" % (event, content)) + payload = str(event) + b'\0' + pickle.dumps(content) + self._send(payload) def _handle_event(self, event, content): """ diff --git a/src/leap/common/events/flags.py b/src/leap/common/events/flags.py new file mode 100644 index 0000000..137f663 --- /dev/null +++ b/src/leap/common/events/flags.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# __init__.py +# Copyright (C) 2015 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Flags for the events framework. +""" +from leap.common.check import leap_assert + +EVENTS_ENABLED = True + + +def set_events_enabled(flag): + leap_assert(isinstance(flag, bool)) + global EVENTS_ENABLED + EVENTS_ENABLED = flag diff --git a/src/leap/common/events/txclient.py b/src/leap/common/events/txclient.py index 0dcfc08..dfd0533 100644 --- a/src/leap/common/events/txclient.py +++ b/src/leap/common/events/txclient.py @@ -14,8 +14,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - - """ The client end point of the events mechanism, implemented using txzmq. @@ -27,8 +25,6 @@ When a client registers a callback for a given event, it also tells the server that it wants to be notified whenever events of that type are sent by some other client. """ - - import logging import pickle @@ -62,7 +58,7 @@ class EventsTxClient(TxZmqClientComponent, EventsClient): """ def __init__(self, emit_addr=EMIT_ADDR, reg_addr=REG_ADDR, - path_prefix=None): + path_prefix=None): """ Initialize the events server. """ diff --git a/src/leap/common/testing/basetest.py b/src/leap/common/testing/basetest.py index 3fbcf33..3d3cee0 100644 --- a/src/leap/common/testing/basetest.py +++ b/src/leap/common/testing/basetest.py @@ -30,8 +30,11 @@ except ImportError: from leap.common.check import leap_assert from leap.common.events import server as events_server from leap.common.events import client as events_client +from leap.common.events import flags, set_events_enabled from leap.common.files import mkdir_p, check_and_fix_urw_only +set_events_enabled(False) + class BaseLeapTest(unittest.TestCase): """ @@ -73,12 +76,13 @@ class BaseLeapTest(unittest.TestCase): @classmethod def _init_events(cls): - cls._server = events_server.ensure_server( - emit_addr="tcp://127.0.0.1:0", - reg_addr="tcp://127.0.0.1:0") - events_client.configure_client( - emit_addr="tcp://127.0.0.1:%d" % cls._server.pull_port, - reg_addr="tcp://127.0.0.1:%d" % cls._server.pub_port) + if flags.EVENTS_ENABLED: + cls._server = events_server.ensure_server( + emit_addr="tcp://127.0.0.1:0", + reg_addr="tcp://127.0.0.1:0") + events_client.configure_client( + emit_addr="tcp://127.0.0.1:%d" % cls._server.pull_port, + reg_addr="tcp://127.0.0.1:%d" % cls._server.pub_port) @classmethod def tearDownEnv(cls): @@ -87,8 +91,9 @@ class BaseLeapTest(unittest.TestCase): - restores the default PATH and HOME variables - removes the temporal folder """ - events_client.shutdown() - cls._server.shutdown() + if flags.EVENTS_ENABLED: + events_client.shutdown() + cls._server.shutdown() os.environ["PATH"] = cls.old_path os.environ["HOME"] = cls.old_home -- cgit v1.2.3 From 46870f1e26ef159bf6fe4744aba3b00a5e81cc3b Mon Sep 17 00:00:00 2001 From: drebs Date: Wed, 8 Jul 2015 19:14:00 -0300 Subject: [feat] add close method for http agent The ability to close cached connections is needed in order to have a clean reactor when the program ends. --- src/leap/common/http.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/leap/common/http.py b/src/leap/common/http.py index d4a214c..8d22f2c 100644 --- a/src/leap/common/http.py +++ b/src/leap/common/http.py @@ -80,6 +80,7 @@ class HTTPClient(object): policy = get_compatible_ssl_context_factory(cert_file) + self._pool = pool self._agent = Agent( reactor, policy, @@ -110,6 +111,12 @@ class HTTPClient(object): d.addCallback(readBody) return d + def close(self): + """ + Close any cached connections. + """ + self._pool.closeCachedConnections() + class StringBodyProducer(object): """ A producer that writes the body of a request to a consumer. -- cgit v1.2.3 From 34f6b07e08540fd9b2ec473d1e4e5a15be4feacc Mon Sep 17 00:00:00 2001 From: drebs Date: Wed, 8 Jul 2015 19:15:56 -0300 Subject: [bug] add http request timeout The connectTimeout parameter of twisted.web.client.Agent only acts on the connection setup, and the Agent will wait forever for incoming data after the connection has been established. This commit adds a timeout for the connection, and will cancel the deferred if the result has not been received after a certain number of seconds. --- changes/bug_7234_add-http-request-timeout | 1 + src/leap/common/http.py | 260 ++++++++++++++++++++++++------ 2 files changed, 215 insertions(+), 46 deletions(-) create mode 100644 changes/bug_7234_add-http-request-timeout diff --git a/changes/bug_7234_add-http-request-timeout b/changes/bug_7234_add-http-request-timeout new file mode 100644 index 0000000..d18b28b --- /dev/null +++ b/changes/bug_7234_add-http-request-timeout @@ -0,0 +1 @@ + o Add http request timeout. Related to #7234. diff --git a/src/leap/common/http.py b/src/leap/common/http.py index 8d22f2c..c93e65b 100644 --- a/src/leap/common/http.py +++ b/src/leap/common/http.py @@ -36,21 +36,24 @@ from zope.interface import implements from twisted.internet import reactor from twisted.internet import defer -from twisted.internet.defer import succeed +from twisted.python import failure from twisted.web.client import Agent from twisted.web.client import HTTPConnectionPool +from twisted.web.client import _HTTP11ClientFactory as HTTP11ClientFactory from twisted.web.client import readBody from twisted.web.http_headers import Headers from twisted.web.iweb import IBodyProducer +from twisted.web._newclient import HTTP11ClientProtocol -def createPool(maxPersistentPerHost=10, persistent=True): - pool = HTTPConnectionPool(reactor, persistent) - pool.maxPersistentPerHost = maxPersistentPerHost - return pool +__all__ = ["HTTPClient"] -_pool = createPool() + +# A default HTTP timeout is used for 2 distinct purposes: +# 1. as HTTP connection timeout, prior to connection estabilshment. +# 2. as data reception timeout, after the connection has been established. +DEFAULT_HTTP_TIMEOUT = 30 # seconds class HTTPClient(object): @@ -66,28 +69,36 @@ class HTTPClient(object): in order to avoid resource abuse on huge requests batches. """ - def __init__(self, cert_file=None, pool=_pool): + def __init__(self, cert_file=None, timeout=DEFAULT_HTTP_TIMEOUT): """ Init the HTTP client :param cert_file: The path to the certificate file, if None given the system's CAs will be used. :type cert_file: str - :param pool: An optional dedicated connection pool to override the - default shared one. - :type pool: HTTPConnectionPool + :param timeout: The amount of time that this Agent will wait for the + peer to accept a connection and for each request to be + finished. If a pool is passed, then this argument is + ignored. + :type timeout: float """ - policy = get_compatible_ssl_context_factory(cert_file) - - self._pool = pool + self._timeout = timeout + self._pool = self._createPool() self._agent = Agent( reactor, - policy, - pool=pool) - self._semaphore = defer.DeferredSemaphore(pool.maxPersistentPerHost) + get_compatible_ssl_context_factory(cert_file), + pool=self._pool, + connectTimeout=self._timeout) + self._semaphore = defer.DeferredSemaphore( + self._pool.maxPersistentPerHost) - def request(self, url, method='GET', body=None, headers={}): + def _createPool(self, maxPersistentPerHost=10, persistent=True): + pool = _HTTPConnectionPool(reactor, persistent, self._timeout) + pool.maxPersistentPerHost = maxPersistentPerHost + return pool + + def _request(self, url, method, body, headers): """ Perform an HTTP request. @@ -104,51 +115,208 @@ class HTTPClient(object): :rtype: twisted.internet.defer.Deferred """ if body: - body = HTTPClient.StringBodyProducer(body) - d = self._semaphore.run(self._agent.request, - method, url, headers=Headers(headers), - bodyProducer=body) + body = _StringBodyProducer(body) + d = self._agent.request( + method, url, headers=Headers(headers), bodyProducer=body) d.addCallback(readBody) return d + def request(self, url, method='GET', body=None, headers={}): + """ + Perform an HTTP request, but limit the maximum amount of concurrent + connections. + + :param url: The URL for the request. + :type url: str + :param method: The HTTP method of the request. + :type method: str + :param body: The body of the request, if any. + :type body: str + :param headers: The headers of the request. + :type headers: dict + + :return: A deferred that fires with the body of the request. + :rtype: twisted.internet.defer.Deferred + """ + return self._semaphore.run(self._request, url, method, body, headers) + def close(self): """ Close any cached connections. """ self._pool.closeCachedConnections() - class StringBodyProducer(object): +# +# An IBodyProducer to write the body of an HTTP request as a string. +# + +class _StringBodyProducer(object): + """ + A producer that writes the body of a request to a consumer. + """ + + implements(IBodyProducer) + + def __init__(self, body): + """ + Initialize the string produer. + + :param body: The body of the request. + :type body: str + """ + self.body = body + self.length = len(body) + + def startProducing(self, consumer): + """ + Write the body to the consumer. + + :param consumer: Any IConsumer provider. + :type consumer: twisted.internet.interfaces.IConsumer + + :return: A successful deferred. + :rtype: twisted.internet.defer.Deferred + """ + consumer.write(self.body) + return defer.succeed(None) + + def pauseProducing(self): + pass + + def stopProducing(self): + pass + + +# +# Patched twisted.web classes +# + +class _HTTP11ClientProtocol(HTTP11ClientProtocol): + """ + A timeout-able HTTP 1.1 client protocol, that is instantiated by the + _HTTP11ClientFactory below. + """ + + def __init__(self, quiescentCallback, timeout): + """ + Initialize the protocol. + + :param quiescentCallback: + :type quiescentCallback: callable + :param timeout: A timeout, in seconds, for requests made by this + protocol. + :type timeout: float + """ + HTTP11ClientProtocol.__init__(self, quiescentCallback) + self._timeout = timeout + self._timeoutCall = None + + def request(self, request): + """ + Issue request over self.transport and return a Deferred which + will fire with a Response instance or an error. + + :param request: The object defining the parameters of the request to + issue. + :type request: twisted.web._newclient.Request + + :return: A deferred which fires after the request has finished. + :rtype: Deferred + """ + d = HTTP11ClientProtocol.request(self, request) + if self._timeout: + self._last_buffer_len = 0 + timeoutCall = reactor.callLater( + self._timeout, self._doTimeout, request) + self._timeoutCall = timeoutCall + return d + + def _doTimeout(self, request): """ - A producer that writes the body of a request to a consumer. + Give up the request because of a timeout. + + :param request: The object defining the parameters of the request to + issue. + :type request: twisted.web._newclient.Request + """ + self._giveUp( + failure.Failure( + defer.TimeoutError( + "Getting %s took longer than %s seconds." + % (request.absoluteURI, self._timeout)))) + + def _cancelTimeout(self): """ + Cancel the request timeout, when it's finished. + """ + if self._timeoutCall.active(): + self._timeoutCall.cancel() + self._timeoutCall = None + + def _finishResponse_WAITING(self, rest): + """ + Cancel the timeout when finished receiving the response. + """ + self._cancelTimeout() + HTTP11ClientProtocol._finishResponse_WAITING(self, rest) - implements(IBodyProducer) + def _finishResponse_TRANSMITTING(self, rest): + """ + Cancel the timeout when finished receiving the response. + """ + self._cancelTimeout() + HTTP11ClientProtocol._finishResponse_TRANSMITTING(self, rest) - def __init__(self, body): - """ - Initialize the string produer. + def dataReceived(self, bytes): + """ + Receive some data and extend the timeout period of this request. + + :param bytes: A string of indeterminate length. + :type bytes: str + """ + HTTP11ClientProtocol.dataReceived(self, bytes) + if self._timeoutCall and self._timeoutCall.active(): + self._timeoutCall.reset(self._timeout) - :param body: The body of the request. - :type body: str - """ - self.body = body - self.length = len(body) - def startProducing(self, consumer): - """ - Write the body to the consumer. +class _HTTP11ClientFactory(HTTP11ClientFactory): + """ + A timeout-able HTTP 1.1 client protocol factory. + """ - :param consumer: Any IConsumer provider. - :type consumer: twisted.internet.interfaces.IConsumer + def __init__(self, quiescentCallback, timeout): + """ + :param quiescentCallback: The quiescent callback to be passed to + protocol instances, used to return them to + the connection pool. + :type quiescentCallback: callable(Protocol) + :param timeout: The timeout, in seconds, for requests made by + protocols created by this factory. + :type timeout: float + """ + HTTP11ClientFactory.__init__(self, quiescentCallback) + self._timeout = timeout + + def buildProtocol(self, _): + """ + Build the HTTP 1.1 client protocol. + """ + return _HTTP11ClientProtocol(self._quiescentCallback, self._timeout) + + +class _HTTPConnectionPool(HTTPConnectionPool): + """ + A timeout-able HTTP connection pool. + """ - :return: A successful deferred. - :rtype: twisted.internet.defer.Deferred - """ - consumer.write(self.body) - return succeed(None) + _factory = _HTTP11ClientFactory - def pauseProducing(self): - pass + def __init__(self, reactor, persistent, timeout): + HTTPConnectionPool.__init__(self, reactor, persistent=persistent) + self._timeout = timeout - def stopProducing(self): - pass + def _newConnection(self, key, endpoint): + def quiescentCallback(protocol): + self._putConnection(key, protocol) + factory = self._factory(quiescentCallback, timeout=self._timeout) + return endpoint.connect(factory) -- cgit v1.2.3 From 07d421a32f3bb45932668f4951233166ada4e770 Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 23 Jul 2015 17:03:50 -0300 Subject: [bug] fix events ioloop client tests The events ioloop client is not completelly thread safe, so we have to use reactor.callFromThread whenever we need it to be thread-safe. Examples of this behaviour are the events ioloop client tests that depend on the firing of deferreds, which are not thread safe. This commit fixes tese tests. --- src/leap/common/tests/test_events.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/leap/common/tests/test_events.py b/src/leap/common/tests/test_events.py index 7ef3e1b..611781c 100644 --- a/src/leap/common/tests/test_events.py +++ b/src/leap/common/tests/test_events.py @@ -20,6 +20,7 @@ import os import logging import time +from twisted.internet.reactor import callFromThread from twisted.trial import unittest from twisted.internet import defer @@ -80,8 +81,8 @@ class EventsGenericClientTestCase(object): """ event = catalog.CLIENT_UID d = defer.Deferred() - cbk_fail = lambda event, _: d.errback(event) - cbk_succeed = lambda event, _: d.callback(event) + cbk_fail = lambda event, _: callFromThread(d.errback, event) + cbk_succeed = lambda event, _: callFromThread(d.callback, event) self._client.register(event, cbk_fail, uid=1) self._client.register(event, cbk_succeed, uid=1, replace=True) self._client.emit(event, None) @@ -105,9 +106,9 @@ class EventsGenericClientTestCase(object): """ event = catalog.CLIENT_UID d1 = defer.Deferred() - cbk1 = lambda event, _: d1.callback(event) + cbk1 = lambda event, _: callFromThread(d1.callback, event) d2 = defer.Deferred() - cbk2 = lambda event, _: d2.callback(event) + cbk2 = lambda event, _: callFromThread(d2.callback, event) self._client.register(event, cbk1) self._client.register(event, cbk2) self._client.emit(event, None) @@ -121,7 +122,7 @@ class EventsGenericClientTestCase(object): event = catalog.CLIENT_UID d = defer.Deferred() def cbk(events, _): - d.callback(event) + callFromThread(d.callback, event) self._client.register(event, cbk) self._client.emit(event, None) return d @@ -133,14 +134,17 @@ class EventsGenericClientTestCase(object): event1 = catalog.CLIENT_UID d = defer.Deferred() # register more than one callback for the same event - self._client.register(event1, lambda ev, _: d.errback(None)) - self._client.register(event1, lambda ev, _: d.errback(None)) + self._client.register( + event1, lambda ev, _: callFromThread(d.errback, None)) + self._client.register( + event1, lambda ev, _: callFromThread(d.errback, None)) # unregister and emit the event self._client.unregister(event1) self._client.emit(event1, None) # register and emit another event so the deferred can succeed event2 = catalog.CLIENT_SESSION_ID - self._client.register(event2, lambda ev, _: d.callback(None)) + self._client.register( + event2, lambda ev, _: callFromThread(d.callback, None)) self._client.emit(event2, None) return d @@ -151,9 +155,11 @@ class EventsGenericClientTestCase(object): event = catalog.CLIENT_UID d = defer.Deferred() # register one callback that would fail - uid = self._client.register(event, lambda ev, _: d.errback(None)) + uid = self._client.register( + event, lambda ev, _: callFromThread(d.errback, None)) # register one callback that will succeed - self._client.register(event, lambda ev, _: d.callback(None)) + self._client.register( + event, lambda ev, _: callFromThread(d.callback, None)) # unregister by uid and emit the event self._client.unregister(event, uid=uid) self._client.emit(event, None) -- cgit v1.2.3 From 25d3b7c80f85cd94159b574274108061a94f1bc9 Mon Sep 17 00:00:00 2001 From: Bruno Wagner Date: Wed, 22 Jul 2015 15:38:56 -0300 Subject: [style] Fixed pep8 warnings --- src/leap/common/__init__.py | 4 +-- src/leap/common/_version.py | 40 ++++++++++++------------- src/leap/common/ca_bundle.py | 1 + src/leap/common/config/pluggableconfig.py | 27 ++++++++++------- src/leap/common/config/tests/test_baseconfig.py | 30 +++++++++---------- src/leap/common/http.py | 21 ++++++------- src/leap/common/tests/test_certs.py | 6 ++-- src/leap/common/tests/test_events.py | 33 +++++++++++++++----- src/leap/common/zmq_utils.py | 2 -- 9 files changed, 96 insertions(+), 68 deletions(-) diff --git a/src/leap/common/__init__.py b/src/leap/common/__init__.py index 5619900..383e198 100644 --- a/src/leap/common/__init__.py +++ b/src/leap/common/__init__.py @@ -4,6 +4,7 @@ from leap.common import certs from leap.common import check from leap.common import files from leap.common import events +from ._version import get_versions logger = logging.getLogger(__name__) @@ -11,11 +12,10 @@ try: import pygeoip HAS_GEOIP = True except ImportError: - #logger.debug('PyGeoIP not found. Disabled Geo support.') + # logger.debug('PyGeoIP not found. Disabled Geo support.') HAS_GEOIP = False __all__ = ["certs", "check", "files", "events"] -from ._version import get_versions __version__ = get_versions()['version'] del get_versions diff --git a/src/leap/common/_version.py b/src/leap/common/_version.py index 597e2e4..de94ba8 100644 --- a/src/leap/common/_version.py +++ b/src/leap/common/_version.py @@ -1,5 +1,3 @@ - -IN_LONG_VERSION_PY = True # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (build by setup.py sdist) and build @@ -10,12 +8,16 @@ IN_LONG_VERSION_PY = True # versioneer-0.7+ (https://github.com/warner/python-versioneer) # these strings will be replaced by git during git-archive -git_refnames = "$Format:%d$" -git_full = "$Format:%H$" - import subprocess import sys +import re +import os.path + +IN_LONG_VERSION_PY = True +git_refnames = "$Format:%d$" +git_full = "$Format:%H$" + def run_command(args, cwd=None, verbose=False): try: @@ -37,10 +39,6 @@ def run_command(args, cwd=None, verbose=False): return stdout -import sys -import re -import os.path - def get_expanded_variables(versionfile_source): # the code embedded in _version.py can just fetch the value of these # variables. When used from setup.py, we don't want to import @@ -48,7 +46,7 @@ def get_expanded_variables(versionfile_source): # used from _version.py. variables = {} try: - f = open(versionfile_source,"r") + f = open(versionfile_source, "r") for line in f.readlines(): if line.strip().startswith("git_refnames ="): mo = re.search(r'=\s*"(.*)"', line) @@ -63,12 +61,13 @@ def get_expanded_variables(versionfile_source): pass return variables + def versions_from_expanded_variables(variables, tag_prefix, verbose=False): refnames = variables["refnames"].strip() if refnames.startswith("$Format"): if verbose: print("variables are unexpanded, not using") - return {} # unexpanded, so not in an unpacked git-archive tarball + return {} # unexpanded, so not in an unpacked git-archive tarball refs = set([r.strip() for r in refnames.strip("()").split(",")]) # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. @@ -84,7 +83,7 @@ def versions_from_expanded_variables(variables, tag_prefix, verbose=False): # "stabilization", as well as "HEAD" and "master". tags = set([r for r in refs if re.search(r'\d', r)]) if verbose: - print("discarding '%s', no digits" % ",".join(refs-tags)) + print("discarding '%s', no digits" % ",".join(refs - tags)) if verbose: print("likely tags: %s" % ",".join(sorted(tags))) for ref in sorted(tags): @@ -93,13 +92,14 @@ def versions_from_expanded_variables(variables, tag_prefix, verbose=False): r = ref[len(tag_prefix):] if verbose: print("picking %s" % r) - return { "version": r, - "full": variables["full"].strip() } + return {"version": r, + "full": variables["full"].strip()} # no suitable tags, so we use the full revision id if verbose: print("no suitable tags, using full revision id") - return { "version": variables["full"].strip(), - "full": variables["full"].strip() } + return {"version": variables["full"].strip(), + "full": variables["full"].strip()} + def versions_from_vcs(tag_prefix, versionfile_source, verbose=False): # this runs 'git' from the root of the source tree. That either means @@ -116,7 +116,7 @@ def versions_from_vcs(tag_prefix, versionfile_source, verbose=False): here = os.path.abspath(__file__) except NameError: # some py2exe/bbfreeze/non-CPython implementations don't do __file__ - return {} # not always correct + return {} # not always correct # versionfile_source is the relative path from the top of the source tree # (where the .git directory might live) to this file. Invert this to find @@ -163,7 +163,7 @@ def versions_from_parentdir(parentdir_prefix, versionfile_source, verbose=False) here = os.path.abspath(__file__) except NameError: # py2exe/bbfreeze/non-CPython don't have __file__ - return {} # without __file__, we have no hope + return {} # without __file__, we have no hope # versionfile_source is the relative path from the top of the source # tree to _version.py. Invert this to find the root from __file__. root = here @@ -189,8 +189,9 @@ tag_prefix = "" parentdir_prefix = "leap.common-" versionfile_source = "src/leap/common/_version.py" + def get_versions(default={"version": "unknown", "full": ""}, verbose=False): - variables = { "refnames": git_refnames, "full": git_full } + variables = {"refnames": git_refnames, "full": git_full} ver = versions_from_expanded_variables(variables, tag_prefix, verbose) if not ver: ver = versions_from_vcs(tag_prefix, versionfile_source, verbose) @@ -200,4 +201,3 @@ def get_versions(default={"version": "unknown", "full": ""}, verbose=False): if not ver: ver = default return ver - diff --git a/src/leap/common/ca_bundle.py b/src/leap/common/ca_bundle.py index d8c72a6..2c41d18 100644 --- a/src/leap/common/ca_bundle.py +++ b/src/leap/common/ca_bundle.py @@ -28,6 +28,7 @@ _system = platform.system() IS_MAC = _system == "Darwin" + def where(): """ Return the preferred certificate bundle. diff --git a/src/leap/common/config/pluggableconfig.py b/src/leap/common/config/pluggableconfig.py index 8535fa6..1a98427 100644 --- a/src/leap/common/config/pluggableconfig.py +++ b/src/leap/common/config/pluggableconfig.py @@ -27,7 +27,7 @@ import urlparse import jsonschema -#from leap.base.util.translations import LEAPTranslatable +# from leap.base.util.translations import LEAPTranslatable from leap.common.check import leap_assert @@ -163,8 +163,8 @@ class TranslatableType(object): return data # LEAPTranslatable(data) # needed? we already have an extended dict... - #def get_prep_value(self, data): - #return dict(data) + # def get_prep_value(self, data): + # return dict(data) class URIType(object): @@ -283,9 +283,13 @@ class PluggableConfig(object): except BaseException, e: raise TypeCastException( "Could not coerce %s, %s, " - "to format %s: %s" % (key, value, - _ftype.__class__.__name__, - e)) + "to format %s: %s" % ( + key, + value, + _ftype.__class__.__name__, + e + ) + ) return config @@ -303,9 +307,12 @@ class PluggableConfig(object): except BaseException, e: raise TypeCastException( "Could not serialize %s, %s, " - "by format %s: %s" % (key, value, - _ftype.__class__.__name__, - e)) + "by format %s: %s" % ( + key, + value, + _ftype.__class__.__name__, + e) + ) else: config[key] = value return config @@ -435,7 +442,7 @@ class PluggableConfig(object): content = self.deserialize(string) if not string and fromfile is not None: - #import ipdb;ipdb.set_trace() + # import ipdb;ipdb.set_trace() content = self.deserialize(fromfile=fromfile) if not content: diff --git a/src/leap/common/config/tests/test_baseconfig.py b/src/leap/common/config/tests/test_baseconfig.py index 8bdf4d0..e17e82d 100644 --- a/src/leap/common/config/tests/test_baseconfig.py +++ b/src/leap/common/config/tests/test_baseconfig.py @@ -29,21 +29,21 @@ from mock import Mock # reduced eipconfig sample config sample_config = { "gateways": [ - { - "capabilities": { - "adblock": False, - "transport": ["openvpn"], - "user_ips": False - }, - "host": "host.dev.example.org", - }, { - "capabilities": { - "adblock": False, - "transport": ["openvpn"], - "user_ips": False - }, - "host": "host2.dev.example.org", - } + { + "capabilities": { + "adblock": False, + "transport": ["openvpn"], + "user_ips": False + }, + "host": "host.dev.example.org", + }, { + "capabilities": { + "adblock": False, + "transport": ["openvpn"], + "user_ips": False + }, + "host": "host2.dev.example.org", + } ], "default_language": "en", "languages": [ diff --git a/src/leap/common/http.py b/src/leap/common/http.py index c93e65b..56938b4 100644 --- a/src/leap/common/http.py +++ b/src/leap/common/http.py @@ -150,6 +150,7 @@ class HTTPClient(object): # An IBodyProducer to write the body of an HTTP request as a string. # + class _StringBodyProducer(object): """ A producer that writes the body of a request to a consumer. @@ -254,18 +255,18 @@ class _HTTP11ClientProtocol(HTTP11ClientProtocol): self._timeoutCall = None def _finishResponse_WAITING(self, rest): - """ - Cancel the timeout when finished receiving the response. - """ - self._cancelTimeout() - HTTP11ClientProtocol._finishResponse_WAITING(self, rest) + """ + Cancel the timeout when finished receiving the response. + """ + self._cancelTimeout() + HTTP11ClientProtocol._finishResponse_WAITING(self, rest) def _finishResponse_TRANSMITTING(self, rest): - """ - Cancel the timeout when finished receiving the response. - """ - self._cancelTimeout() - HTTP11ClientProtocol._finishResponse_TRANSMITTING(self, rest) + """ + Cancel the timeout when finished receiving the response. + """ + self._cancelTimeout() + HTTP11ClientProtocol._finishResponse_TRANSMITTING(self, rest) def dataReceived(self, bytes): """ diff --git a/src/leap/common/tests/test_certs.py b/src/leap/common/tests/test_certs.py index 999071f..209e051 100644 --- a/src/leap/common/tests/test_certs.py +++ b/src/leap/common/tests/test_certs.py @@ -60,11 +60,13 @@ class CertsTest(BaseLeapTest): self.assertTrue(certs.should_redownload(cert_path)) def test_should_redownload_if_before(self): - new_now = lambda: time.struct_time(CERT_NOT_BEFORE) + def new_now(): + time.struct_time(CERT_NOT_BEFORE) self.assertTrue(certs.should_redownload(TEST_CERT_PEM, now=new_now)) def test_should_redownload_if_after(self): - new_now = lambda: time.struct_time(CERT_NOT_AFTER) + def new_now(): + time.struct_time(CERT_NOT_AFTER) self.assertTrue(certs.should_redownload(TEST_CERT_PEM, now=new_now)) def test_not_should_redownload(self): diff --git a/src/leap/common/tests/test_events.py b/src/leap/common/tests/test_events.py index 611781c..e2a918f 100644 --- a/src/leap/common/tests/test_events.py +++ b/src/leap/common/tests/test_events.py @@ -1,4 +1,4 @@ -## -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- # test_events.py # Copyright (C) 2013 LEAP # @@ -60,7 +60,10 @@ class EventsGenericClientTestCase(object): 'There should be no callback for this event.') # register one event event1 = catalog.CLIENT_UID - cbk1 = lambda event, _: True + + def cbk1(event, _): + return True + uid1 = self._client.register(event1, cbk1) # assert for correct registration self.assertTrue(len(callbacks) == 1) @@ -68,7 +71,10 @@ class EventsGenericClientTestCase(object): 'Could not register event in local client.') # register another event event2 = catalog.CLIENT_SESSION_ID - cbk2 = lambda event, _: True + + def cbk2(event, _): + return True + uid2 = self._client.register(event2, cbk2) # assert for correct registration self.assertTrue(len(callbacks) == 2) @@ -81,8 +87,13 @@ class EventsGenericClientTestCase(object): """ event = catalog.CLIENT_UID d = defer.Deferred() - cbk_fail = lambda event, _: callFromThread(d.errback, event) - cbk_succeed = lambda event, _: callFromThread(d.callback, event) + + def cbk_fail(event, _): + return callFromThread(d.errback, event) + + def cbk_succeed(event, _): + return callFromThread(d.callback, event) + self._client.register(event, cbk_fail, uid=1) self._client.register(event, cbk_succeed, uid=1, replace=True) self._client.emit(event, None) @@ -106,9 +117,15 @@ class EventsGenericClientTestCase(object): """ event = catalog.CLIENT_UID d1 = defer.Deferred() - cbk1 = lambda event, _: callFromThread(d1.callback, event) + + def cbk1(event, _): + return callFromThread(d1.callback, event) + d2 = defer.Deferred() - cbk2 = lambda event, _: callFromThread(d2.callback, event) + + def cbk2(event, _): + return d2.callback(event) + self._client.register(event, cbk1) self._client.register(event, cbk2) self._client.emit(event, None) @@ -121,8 +138,10 @@ class EventsGenericClientTestCase(object): """ event = catalog.CLIENT_UID d = defer.Deferred() + def cbk(events, _): callFromThread(d.callback, event) + self._client.register(event, cbk) self._client.emit(event, None) return d diff --git a/src/leap/common/zmq_utils.py b/src/leap/common/zmq_utils.py index 19625b9..0a781de 100644 --- a/src/leap/common/zmq_utils.py +++ b/src/leap/common/zmq_utils.py @@ -101,5 +101,3 @@ def maybe_create_and_get_certificates(basedir, name): mkdir_p(public_keys_dir) shutil.move(old_public_key, new_public_key) return zmq.auth.load_certificate(private_key) - - -- cgit v1.2.3 From a1a23479ea89518faa5e59f1490a6203453801ca Mon Sep 17 00:00:00 2001 From: Bruno Wagner Date: Wed, 22 Jul 2015 16:51:51 -0300 Subject: [style] fixed extra requires typo --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 776a477..ca83017 100644 --- a/setup.py +++ b/setup.py @@ -143,6 +143,6 @@ setup( # needed for leap.common.http # service_identity needed for propper hostname identification, # see http://twistedmatrix.com/documents/current/core/howto/ssl.html - 'Twisted': ["Twisted>=14.0.2", "service_identity", "zope.insterface"] + 'Twisted': ["Twisted>=14.0.2", "service_identity", "zope.interface"] }, ) -- cgit v1.2.3 From 78e818ebf64e0ca7a398dca35e951ff273c26fa5 Mon Sep 17 00:00:00 2001 From: Bruno Wagner Date: Wed, 22 Jul 2015 17:04:34 -0300 Subject: [tests] added setuptools_trial so the tests run using python setup.py test --- pkg/requirements-testing.pip | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/requirements-testing.pip b/pkg/requirements-testing.pip index 932a895..c3c05fd 100644 --- a/pkg/requirements-testing.pip +++ b/pkg/requirements-testing.pip @@ -1 +1,2 @@ mock +setuptools-trial -- cgit v1.2.3 From 301be892a67f88b8b9479531003403a77b36a5f2 Mon Sep 17 00:00:00 2001 From: Bruno Wagner Date: Wed, 22 Jul 2015 19:17:03 -0300 Subject: [tests] set environment for certs test --- src/leap/common/tests/test_certs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/leap/common/tests/test_certs.py b/src/leap/common/tests/test_certs.py index 209e051..8ebc0f4 100644 --- a/src/leap/common/tests/test_certs.py +++ b/src/leap/common/tests/test_certs.py @@ -43,10 +43,10 @@ CERT_NOT_AFTER = (2023, 9, 1, 17, 52, 16, 4, 244, 0) class CertsTest(BaseLeapTest): def setUp(self): - pass + self.setUpEnv() def tearDown(self): - pass + self.tearDownEnv() def test_should_redownload_if_no_cert(self): self.assertTrue(certs.should_redownload(certfile="")) -- cgit v1.2.3 From 5502318835626527d9818c360c7fd2b1a4b01145 Mon Sep 17 00:00:00 2001 From: Bruno Wagner Date: Wed, 22 Jul 2015 23:55:39 -0300 Subject: [tests] implemented http feature according to test Two test cases were broken and were implemented here: The first was that HTTPClient should share the connection between clients if a pool was not passed explicitly. If you initialize an HTTPClient without a pool, it will reuse a pool created on the class. The second was that you should be able to pass to the HTTPCLient a pool on initialization. Added that possibility and fixed the tests accordingly --- src/leap/common/http.py | 98 +++++++++++++++++++++----------------- src/leap/common/tests/test_http.py | 7 ++- 2 files changed, 59 insertions(+), 46 deletions(-) diff --git a/src/leap/common/http.py b/src/leap/common/http.py index 56938b4..f67507d 100644 --- a/src/leap/common/http.py +++ b/src/leap/common/http.py @@ -56,6 +56,50 @@ __all__ = ["HTTPClient"] DEFAULT_HTTP_TIMEOUT = 30 # seconds +class _HTTP11ClientFactory(HTTP11ClientFactory): + """ + A timeout-able HTTP 1.1 client protocol factory. + """ + + def __init__(self, quiescentCallback, timeout): + """ + :param quiescentCallback: The quiescent callback to be passed to + protocol instances, used to return them to + the connection pool. + :type quiescentCallback: callable(Protocol) + :param timeout: The timeout, in seconds, for requests made by + protocols created by this factory. + :type timeout: float + """ + HTTP11ClientFactory.__init__(self, quiescentCallback) + self._timeout = timeout + + def buildProtocol(self, _): + """ + Build the HTTP 1.1 client protocol. + """ + return _HTTP11ClientProtocol(self._quiescentCallback, self._timeout) + + +class _HTTPConnectionPool(HTTPConnectionPool): + """ + A timeout-able HTTP connection pool. + """ + + _factory = _HTTP11ClientFactory + + def __init__(self, reactor, persistent, timeout, maxPersistentPerHost=10): + HTTPConnectionPool.__init__(self, reactor, persistent=persistent) + self.maxPersistentPerHost = maxPersistentPerHost + self._timeout = timeout + + def _newConnection(self, key, endpoint): + def quiescentCallback(protocol): + self._putConnection(key, protocol) + factory = self._factory(quiescentCallback, timeout=self._timeout) + return endpoint.connect(factory) + + class HTTPClient(object): """ HTTP client done the twisted way, with a main focus on pinning the SSL @@ -69,7 +113,14 @@ class HTTPClient(object): in order to avoid resource abuse on huge requests batches. """ - def __init__(self, cert_file=None, timeout=DEFAULT_HTTP_TIMEOUT): + _pool = _HTTPConnectionPool( + reactor, + persistent=True, + timeout = DEFAULT_HTTP_TIMEOUT, + maxPersistentPerHost=10 + ) + + def __init__(self, cert_file=None, timeout=DEFAULT_HTTP_TIMEOUT, pool=None): """ Init the HTTP client @@ -84,7 +135,7 @@ class HTTPClient(object): """ self._timeout = timeout - self._pool = self._createPool() + self._pool = pool if pool is not None else self._pool self._agent = Agent( reactor, get_compatible_ssl_context_factory(cert_file), @@ -278,46 +329,3 @@ class _HTTP11ClientProtocol(HTTP11ClientProtocol): HTTP11ClientProtocol.dataReceived(self, bytes) if self._timeoutCall and self._timeoutCall.active(): self._timeoutCall.reset(self._timeout) - - -class _HTTP11ClientFactory(HTTP11ClientFactory): - """ - A timeout-able HTTP 1.1 client protocol factory. - """ - - def __init__(self, quiescentCallback, timeout): - """ - :param quiescentCallback: The quiescent callback to be passed to - protocol instances, used to return them to - the connection pool. - :type quiescentCallback: callable(Protocol) - :param timeout: The timeout, in seconds, for requests made by - protocols created by this factory. - :type timeout: float - """ - HTTP11ClientFactory.__init__(self, quiescentCallback) - self._timeout = timeout - - def buildProtocol(self, _): - """ - Build the HTTP 1.1 client protocol. - """ - return _HTTP11ClientProtocol(self._quiescentCallback, self._timeout) - - -class _HTTPConnectionPool(HTTPConnectionPool): - """ - A timeout-able HTTP connection pool. - """ - - _factory = _HTTP11ClientFactory - - def __init__(self, reactor, persistent, timeout): - HTTPConnectionPool.__init__(self, reactor, persistent=persistent) - self._timeout = timeout - - def _newConnection(self, key, endpoint): - def quiescentCallback(protocol): - self._putConnection(key, protocol) - factory = self._factory(quiescentCallback, timeout=self._timeout) - return endpoint.connect(factory) diff --git a/src/leap/common/tests/test_http.py b/src/leap/common/tests/test_http.py index e240ca3..a586fd1 100644 --- a/src/leap/common/tests/test_http.py +++ b/src/leap/common/tests/test_http.py @@ -47,7 +47,12 @@ class HTTPClientTest(BaseLeapTest): self.assertEquals(client._agent._pool, client2._agent._pool, "Pool was not reused by default") def test_agent_can_have_dedicated_custom_pool(self): - custom_pool = http.createPool(maxPersistentPerHost=42, persistent=False) + custom_pool = http._HTTPConnectionPool( + None, + timeout=10, + maxPersistentPerHost=42, + persistent=False + ) self.assertEquals(custom_pool.maxPersistentPerHost, 42, "Custom persistent connections limit is not being respected") self.assertFalse(custom_pool.persistent, -- cgit v1.2.3 From 486da2654c63262f0dbc2d603125f0c8c5c4ea74 Mon Sep 17 00:00:00 2001 From: Bruno Wagner Date: Thu, 23 Jul 2015 00:00:39 -0300 Subject: [tests] fixed events tests The events tests check for register and emit signals, but because the flag set_events_enabled was False by default in the tests, no signals were being emitted. I added the flag to the setUp and tearDown of the tests, they are still very slow but at least they are passing now --- src/leap/common/tests/test_events.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/leap/common/tests/test_events.py b/src/leap/common/tests/test_events.py index e2a918f..8f08eeb 100644 --- a/src/leap/common/tests/test_events.py +++ b/src/leap/common/tests/test_events.py @@ -26,6 +26,7 @@ from twisted.internet import defer from leap.common.events import server from leap.common.events import client +from leap.common.events import flags from leap.common.events import txclient from leap.common.events import catalog from leap.common.events.errors import CallbackAlreadyRegisteredError @@ -44,10 +45,12 @@ class EventsGenericClientTestCase(object): self._client.configure_client( emit_addr="tcp://127.0.0.1:%d" % self._server.pull_port, reg_addr="tcp://127.0.0.1:%d" % self._server.pub_port) + flags.set_events_enabled(True) def tearDown(self): self._client.shutdown() self._server.shutdown() + flags.set_events_enabled(False) # wait a bit for sockets to close properly time.sleep(0.1) -- cgit v1.2.3 From e8d54bd3dc4b5f0327a30c0a6848dd832beb7da0 Mon Sep 17 00:00:00 2001 From: Bruno Wagner Date: Thu, 23 Jul 2015 00:09:03 -0300 Subject: [style] fixed pep8 warnings on http and test events --- src/leap/common/http.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/leap/common/http.py b/src/leap/common/http.py index f67507d..8e8d3d9 100644 --- a/src/leap/common/http.py +++ b/src/leap/common/http.py @@ -116,7 +116,7 @@ class HTTPClient(object): _pool = _HTTPConnectionPool( reactor, persistent=True, - timeout = DEFAULT_HTTP_TIMEOUT, + timeout=DEFAULT_HTTP_TIMEOUT, maxPersistentPerHost=10 ) -- cgit v1.2.3 From 16242a1ebbac7ee0b38d3199b2a1317297d4506b Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 23 Jul 2015 16:36:12 -0400 Subject: [tests] fix initialization of basetest case --- src/leap/common/testing/test_basetest.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/leap/common/testing/test_basetest.py b/src/leap/common/testing/test_basetest.py index cf0962d..ec42a62 100644 --- a/src/leap/common/testing/test_basetest.py +++ b/src/leap/common/testing/test_basetest.py @@ -83,12 +83,10 @@ class TestInitBaseLeapTest(BaseLeapTest): """ def setUp(self): - """nuke it""" - pass + self.setUpEnv() def tearDown(self): - """nuke it""" - pass + self.tearDownEnv() def test_path_is_changed(self): """tests whether we have changed the PATH env var""" -- cgit v1.2.3 From c5cb769c75c5dc9b3b22b3b3a8102c3244b4edad Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 27 Jul 2015 21:42:47 -0400 Subject: [pkg] add script to install base requirements - update pip - install base reqs, with insecure flags for dirspec. fuck canonical. --- pkg/pip_install_requirements.sh | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100755 pkg/pip_install_requirements.sh diff --git a/pkg/pip_install_requirements.sh b/pkg/pip_install_requirements.sh new file mode 100755 index 0000000..8576c8c --- /dev/null +++ b/pkg/pip_install_requirements.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# Update pip and install LEAP base requirements. +# For convenience, u1db and dirspec are allowed with insecure flags enabled. +# Use at your own risk. +pip install -U pip +pip install --allow-external dirspec --allow-unverified dirspec -r pkg/requirements.pip -- cgit v1.2.3 From f28824c8f795bdf91cd837aef8516963b53246c6 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 27 Jul 2015 22:32:38 -0400 Subject: [pkg] add AUTHORS file + one-liner to generate it --- AUTHORS | 7 +++++++ pkg/tools/get_authors.sh | 2 ++ 2 files changed, 9 insertions(+) create mode 100644 AUTHORS create mode 100755 pkg/tools/get_authors.sh diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e47319e --- /dev/null +++ b/AUTHORS @@ -0,0 +1,7 @@ +Tomás Touceda +Kali Kaneko +drebs +Ivan Alejandro +Ruben Pollan +Bruno Wagner +Victor Shyba diff --git a/pkg/tools/get_authors.sh b/pkg/tools/get_authors.sh new file mode 100755 index 0000000..0169bb1 --- /dev/null +++ b/pkg/tools/get_authors.sh @@ -0,0 +1,2 @@ +#!/bin/sh +git log --format='%aN <%aE>' | awk '{arr[$0]++} END{for (i in arr){print arr[i], i;}}' | sort -rn | cut -d' ' -f2- -- cgit v1.2.3 From 8668cf04e59bd4c1fe45a7e74cd0c214ae50b052 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 28 Jul 2015 09:55:33 -0400 Subject: [tests] add pep8 to requirements-testing --- pkg/requirements-testing.pip | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/requirements-testing.pip b/pkg/requirements-testing.pip index c3c05fd..c5a3ad0 100644 --- a/pkg/requirements-testing.pip +++ b/pkg/requirements-testing.pip @@ -1,2 +1,3 @@ mock setuptools-trial +pep8 -- cgit v1.2.3 From a119dd4fa2fc4a14577fd2d6e32dff950d934193 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 28 Jul 2015 11:41:32 -0400 Subject: [style] more pep8 cleanup --- pkg/utils.py | 6 +++--- src/leap/common/certs.py | 3 ++- src/leap/common/http.py | 3 ++- src/leap/common/tests/test_http.py | 18 +++++++++++++----- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/pkg/utils.py b/pkg/utils.py index deace14..521cd4e 100644 --- a/pkg/utils.py +++ b/pkg/utils.py @@ -58,9 +58,9 @@ def parse_requirements(reqfiles=['requirements.txt', if re.match(r'\s*-e\s+', line): pass # do not try to do anything with externals on vcs - #requirements.append(re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1', - #line)) - # http://foo.bar/baz/foobar/zipball/master#egg=foobar + # requirements.append(re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1', + # line)) + # http://foo.bar/baz/foobar/zipball/master#egg=foobar elif re.match(r'\s*https?:', line): requirements.append(re.sub(r'\s*https?:.*#egg=(.*)$', r'\1', line)) diff --git a/src/leap/common/certs.py b/src/leap/common/certs.py index c8e0743..37ede8e 100644 --- a/src/leap/common/certs.py +++ b/src/leap/common/certs.py @@ -192,6 +192,7 @@ def get_compatible_ssl_context_factory(cert_path=None): return policy else: raise Exception((""" - Twisted 14.0.2 is needed in order to have secure Client Web SSL Contexts, not %s + Twisted 14.0.2 is needed in order to have secure + Client Web SSL Contexts, not %s See: http://twistedmatrix.com/trac/ticket/7647 """) % (twisted.version.base())) diff --git a/src/leap/common/http.py b/src/leap/common/http.py index 8e8d3d9..1e7ded7 100644 --- a/src/leap/common/http.py +++ b/src/leap/common/http.py @@ -120,7 +120,8 @@ class HTTPClient(object): maxPersistentPerHost=10 ) - def __init__(self, cert_file=None, timeout=DEFAULT_HTTP_TIMEOUT, pool=None): + def __init__(self, cert_file=None, + timeout=DEFAULT_HTTP_TIMEOUT, pool=None): """ Init the HTTP client diff --git a/src/leap/common/tests/test_http.py b/src/leap/common/tests/test_http.py index a586fd1..f44550f 100644 --- a/src/leap/common/tests/test_http.py +++ b/src/leap/common/tests/test_http.py @@ -43,8 +43,11 @@ class HTTPClientTest(BaseLeapTest): def test_agents_sharing_pool_by_default(self): client = http.HTTPClient() client2 = http.HTTPClient(TEST_CERT_PEM) - self.assertNotEquals(client._agent, client2._agent, "Expected dedicated agents") - self.assertEquals(client._agent._pool, client2._agent._pool, "Pool was not reused by default") + self.assertNotEquals( + client._agent, client2._agent, "Expected dedicated agents") + self.assertEquals( + client._agent._pool, client2._agent._pool, + "Pool was not reused by default") def test_agent_can_have_dedicated_custom_pool(self): custom_pool = http._HTTPConnectionPool( @@ -54,14 +57,19 @@ class HTTPClientTest(BaseLeapTest): persistent=False ) self.assertEquals(custom_pool.maxPersistentPerHost, 42, - "Custom persistent connections limit is not being respected") + "Custom persistent connections " + "limit is not being respected") self.assertFalse(custom_pool.persistent, "Custom persistence is not being respected") default_client = http.HTTPClient() custom_client = http.HTTPClient(pool=custom_pool) - self.assertNotEquals(default_client._agent, custom_client._agent, "No agent reuse is expected") - self.assertEquals(custom_pool, custom_client._agent._pool, "Custom pool usage was not respected") + self.assertNotEquals( + default_client._agent, custom_client._agent, + "No agent reuse is expected") + self.assertEquals( + custom_pool, custom_client._agent._pool, + "Custom pool usage was not respected") if __name__ == "__main__": unittest.main() -- cgit v1.2.3 From 08779379337ff77729c9d2ebdb6f4aaf486d38a7 Mon Sep 17 00:00:00 2001 From: Victor Shyba Date: Tue, 28 Jul 2015 16:26:14 -0300 Subject: [bug] Consider events flag when ensuring client Change EventsClientThread behavior so it won't start anymore if the events flag is set to False --- src/leap/common/events/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/leap/common/events/client.py b/src/leap/common/events/client.py index 1744341..8d8d522 100644 --- a/src/leap/common/events/client.py +++ b/src/leap/common/events/client.py @@ -466,7 +466,7 @@ class EventsClientThread(threading.Thread, EventsClient): Make sure the events client thread is started. """ with self._lock: - if not self.is_alive(): + if flags.EVENTS_ENABLED and not self.is_alive(): self.daemon = True self.start() self._initialized.wait() -- cgit v1.2.3 From e47f4bcc0eb9c3d08c649adcb62b0325f439113e Mon Sep 17 00:00:00 2001 From: Victor Shyba Date: Tue, 28 Jul 2015 16:48:04 -0300 Subject: [test] set flag before starting client Test client will only start with flag set to True. Change EventsGenericClientTestCase to set the flag on the first line of setUp. --- src/leap/common/tests/test_events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/leap/common/tests/test_events.py b/src/leap/common/tests/test_events.py index 8f08eeb..2ad097e 100644 --- a/src/leap/common/tests/test_events.py +++ b/src/leap/common/tests/test_events.py @@ -39,13 +39,13 @@ if 'DEBUG' in os.environ: class EventsGenericClientTestCase(object): def setUp(self): + flags.set_events_enabled(True) self._server = server.ensure_server( emit_addr="tcp://127.0.0.1:0", reg_addr="tcp://127.0.0.1:0") self._client.configure_client( emit_addr="tcp://127.0.0.1:%d" % self._server.pull_port, reg_addr="tcp://127.0.0.1:%d" % self._server.pub_port) - flags.set_events_enabled(True) def tearDown(self): self._client.shutdown() -- cgit v1.2.3 From 2e911bd0c949b7f42824ed87c467b1ac0919224a Mon Sep 17 00:00:00 2001 From: Victor Shyba Date: Wed, 29 Jul 2015 13:30:41 -0300 Subject: [refactor] Extract flags check to caller Checking was done inside of emit method. Doing on emit function at a module level makes it cleaner with less lines inside of check. --- src/leap/common/events/client.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/leap/common/events/client.py b/src/leap/common/events/client.py index 8d8d522..4790fc3 100644 --- a/src/leap/common/events/client.py +++ b/src/leap/common/events/client.py @@ -170,10 +170,9 @@ class EventsClient(object): :param content: The content of the event. :type content: list """ - if flags.EVENTS_ENABLED: - logger.debug("Emitting event: (%s, %s)" % (event, content)) - payload = str(event) + b'\0' + pickle.dumps(content) - self._send(payload) + logger.debug("Emitting event: (%s, %s)" % (event, content)) + payload = str(event) + b'\0' + pickle.dumps(content) + self._send(payload) def _handle_event(self, event, content): """ @@ -537,7 +536,8 @@ def emit(event, *content): :param content: The content of the event. :type content: list """ - return EventsClientThread.instance().emit(event, *content) + if flags.EVENTS_ENABLED: + return EventsClientThread.instance().emit(event, *content) def instance(): -- cgit v1.2.3 From 2d9ce114daeaf6c4c193079b018576ad1f4f8f28 Mon Sep 17 00:00:00 2001 From: Victor Shyba Date: Wed, 29 Jul 2015 13:34:08 -0300 Subject: [bug] register and unregister controlled by flag Since register and unregister cant be used without full zmq initialization, it should make sense to also check flag for them. --- src/leap/common/events/client.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/leap/common/events/client.py b/src/leap/common/events/client.py index 4790fc3..e085f5b 100644 --- a/src/leap/common/events/client.py +++ b/src/leap/common/events/client.py @@ -508,8 +508,9 @@ def register(event, callback, uid=None, replace=False): :raises CallbackAlreadyRegisteredError: when there's already a callback identified by the given uid and replace is False. """ - return EventsClientThread.instance().register( - event, callback, uid=uid, replace=replace) + if flags.EVENTS_ENABLED: + return EventsClientThread.instance().register( + event, callback, uid=uid, replace=replace) def unregister(event, uid=None): @@ -524,7 +525,8 @@ def unregister(event, uid=None): :param uid: The callback uid. :type uid: str """ - return EventsClientThread.instance().unregister(event, uid=uid) + if flags.EVENTS_ENABLED: + return EventsClientThread.instance().unregister(event, uid=uid) def emit(event, *content): -- cgit v1.2.3 From 6ff2cf53bf57f7b0dcf24c7528504009f05fdb56 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 29 Jul 2015 16:08:30 -0400 Subject: [docs] remove ref to protobuf from README we stopped using protobuf after the events submodule refactor --- README.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/README.rst b/README.rst index 9164054..9fcf788 100644 --- a/README.rst +++ b/README.rst @@ -15,7 +15,6 @@ A collection of shared utils used by the several python LEAP subprojects. Library dependencies -------------------- -* ``protobuf-compiler`` * ``libssl-dev`` Python dependencies -- cgit v1.2.3 From 638f3edb91ef8c6254d84a2eb083f725da2a878a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Thu, 30 Jul 2015 18:29:59 +0200 Subject: [feat] use wheels to install dependencies generate_wheels uses $WHEELHOUSE to generate and store the wheels for requirements.pip and requirements-testing.pip (if it exists). pip_install_requirements.sh installs requirements.pip from them if possible (if not, then it fetches them from pypi) or, if passed the --testing flag, it installs requirements-testing.pip. --- pkg/generate_wheels.sh | 14 ++++++++ pkg/pip_install_requirements.sh | 71 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 81 insertions(+), 4 deletions(-) create mode 100755 pkg/generate_wheels.sh diff --git a/pkg/generate_wheels.sh b/pkg/generate_wheels.sh new file mode 100755 index 0000000..4cdc34e --- /dev/null +++ b/pkg/generate_wheels.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# Generate wheels for dependencies +# For convenience, dirspec is allowed with insecure flags enabled. +# Use at your own risk. + +if [ "$WHEELHOUSE" = "" ]; then + WHEELHOUSE=$HOME/wheelhouse +fi + +pip wheel --wheel-dir $WHEELHOUSE pip +pip wheel --wheel-dir $WHEELHOUSE --allow-external dirspec --allow-unverified dirspec -r pkg/requirements.pip +if [ -f pkg/requirements-testing.pip ]; then + pip wheel --wheel-dir $WHEELHOUSE -r pkg/requirements-testing.pip +fi diff --git a/pkg/pip_install_requirements.sh b/pkg/pip_install_requirements.sh index 8576c8c..949f738 100755 --- a/pkg/pip_install_requirements.sh +++ b/pkg/pip_install_requirements.sh @@ -1,6 +1,69 @@ #!/bin/sh -# Update pip and install LEAP base requirements. -# For convenience, u1db and dirspec are allowed with insecure flags enabled. +# Update pip and install LEAP base/testing requirements. +# For convenience, $insecure_packages are allowed with insecure flags enabled. # Use at your own risk. -pip install -U pip -pip install --allow-external dirspec --allow-unverified dirspec -r pkg/requirements.pip +# See $usage for help + +insecure_packages="dirspec" + +return_wheelhouse() { + if [ "$WHEELHOUSE" = "" ]; then + WHEELHOUSE=$HOME/wheelhouse + fi + + if [ ! -d "$WHEELHOUSE" ]; then + mkdir $WHEELHOUSE + fi + + echo "$WHEELHOUSE" +} + +show_help() { + usage="Usage: $0 [--testing]\n --testing\tInstall dependencies from requirements-testing.pip\n +\t\tOtherwise, it will install requirements.pip" + echo $usage + + exit 1 +} + +process_arguments() { + testing=false + while [ "$#" -gt 0 ]; do + # From http://stackoverflow.com/a/31443098 + case "$1" in + --help) show_help;; + --testing) testing=true; shift 1;; + + -h) show_help;; + -*) echo "unknown option: $1" >&2; exit 1;; + esac + done +} + +return_insecure_flags() { + for insecure_package in $insecure_packages; do + flags="$flags --allow-external $insecure_package --allow-unverified $insecure_package" + done + + echo $flags +} + +return_packages() { + if $testing ; then + packages="-r pkg/requirements-testing.pip" + else + packages="-r pkg/requirements.pip" + fi + + echo $packages +} + +process_arguments $@ +wheelhouse=`return_wheelhouse` +install_options="-U --find-links=$wheelhouse" +insecure_flags=`return_insecure_flags` +packages=`return_packages` + +pip install -U wheel +pip install $install_options pip +pip install $install_options $insecure_flags $packages -- cgit v1.2.3 From e3dfc2b6627f9db6a0ee50ee7d75d433a3816c3f Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 3 Aug 2015 23:24:49 -0400 Subject: [tests] ignore trial temp folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 25876cf..0e26c09 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ dist/ build/ MANIFEST +_trial_temp -- cgit v1.2.3 From 0a8f455965fff778d993912f1dc11a77db264a93 Mon Sep 17 00:00:00 2001 From: Bruno Wagner Date: Tue, 4 Aug 2015 18:44:21 -0300 Subject: [bug] HTTP timeout was not being cleared on abort In case the http client loses connection, it has to clear it's timeout or the reactor will be left in a dirty state Fixing this solves a problem with some of the tests in Soledad that were trying to run on a dirty reactor --- src/leap/common/http.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/leap/common/http.py b/src/leap/common/http.py index 1e7ded7..cda8ee8 100644 --- a/src/leap/common/http.py +++ b/src/leap/common/http.py @@ -302,23 +302,16 @@ class _HTTP11ClientProtocol(HTTP11ClientProtocol): """ Cancel the request timeout, when it's finished. """ - if self._timeoutCall.active(): + if self._timeoutCall and self._timeoutCall.active(): self._timeoutCall.cancel() self._timeoutCall = None - def _finishResponse_WAITING(self, rest): - """ - Cancel the timeout when finished receiving the response. - """ - self._cancelTimeout() - HTTP11ClientProtocol._finishResponse_WAITING(self, rest) - - def _finishResponse_TRANSMITTING(self, rest): + def _finishResponse(self, rest): """ Cancel the timeout when finished receiving the response. """ self._cancelTimeout() - HTTP11ClientProtocol._finishResponse_TRANSMITTING(self, rest) + HTTP11ClientProtocol._finishResponse(self, rest) def dataReceived(self, bytes): """ @@ -330,3 +323,7 @@ class _HTTP11ClientProtocol(HTTP11ClientProtocol): HTTP11ClientProtocol.dataReceived(self, bytes) if self._timeoutCall and self._timeoutCall.active(): self._timeoutCall.reset(self._timeout) + + def connectionLost(self, reason): + self._cancelTimeout() + return HTTP11ClientProtocol.connectionLost(self, reason) -- cgit v1.2.3 From f1639503a58c76b00afc0a22b7928661dd08c771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Tue, 4 Aug 2015 14:59:36 +0200 Subject: [feat] WHEELHOUSE can be a url + --use-leap-wheels --use-leap-wheels sets --trusted-host (remove it when we have a proper cert) and WHEELHOUSE to https://ftp.lizard.leap.se Until we get ftp.lizard cname, use lizard as the wheels server. --- pkg/pip_install_requirements.sh | 53 +++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/pkg/pip_install_requirements.sh b/pkg/pip_install_requirements.sh index 949f738..6d8ed28 100755 --- a/pkg/pip_install_requirements.sh +++ b/pkg/pip_install_requirements.sh @@ -1,38 +1,31 @@ -#!/bin/sh +#!/bin/bash # Update pip and install LEAP base/testing requirements. # For convenience, $insecure_packages are allowed with insecure flags enabled. # Use at your own risk. # See $usage for help insecure_packages="dirspec" - -return_wheelhouse() { - if [ "$WHEELHOUSE" = "" ]; then - WHEELHOUSE=$HOME/wheelhouse - fi - - if [ ! -d "$WHEELHOUSE" ]; then - mkdir $WHEELHOUSE - fi - - echo "$WHEELHOUSE" -} +leap_wheelhouse=https://lizard.leap.se/wheels show_help() { - usage="Usage: $0 [--testing]\n --testing\tInstall dependencies from requirements-testing.pip\n -\t\tOtherwise, it will install requirements.pip" - echo $usage + usage="Usage: $0 [--testing] [--use-leap-wheels]\n --testing\t\tInstall dependencies from requirements-testing.pip\n +\t\t\tOtherwise, it will install requirements.pip\n +--use-leap-wheels\tUse wheels from leap.se" + echo -e $usage exit 1 } process_arguments() { testing=false + use_leap_wheels=false + while [ "$#" -gt 0 ]; do # From http://stackoverflow.com/a/31443098 case "$1" in --help) show_help;; --testing) testing=true; shift 1;; + --use-leap-wheels) use_leap_wheels=true; shift 1;; -h) show_help;; -*) echo "unknown option: $1" >&2; exit 1;; @@ -40,6 +33,31 @@ process_arguments() { done } +return_wheelhouse() { + if $use_leap_wheels ; then + WHEELHOUSE=$leap_wheelhouse + elif [ "$WHEELHOUSE" = "" ]; then + WHEELHOUSE=$HOME/wheelhouse + fi + + # Tested with bash and zsh + if [[ $WHEELHOUSE != http* && ! -d "$WHEELHOUSE" ]]; then + mkdir $WHEELHOUSE + fi + + echo "$WHEELHOUSE" +} + +return_install_options() { + wheelhouse=`return_wheelhouse` + install_options="-U --find-links=$wheelhouse" + if $use_leap_wheels ; then + install_options="$install_options --trusted-host lizard.leap.se" + fi + + echo $install_options +} + return_insecure_flags() { for insecure_package in $insecure_packages; do flags="$flags --allow-external $insecure_package --allow-unverified $insecure_package" @@ -59,8 +77,7 @@ return_packages() { } process_arguments $@ -wheelhouse=`return_wheelhouse` -install_options="-U --find-links=$wheelhouse" +install_options=`return_install_options` insecure_flags=`return_insecure_flags` packages=`return_packages` -- cgit v1.2.3 From 8965be42773e3e6c7e154f2c1a27f4e34031ae91 Mon Sep 17 00:00:00 2001 From: drebs Date: Tue, 11 Aug 2015 11:44:42 -0300 Subject: [feature] allow passing callback to http client --- .../feature_allow-passing-callback-to-http-client | 1 + src/leap/common/http.py | 26 ++++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 changes/feature_allow-passing-callback-to-http-client diff --git a/changes/feature_allow-passing-callback-to-http-client b/changes/feature_allow-passing-callback-to-http-client new file mode 100644 index 0000000..e7d2968 --- /dev/null +++ b/changes/feature_allow-passing-callback-to-http-client @@ -0,0 +1 @@ +- Allow passing callback to HTTP client. diff --git a/src/leap/common/http.py b/src/leap/common/http.py index cda8ee8..6fc10b4 100644 --- a/src/leap/common/http.py +++ b/src/leap/common/http.py @@ -31,6 +31,7 @@ except ImportError: from leap.common.certs import get_compatible_ssl_context_factory +from leap.common.check import leap_assert from zope.interface import implements @@ -150,7 +151,7 @@ class HTTPClient(object): pool.maxPersistentPerHost = maxPersistentPerHost return pool - def _request(self, url, method, body, headers): + def _request(self, url, method, body, headers, callback): """ Perform an HTTP request. @@ -162,6 +163,9 @@ class HTTPClient(object): :type body: str :param headers: The headers of the request. :type headers: dict + :param callback: A callback to be added to the request's deferred + callback chain. + :type callback: callable :return: A deferred that fires with the body of the request. :rtype: twisted.internet.defer.Deferred @@ -170,14 +174,21 @@ class HTTPClient(object): body = _StringBodyProducer(body) d = self._agent.request( method, url, headers=Headers(headers), bodyProducer=body) - d.addCallback(readBody) + d.addCallback(callback) return d - def request(self, url, method='GET', body=None, headers={}): + def request(self, url, method='GET', body=None, headers={}, + callback=readBody): """ Perform an HTTP request, but limit the maximum amount of concurrent connections. + May be passed a callback to be added to the request's deferred + callback chain. The callback is expected to receive the response of + the request and may do whatever it wants with the response. By + default, if no callback is passed, we will use a simple body reader + which returns a deferred that is fired with the body of the response. + :param url: The URL for the request. :type url: str :param method: The HTTP method of the request. @@ -186,11 +197,18 @@ class HTTPClient(object): :type body: str :param headers: The headers of the request. :type headers: dict + :param callback: A callback to be added to the request's deferred + callback chain. + :type callback: callable :return: A deferred that fires with the body of the request. :rtype: twisted.internet.defer.Deferred """ - return self._semaphore.run(self._request, url, method, body, headers) + leap_assert( + callable(callback), + message="The callback parameter should be a callable!") + return self._semaphore.run(self._request, url, method, body, headers, + callback) def close(self): """ -- cgit v1.2.3 From 97096fa2c22e96ec238c38ad3befdd052b5028cf Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 17 Aug 2015 19:15:05 -0400 Subject: [style] pep8 fix --- src/leap/common/http.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/leap/common/http.py b/src/leap/common/http.py index 6fc10b4..0dee3a2 100644 --- a/src/leap/common/http.py +++ b/src/leap/common/http.py @@ -20,6 +20,7 @@ Twisted HTTP/HTTPS client. try: import twisted + assert twisted except ImportError: print "*******" print "Twisted is needed to use leap.common.http module" @@ -178,7 +179,7 @@ class HTTPClient(object): return d def request(self, url, method='GET', body=None, headers={}, - callback=readBody): + callback=readBody): """ Perform an HTTP request, but limit the maximum amount of concurrent connections. @@ -208,7 +209,7 @@ class HTTPClient(object): callable(callback), message="The callback parameter should be a callable!") return self._semaphore.run(self._request, url, method, body, headers, - callback) + callback) def close(self): """ -- cgit v1.2.3 From 580c2a8990ceadcd49fdffdccaaa2a73b95807d1 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 17 Aug 2015 19:24:41 -0400 Subject: [tests] add pep8/flake8 ignores --- setup.cfg | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/setup.cfg b/setup.cfg index c71bffa..51070c6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,10 @@ [aliases] test = trial + +[pep8] +exclude = versioneer.py,_version.py,*.egg,build,docs +ignore = E731 + +[flake8] +exclude = versioneer.py,_version.py,*.egg,build,docs +ignore = E731 -- cgit v1.2.3 From ccecd1b3750bd10404511c33be1aaca82631a502 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 26 Aug 2015 16:28:30 -0300 Subject: [pkg] fold in changes --- CHANGELOG | 9 +++++++++ changes/bug_7234_add-http-request-timeout | 1 - changes/feature_7259_disable_events | 1 - changes/feature_allow-passing-callback-to-http-client | 1 - 4 files changed, 9 insertions(+), 3 deletions(-) delete mode 100644 changes/bug_7234_add-http-request-timeout delete mode 100644 changes/feature_7259_disable_events delete mode 100644 changes/feature_allow-passing-callback-to-http-client diff --git a/CHANGELOG b/CHANGELOG index 1c6a871..c34dc2f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,12 @@ +0.4.2 Aug 26, 2015: + o Add http request timeout. Related to #7234. + o Add a flag to disable events framework. Closes: #7259 + o Allow passing callback to HTTP client. + o Bugfix: do not add a port string to non-tcp addresses. + o Add close method for http agent. + o Fix code style and tests. + o Bugfix: HTTP timeout was not being cleared on abort. + 0.4.1 Jul 10, 2015: o Fix regexp to allow ipc protocol in zmq sockets. Closes: #7089. o Remove extraneous data from events logs. Closes #7130. diff --git a/changes/bug_7234_add-http-request-timeout b/changes/bug_7234_add-http-request-timeout deleted file mode 100644 index d18b28b..0000000 --- a/changes/bug_7234_add-http-request-timeout +++ /dev/null @@ -1 +0,0 @@ - o Add http request timeout. Related to #7234. diff --git a/changes/feature_7259_disable_events b/changes/feature_7259_disable_events deleted file mode 100644 index d08d3c1..0000000 --- a/changes/feature_7259_disable_events +++ /dev/null @@ -1 +0,0 @@ -- Add a flag to disable events framework. Closes: #7259 diff --git a/changes/feature_allow-passing-callback-to-http-client b/changes/feature_allow-passing-callback-to-http-client deleted file mode 100644 index e7d2968..0000000 --- a/changes/feature_allow-passing-callback-to-http-client +++ /dev/null @@ -1 +0,0 @@ -- Allow passing callback to HTTP client. -- cgit v1.2.3 From 0bcd8ae08d9b0d42e8cc57e34d301c192a582d60 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Fri, 28 Aug 2015 11:00:26 -0400 Subject: freeze debian version --- src/leap/common/_version.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/leap/common/_version.py b/src/leap/common/_version.py index 3500492..c46fd83 100644 --- a/src/leap/common/_version.py +++ b/src/leap/common/_version.py @@ -1,3 +1,13 @@ -version_version = '0.3.9' -version_full = '3de1eeba83d50793283d65ba4566dd1611ee9d4b' +# This file was generated by the `freeze_debianver` command in setup.py +# Using 'versioneer.py' (0.7+) from +# revision-control system data, or from the parent directory name of an +# unpacked source archive. Distribution tarballs contain a pre-generated copy +# of this file. + +version_version = '0.4.2' +version_full = '8fa97c02b5f07f896e52d9bb272128f267af04ea' + + +def get_versions(default={}, verbose=False): + return {'version': version_version, 'full': version_full} -- cgit v1.2.3 From c5af52e28e64c170c281942ca09049b57e4cdb14 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Fri, 28 Aug 2015 11:01:26 -0400 Subject: [deb] bump changelog --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 31e31e2..fd5c09a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,6 @@ -leap-common (0.3.9) unstable; urgency=medium +leap-common (0.4.2) unstable; urgency=medium - * Update to 0.3.9 release + * Update to 0.4.2 release -- Ben Carrillo Fri, 07 Nov 2014 18:50:11 +0100 -- cgit v1.2.3 From 33baddb32452a60fe5b8efc35929741e11f53b35 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Fri, 28 Aug 2015 11:16:08 -0400 Subject: update dependencies - remove protobuf - add zmq/txzmq --- debian/control | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/debian/control b/debian/control index ce369f3..0ace9f9 100644 --- a/debian/control +++ b/debian/control @@ -3,13 +3,12 @@ Maintainer: Micah Anderson Uploaders: Ben Carrillo Section: python Priority: optional -Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 9), python-protobuf, python-protobuf.socketrpc -Standards-Version: 3.9.5 +Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 9), dh-python +Standards-Version: 3.9.6 Package: python-leap-common Architecture: all -Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-dirspec, python-protobuf (>=2.4.1), python-protobuf.socketrpc, - python-dateutil, python-openssl, python-gnupg +Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-dirspec, python-dateutil, python-openssl, python-gnupg, python-zmq, python-txzmq Description: Common python files needed by LEAP projects. This package contains common python functions that are needed for the LEAP Client project -- cgit v1.2.3 From 56724056f95e3d908c3fd11840cf62e64afb9e0f Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Fri, 28 Aug 2015 11:26:53 -0400 Subject: remove python-gnupg dependency this was moved to keymanager time ago --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 0ace9f9..a5795bd 100644 --- a/debian/control +++ b/debian/control @@ -8,7 +8,7 @@ Standards-Version: 3.9.6 Package: python-leap-common Architecture: all -Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-dirspec, python-dateutil, python-openssl, python-gnupg, python-zmq, python-txzmq +Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-dirspec, python-dateutil, python-openssl, python-zmq, python-txzmq Description: Common python files needed by LEAP projects. This package contains common python functions that are needed for the LEAP Client project -- cgit v1.2.3 From 934aedc932f3d5cbfeacb4d6c2a2faae1e54c390 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Fri, 28 Aug 2015 12:10:41 -0400 Subject: [style] pep8 fixes --- setup.cfg | 4 ++-- setup.py | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/setup.cfg b/setup.cfg index 51070c6..4a2ab2b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,9 +2,9 @@ test = trial [pep8] -exclude = versioneer.py,_version.py,*.egg,build,docs +exclude = versioneer.py,_version.py,*.egg,build,dist,docs ignore = E731 [flake8] -exclude = versioneer.py,_version.py,*.egg,build,docs +exclude = versioneer.py,_version.py,*.egg,build,dist,docs ignore = E731 diff --git a/setup.py b/setup.py index ca83017..ae0ec63 100644 --- a/setup.py +++ b/setup.py @@ -19,9 +19,9 @@ setup file for leap.common """ import re from setuptools import setup, find_packages +from setuptools import Command from pkg import utils -parsed_reqs = utils.parse_requirements() import versioneer versioneer.versionfile_source = 'src/leap/common/_version.py' @@ -29,6 +29,8 @@ versioneer.versionfile_build = 'leap/common/_version.py' versioneer.tag_prefix = '' # tags are like 1.2.0 versioneer.parentdir_prefix = 'leap.common-' +parsed_reqs = utils.parse_requirements() + tests_requirements = [ 'mock', ] @@ -61,7 +63,6 @@ if len(_version_short) > 0: DOWNLOAD_URL = DOWNLOAD_BASE % VERSION_SHORT cmdclass = versioneer.get_cmdclass() -from setuptools import Command class freeze_debianver(Command): @@ -130,11 +131,11 @@ setup( package_data={'': ['*.pem']}, # For now, we do not exclude tests because of the circular dependency # between leap.common and leap.soledad. - #packages=find_packages('src', exclude=['leap.common.tests']), + # packages=find_packages('src', exclude=['leap.common.tests']), packages=find_packages('src'), test_suite='leap.common.tests', install_requires=parsed_reqs, - #dependency_links=dependency_links, + # dependency_links=dependency_links, tests_require=tests_requirements, include_package_data=True, zip_safe=False, @@ -143,6 +144,6 @@ setup( # needed for leap.common.http # service_identity needed for propper hostname identification, # see http://twistedmatrix.com/documents/current/core/howto/ssl.html - 'Twisted': ["Twisted>=14.0.2", "service_identity", "zope.interface"] + 'Twisted': ["Twisted>=14.0.2", "service_identity", "zope.interface"] }, ) -- cgit v1.2.3 From f4cb2c1e639f9f18b91b6d498f484edd444f9cc2 Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Tue, 1 Sep 2015 14:33:45 -0400 Subject: minor lintian fixes for new package --- debian/changelog | 2 +- debian/control | 4 ++-- debian/copyright | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/debian/changelog b/debian/changelog index db44bd3..7d9f5b7 100644 --- a/debian/changelog +++ b/debian/changelog @@ -4,7 +4,7 @@ leap-common (0.4.2) unstable; urgency=medium * Update standards version to 3.9.6 (no changes) * Change my email address - -- Ben Carrillo Fri, 07 Nov 2014 18:50:11 +0100 + -- Micah Anderson Tue, 01 Sep 2015 14:18:31 -0400 leap-common (0.3.8) unstable; urgency=medium diff --git a/debian/control b/debian/control index d7097a3..e35b604 100644 --- a/debian/control +++ b/debian/control @@ -9,6 +9,6 @@ Standards-Version: 3.9.6 Package: python-leap-common Architecture: all Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-dirspec, python-dateutil, python-openssl, python-zmq, python-txzmq -Description: Common python files needed by LEAP projects. - This package contains common python functions that are needed +Description: Common Python files needed by LEAP projects + This package contains common Python functions that are needed for the LEAP Client project diff --git a/debian/copyright b/debian/copyright index 353207a..ed928e5 100644 --- a/debian/copyright +++ b/debian/copyright @@ -13,4 +13,4 @@ License: GPL-3+ License: GPL-3+ On Debian systems, the complete text of the GNU General - Public License can be found in `/usr/share/common-licenses/GPL'. + Public License can be found in `/usr/share/common-licenses/GPL-3'. -- cgit v1.2.3 From 4772787239cda262e5194d370b697e7a19e2e1b7 Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Thu, 3 Sep 2015 16:00:18 -0400 Subject: add pydist-overrides for pyopenssl --- debian/changelog | 1 + debian/pydist-overrides | 1 + 2 files changed, 2 insertions(+) create mode 100644 debian/pydist-overrides diff --git a/debian/changelog b/debian/changelog index 7d9f5b7..0aba906 100644 --- a/debian/changelog +++ b/debian/changelog @@ -3,6 +3,7 @@ leap-common (0.4.2) unstable; urgency=medium * Update to 0.4.2 release * Update standards version to 3.9.6 (no changes) * Change my email address + * Add pydist-overrides for pyopenssl -- Micah Anderson Tue, 01 Sep 2015 14:18:31 -0400 diff --git a/debian/pydist-overrides b/debian/pydist-overrides new file mode 100644 index 0000000..b1ee55e --- /dev/null +++ b/debian/pydist-overrides @@ -0,0 +1 @@ +pyopenssl python-openssl -- cgit v1.2.3 From 0f1e829667031668211f8152cdff6d8b3ae292e8 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 9 Sep 2015 14:58:47 -0400 Subject: [docs] fix broken pypi badge --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 9fcf788..27a3952 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,8 @@ leap.common =========== -.. image:: https://pypip.in/v/leap.common/badge.png - :target: https://crate.io/packages/leap.common +.. image:: https://badge.fury.io/py/leap.common.svg + :target: http://badge.fury.io/py/leap.common A collection of shared utils used by the several python LEAP subprojects. -- cgit v1.2.3 From 0748be7aa4a7031adf55cccf3d106f0ca8e64f07 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 9 Sep 2015 15:43:26 -0400 Subject: [docs] add downloads badge --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index 27a3952..0aed339 100644 --- a/README.rst +++ b/README.rst @@ -3,6 +3,8 @@ leap.common .. image:: https://badge.fury.io/py/leap.common.svg :target: http://badge.fury.io/py/leap.common +.. image:: https://img.shields.io/pypi/dm/leap.common.svg + :target: http://badge.fury.io/py/leap.common A collection of shared utils used by the several python LEAP subprojects. -- cgit v1.2.3 From 0176fddcc10c6e8c3be4404ead2511a36eb86a90 Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Thu, 10 Sep 2015 15:07:19 -0400 Subject: Specify explicit dependencies for python-zmq and python-txzmq --- debian/changelog | 1 + debian/control | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 0aba906..bf8721f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -4,6 +4,7 @@ leap-common (0.4.2) unstable; urgency=medium * Update standards version to 3.9.6 (no changes) * Change my email address * Add pydist-overrides for pyopenssl + * Specify explicit dependencies for python-zmq and python-txzmq -- Micah Anderson Tue, 01 Sep 2015 14:18:31 -0400 diff --git a/debian/control b/debian/control index e35b604..45ed005 100644 --- a/debian/control +++ b/debian/control @@ -8,7 +8,7 @@ Standards-Version: 3.9.6 Package: python-leap-common Architecture: all -Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-dirspec, python-dateutil, python-openssl, python-zmq, python-txzmq +Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-dirspec, python-dateutil, python-openssl, python-zmq (>=14.4.1-1.1), python-txzmq (>= 0.7.3) Description: Common Python files needed by LEAP projects This package contains common Python functions that are needed for the LEAP Client project -- cgit v1.2.3 From 9d0900de85d07ec5b7926708c51136810b337158 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 14 Sep 2015 23:06:29 -0400 Subject: [feat] expose async events in api to make simpler the import of the tx client - Related: #7274 --- changes/feature_async-events-api | 1 + src/leap/common/events/__init__.py | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 changes/feature_async-events-api diff --git a/changes/feature_async-events-api b/changes/feature_async-events-api new file mode 100644 index 0000000..d4979bd --- /dev/null +++ b/changes/feature_async-events-api @@ -0,0 +1 @@ +- Expose async methods for events. Closes: #7274 diff --git a/src/leap/common/events/__init__.py b/src/leap/common/events/__init__.py index 87ed8ae..18d0ac8 100644 --- a/src/leap/common/events/__init__.py +++ b/src/leap/common/events/__init__.py @@ -39,6 +39,7 @@ import logging import argparse from leap.common.events import client +from leap.common.events import txclient from leap.common.events import server from leap.common.events.flags import set_events_enabled @@ -80,6 +81,10 @@ def register(event, callback, uid=None, replace=False): return client.register(event, callback, uid, replace) +def register_async(event, callback, uid=None, replace=False): + return txclient.register(event, callback, uid, replace) + + def unregister(event, uid=None): """ Unregister callbacks for an event. @@ -95,6 +100,10 @@ def unregister(event, uid=None): return client.unregister(event, uid) +def unregister_async(event, uid=None): + return txclient.unregister(event, uid) + + def emit(event, *content): """ Send an event. @@ -107,6 +116,10 @@ def emit(event, *content): return client.emit(event, *content) +def emit_async(event, *content): + return txclient.emit(event, *content) + + if __name__ == "__main__": def _echo(event, *content): -- cgit v1.2.3 From d4e4d3dc8ff88d98477e1fcd1104d7cd8b9e6612 Mon Sep 17 00:00:00 2001 From: Folker Bernitt Date: Wed, 16 Sep 2015 11:21:01 +0200 Subject: [tests] Make txclient honor flags.EVENTS_ENABLED - Breaks tests in dependent repos like soledad otherwise, because server.key could not be found --- src/leap/common/events/txclient.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/leap/common/events/txclient.py b/src/leap/common/events/txclient.py index dfd0533..be4b3ef 100644 --- a/src/leap/common/events/txclient.py +++ b/src/leap/common/events/txclient.py @@ -35,7 +35,7 @@ from leap.common.events.client import EventsClient from leap.common.events.client import configure_client from leap.common.events.server import EMIT_ADDR from leap.common.events.server import REG_ADDR -from leap.common.events import catalog +from leap.common.events import catalog, flags logger = logging.getLogger(__name__) @@ -146,8 +146,9 @@ def register(event, callback, uid=None, replace=False): :raises CallbackAlreadyRegisteredError: when there's already a callback identified by the given uid and replace is False. """ - return EventsTxClient.instance().register( - event, callback, uid=uid, replace=replace) + if flags.EVENTS_ENABLED: + return EventsTxClient.instance().register( + event, callback, uid=uid, replace=replace) def unregister(event, uid=None): @@ -162,7 +163,8 @@ def unregister(event, uid=None): :param uid: The callback uid. :type uid: str """ - return EventsTxClient.instance().unregister(event, uid=uid) + if flags.EVENTS_ENABLED: + return EventsTxClient.instance().unregister(event, uid=uid) def emit(event, *content): @@ -174,7 +176,8 @@ def emit(event, *content): :param content: The content of the event. :type content: list """ - return EventsTxClient.instance().emit(event, *content) + if flags.EVENTS_ENABLED: + return EventsTxClient.instance().emit(event, *content) def shutdown(): -- cgit v1.2.3 From 967763e8d0cc5f70f0760994d87500e30ad724a7 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 16 Sep 2015 12:20:22 -0400 Subject: [refactor] move checking flag to main module --- src/leap/common/events/__init__.py | 19 +++++++++++++------ src/leap/common/events/client.py | 13 +++++-------- src/leap/common/events/txclient.py | 11 ++++------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/leap/common/events/__init__.py b/src/leap/common/events/__init__.py index 18d0ac8..f9ad5fa 100644 --- a/src/leap/common/events/__init__.py +++ b/src/leap/common/events/__init__.py @@ -41,6 +41,7 @@ import argparse from leap.common.events import client from leap.common.events import txclient from leap.common.events import server +from leap.common.events import flags from leap.common.events.flags import set_events_enabled from leap.common.events import catalog @@ -78,11 +79,13 @@ def register(event, callback, uid=None, replace=False): :raises CallbackAlreadyRegistered: when there's already a callback identified by the given uid and replace is False. """ - return client.register(event, callback, uid, replace) + if flags.EVENTS_ENABLED: + return client.register(event, callback, uid, replace) def register_async(event, callback, uid=None, replace=False): - return txclient.register(event, callback, uid, replace) + if flags.EVENTS_ENABLED: + return txclient.register(event, callback, uid, replace) def unregister(event, uid=None): @@ -97,11 +100,13 @@ def unregister(event, uid=None): :param uid: The callback uid. :type uid: str """ - return client.unregister(event, uid) + if flags.EVENTS_ENABLED: + return client.unregister(event, uid) def unregister_async(event, uid=None): - return txclient.unregister(event, uid) + if flags.EVENTS_ENABLED: + return txclient.unregister(event, uid) def emit(event, *content): @@ -113,11 +118,13 @@ def emit(event, *content): :param content: The content of the event. :type content: list """ - return client.emit(event, *content) + if flags.EVENTS_ENABLED: + return client.emit(event, *content) def emit_async(event, *content): - return txclient.emit(event, *content) + if flags.EVENTS_ENABLED: + return txclient.emit(event, *content) if __name__ == "__main__": diff --git a/src/leap/common/events/client.py b/src/leap/common/events/client.py index e085f5b..e38e9d3 100644 --- a/src/leap/common/events/client.py +++ b/src/leap/common/events/client.py @@ -465,7 +465,7 @@ class EventsClientThread(threading.Thread, EventsClient): Make sure the events client thread is started. """ with self._lock: - if flags.EVENTS_ENABLED and not self.is_alive(): + if not self.is_alive(): self.daemon = True self.start() self._initialized.wait() @@ -508,9 +508,8 @@ def register(event, callback, uid=None, replace=False): :raises CallbackAlreadyRegisteredError: when there's already a callback identified by the given uid and replace is False. """ - if flags.EVENTS_ENABLED: - return EventsClientThread.instance().register( - event, callback, uid=uid, replace=replace) + return EventsClientThread.instance().register( + event, callback, uid=uid, replace=replace) def unregister(event, uid=None): @@ -525,8 +524,7 @@ def unregister(event, uid=None): :param uid: The callback uid. :type uid: str """ - if flags.EVENTS_ENABLED: - return EventsClientThread.instance().unregister(event, uid=uid) + return EventsClientThread.instance().unregister(event, uid=uid) def emit(event, *content): @@ -538,8 +536,7 @@ def emit(event, *content): :param content: The content of the event. :type content: list """ - if flags.EVENTS_ENABLED: - return EventsClientThread.instance().emit(event, *content) + return EventsClientThread.instance().emit(event, *content) def instance(): diff --git a/src/leap/common/events/txclient.py b/src/leap/common/events/txclient.py index be4b3ef..f3c183e 100644 --- a/src/leap/common/events/txclient.py +++ b/src/leap/common/events/txclient.py @@ -146,9 +146,8 @@ def register(event, callback, uid=None, replace=False): :raises CallbackAlreadyRegisteredError: when there's already a callback identified by the given uid and replace is False. """ - if flags.EVENTS_ENABLED: - return EventsTxClient.instance().register( - event, callback, uid=uid, replace=replace) + return EventsTxClient.instance().register( + event, callback, uid=uid, replace=replace) def unregister(event, uid=None): @@ -163,8 +162,7 @@ def unregister(event, uid=None): :param uid: The callback uid. :type uid: str """ - if flags.EVENTS_ENABLED: - return EventsTxClient.instance().unregister(event, uid=uid) + return EventsTxClient.instance().unregister(event, uid=uid) def emit(event, *content): @@ -176,8 +174,7 @@ def emit(event, *content): :param content: The content of the event. :type content: list """ - if flags.EVENTS_ENABLED: - return EventsTxClient.instance().emit(event, *content) + return EventsTxClient.instance().emit(event, *content) def shutdown(): -- cgit v1.2.3 From d3519a062ea6924fa6b6b5cb4d804b5a39eb249f Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 22 Sep 2015 13:30:24 -0300 Subject: [pkg] fold in changes --- CHANGELOG | 3 +++ changes/feature_async-events-api | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) delete mode 100644 changes/feature_async-events-api diff --git a/CHANGELOG b/CHANGELOG index c34dc2f..9356ade 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +0.4.3 Sep 22, 2015: + o Expose async methods for events. Closes: #7274 + 0.4.2 Aug 26, 2015: o Add http request timeout. Related to #7234. o Add a flag to disable events framework. Closes: #7259 diff --git a/changes/feature_async-events-api b/changes/feature_async-events-api deleted file mode 100644 index d4979bd..0000000 --- a/changes/feature_async-events-api +++ /dev/null @@ -1 +0,0 @@ -- Expose async methods for events. Closes: #7274 -- cgit v1.2.3 From c84a7358a922b090a25789e76fcbc9a37b344117 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 24 Sep 2015 15:19:20 -0400 Subject: [pkg] update debian changelog to 0.4.3 --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index fd5c09a..d5243da 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +leap-common (0.4.3) unstable; urgency=medium + + * Update to 0.4.3 release + + -- Ben Carrillo Thu, 24 Sep 2015 15:18:51 -0400 + leap-common (0.4.2) unstable; urgency=medium * Update to 0.4.2 release -- cgit v1.2.3 From 5b62dbbc0de18342c7b9ab0236e888b01be0ff41 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 24 Sep 2015 15:19:25 -0400 Subject: freeze debian version --- src/leap/common/_version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/leap/common/_version.py b/src/leap/common/_version.py index c46fd83..5a1d563 100644 --- a/src/leap/common/_version.py +++ b/src/leap/common/_version.py @@ -5,8 +5,8 @@ # unpacked source archive. Distribution tarballs contain a pre-generated copy # of this file. -version_version = '0.4.2' -version_full = '8fa97c02b5f07f896e52d9bb272128f267af04ea' +version_version = '0.4.3' +version_full = 'c84a7358a922b090a25789e76fcbc9a37b344117' def get_versions(default={}, verbose=False): -- cgit v1.2.3 From 3ab6b2aff4a38a4fabd4320880ef795f05ccd17f Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 6 Oct 2015 15:38:43 -0400 Subject: [pkg] add python-twisted-web and python-service-identity as Depends. this is listed as extras, but it's needed for soledad and the client. --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 45ed005..8c8cfb8 100644 --- a/debian/control +++ b/debian/control @@ -8,7 +8,7 @@ Standards-Version: 3.9.6 Package: python-leap-common Architecture: all -Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-dirspec, python-dateutil, python-openssl, python-zmq (>=14.4.1-1.1), python-txzmq (>= 0.7.3) +Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-dirspec, python-dateutil, python-openssl, python-zmq (>=14.4.1-1.1), python-txzmq (>= 0.7.3), python-twisted-web (>= 14.0.2), python-service-identity Description: Common Python files needed by LEAP projects This package contains common Python functions that are needed for the LEAP Client project -- cgit v1.2.3 From 459024de9e36aea0813aa01a570b68db7e9c1a26 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 6 Oct 2015 14:41:18 -0300 Subject: [bug] consider STANDALONE for the paths Since we need to write a file we have to consider whether we are running in 'standalone' mode or not to use the right path prefix. - Related: #7512 --- changes/bug-7512_use-right-config-path | 1 + src/leap/common/config/flags.py | 28 ++++++++++++++++++++++++++++ src/leap/common/events/client.py | 5 ++--- src/leap/common/events/txclient.py | 2 +- src/leap/common/events/zmq_components.py | 4 ++-- 5 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 changes/bug-7512_use-right-config-path create mode 100644 src/leap/common/config/flags.py diff --git a/changes/bug-7512_use-right-config-path b/changes/bug-7512_use-right-config-path new file mode 100644 index 0000000..b472431 --- /dev/null +++ b/changes/bug-7512_use-right-config-path @@ -0,0 +1 @@ +- Consider standalone flag when saving events certificates. Related #7512. diff --git a/src/leap/common/config/flags.py b/src/leap/common/config/flags.py new file mode 100644 index 0000000..6fd43f6 --- /dev/null +++ b/src/leap/common/config/flags.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# flags.py +# Copyright (C) 2015 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +This file is meant to be used to store global flags that affect the +application. + +WARNING: You should NOT use this kind of flags unless you're sure of what + you're doing, and someone else tells you that you're right. + Most of the times there is a better and safer alternative. +""" + +# The STANDALONE flag is used to: +# - use a relative or system wide path to find the configuration files. +STANDALONE = False diff --git a/src/leap/common/events/client.py b/src/leap/common/events/client.py index e38e9d3..60d24bc 100644 --- a/src/leap/common/events/client.py +++ b/src/leap/common/events/client.py @@ -47,7 +47,7 @@ try: except ImportError: pass -from leap.common.config import get_path_prefix +from leap.common.config import flags, get_path_prefix from leap.common.zmq_utils import zmq_has_curve from leap.common.zmq_utils import maybe_create_and_get_certificates from leap.common.zmq_utils import PUBLIC_KEYS_PREFIX @@ -55,7 +55,6 @@ from leap.common.zmq_utils import PUBLIC_KEYS_PREFIX from leap.common.events.errors import CallbackAlreadyRegisteredError from leap.common.events.server import EMIT_ADDR from leap.common.events.server import REG_ADDR -from leap.common.events import flags from leap.common.events import catalog @@ -280,7 +279,7 @@ class EventsClientThread(threading.Thread, EventsClient): self._lock = threading.Lock() self._initialized = threading.Event() self._config_prefix = os.path.join( - get_path_prefix(), "leap", "events") + get_path_prefix(flags.STANDALONE), "leap", "events") self._loop = None self._context = None self._push = None diff --git a/src/leap/common/events/txclient.py b/src/leap/common/events/txclient.py index f3c183e..dfd0533 100644 --- a/src/leap/common/events/txclient.py +++ b/src/leap/common/events/txclient.py @@ -35,7 +35,7 @@ from leap.common.events.client import EventsClient from leap.common.events.client import configure_client from leap.common.events.server import EMIT_ADDR from leap.common.events.server import REG_ADDR -from leap.common.events import catalog, flags +from leap.common.events import catalog logger = logging.getLogger(__name__) diff --git a/src/leap/common/events/zmq_components.py b/src/leap/common/events/zmq_components.py index f99c754..729ca90 100644 --- a/src/leap/common/events/zmq_components.py +++ b/src/leap/common/events/zmq_components.py @@ -36,7 +36,7 @@ try: except ImportError: pass -from leap.common.config import get_path_prefix +from leap.common.config import flags, get_path_prefix from leap.common.zmq_utils import zmq_has_curve from leap.common.zmq_utils import maybe_create_and_get_certificates from leap.common.zmq_utils import PUBLIC_KEYS_PREFIX @@ -64,7 +64,7 @@ class TxZmqComponent(object): self._factory = txzmq.ZmqFactory() self._factory.registerForShutdown() if path_prefix is None: - path_prefix = get_path_prefix() + path_prefix = get_path_prefix(flags.STANDALONE) self._config_prefix = os.path.join(path_prefix, "leap", "events") self._connections = [] -- cgit v1.2.3 From c1781d87a76f941c588709405cd5b2634dc6b1e8 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Fri, 9 Oct 2015 15:01:13 -0400 Subject: [bug] fix wrong ca_cert path inside bundle -Resolves: #7524 --- src/leap/common/ca_bundle.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/leap/common/ca_bundle.py b/src/leap/common/ca_bundle.py index 2c41d18..c0ce35f 100644 --- a/src/leap/common/ca_bundle.py +++ b/src/leap/common/ca_bundle.py @@ -21,8 +21,9 @@ If you are packaging Requests, e.g., for a Linux distribution or a managed environment, you can change the definition of where() to return a separately packaged CA bundle. """ -import platform import os.path +import platform +import sys _system = platform.system() @@ -34,12 +35,11 @@ def where(): Return the preferred certificate bundle. :rtype: str """ - # vendored bundle inside Requests, plus some additions of ours - if IS_MAC: - return os.path.join("/Applications", "Bitmask.app", - "Contents", "Resources", - "cacert.pem") - return os.path.join(os.path.dirname(__file__), 'cacert.pem') + if getattr(sys, 'frozen', False): + # we are running in a |PyInstaller| bundle + path = sys._MEIPASS + return os.path.join(path, 'cacert.pem') + return os.path.join(os.path, dirname(__file__), 'cacert.pem') if __name__ == '__main__': print(where()) -- cgit v1.2.3 From c105a8d3b016cc907a908de1a31b445b32b42ca9 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 13 Oct 2015 13:09:45 -0300 Subject: [bug] fix typo on dirname usage --- src/leap/common/ca_bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/leap/common/ca_bundle.py b/src/leap/common/ca_bundle.py index c0ce35f..e2a624d 100644 --- a/src/leap/common/ca_bundle.py +++ b/src/leap/common/ca_bundle.py @@ -39,7 +39,7 @@ def where(): # we are running in a |PyInstaller| bundle path = sys._MEIPASS return os.path.join(path, 'cacert.pem') - return os.path.join(os.path, dirname(__file__), 'cacert.pem') + return os.path.join(os.path.dirname(__file__), 'cacert.pem') if __name__ == '__main__': print(where()) -- cgit v1.2.3 From 5af395a5662c63a09cc6db2bacbb495090488d78 Mon Sep 17 00:00:00 2001 From: Folker Bernitt Date: Wed, 21 Oct 2015 10:07:06 +0200 Subject: Workaround for deadlock problem in zmq auth - See https://leap.se/code/issues/7536 - Actual root cause not identified yet --- src/leap/common/events/zmq_components.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/leap/common/events/zmq_components.py b/src/leap/common/events/zmq_components.py index 729ca90..51de02c 100644 --- a/src/leap/common/events/zmq_components.py +++ b/src/leap/common/events/zmq_components.py @@ -25,6 +25,7 @@ import os import logging import txzmq import re +import time from abc import ABCMeta @@ -154,6 +155,11 @@ class TxZmqComponent(object): :type socket: zmq.Socket """ authenticator = ThreadAuthenticator(self._factory.context) + + # Temporary fix until we understand what the problem is + # See https://leap.se/code/issues/7536 + time.sleep(0.5) + authenticator.start() # XXX do not hardcode this here. authenticator.allow('127.0.0.1') -- cgit v1.2.3 From 3fabc788c27b2c2619d8e23b3eebc018b95faf7a Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 27 Oct 2015 18:09:05 -0300 Subject: [pkg] fold in changes --- CHANGELOG | 5 +++++ changes/bug-7512_use-right-config-path | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) delete mode 100644 changes/bug-7512_use-right-config-path diff --git a/CHANGELOG b/CHANGELOG index 9356ade..1ce64b7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +0.4.4 Oct 28, 2015: + o Consider standalone flag when saving events certificates. Related #7512. + o fix wrong ca_cert path inside bundle. + o Workaround for deadlock problem in zmq auth. + 0.4.3 Sep 22, 2015: o Expose async methods for events. Closes: #7274 diff --git a/changes/bug-7512_use-right-config-path b/changes/bug-7512_use-right-config-path deleted file mode 100644 index b472431..0000000 --- a/changes/bug-7512_use-right-config-path +++ /dev/null @@ -1 +0,0 @@ -- Consider standalone flag when saving events certificates. Related #7512. -- cgit v1.2.3 From ef839547f9404dd8ba70bdaa2fec82dfd07cb383 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 29 Oct 2015 12:00:48 -0400 Subject: freeze debian version --- src/leap/common/_version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/leap/common/_version.py b/src/leap/common/_version.py index 5a1d563..f5738ea 100644 --- a/src/leap/common/_version.py +++ b/src/leap/common/_version.py @@ -5,8 +5,8 @@ # unpacked source archive. Distribution tarballs contain a pre-generated copy # of this file. -version_version = '0.4.3' -version_full = 'c84a7358a922b090a25789e76fcbc9a37b344117' +version_version = '0.4.4' +version_full = 'ee0e9cadccd00cb62032d8fc4b322bb6fe3dc7ed' def get_versions(default={}, verbose=False): -- cgit v1.2.3 From 3ba7e825a1aef1b10530c6c0e71ee2762419341c Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 29 Oct 2015 12:01:34 -0400 Subject: [pkg] bump changelog to 0.4.4 --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index fcee68a..2bdb71a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +leap-common (0.4.4) unstable; urgency=medium + + * Update to 0.4.4 release + + -- Ben Carrillo Thu, 29 Oct 2015 12:00:51 -0400 + leap-common (0.4.3) unstable; urgency=medium * Update to 0.4.3 release -- cgit v1.2.3 From af225cc59b147710de54857b3fd287b511c89f98 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 29 Oct 2015 16:35:09 -0400 Subject: [pkg] bump changelog to dummy release to fix former versioning error --- debian/changelog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/debian/changelog b/debian/changelog index 2bdb71a..bcbc0bb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +leap-common (0.4.11) unstable; urgency=medium + + * Dummy release. This is exactly tag 0.4.4, to fix a former versioning + error. Closes leap issue 7555. + + -- Ben Carrillo Thu, 29 Oct 2015 16:33:25 -0400 + leap-common (0.4.4) unstable; urgency=medium * Update to 0.4.4 release -- cgit v1.2.3 From 33d9cec7db01e1a50940fda9fe3903e67c02af1c Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 29 Oct 2015 16:35:44 -0400 Subject: [pkg] relax dependency on twisted version. Closes: #7556 we're going to use soledad-client on wheezy (for the soledad-sync test). soledad-client depends on leap-common, which depends on twisted, but we only have 13.x in wheezy backports. we do this assuming that the hard pinning on twisted-web is done elsewhere for the moment (ie, in bitmask). --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 8c8cfb8..b9033ad 100644 --- a/debian/control +++ b/debian/control @@ -8,7 +8,7 @@ Standards-Version: 3.9.6 Package: python-leap-common Architecture: all -Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-dirspec, python-dateutil, python-openssl, python-zmq (>=14.4.1-1.1), python-txzmq (>= 0.7.3), python-twisted-web (>= 14.0.2), python-service-identity +Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-dirspec, python-dateutil, python-openssl, python-zmq (>=14.4.1-1.1), python-txzmq (>= 0.7.3), python-twisted-web (>= 13.0.0), python-service-identity Description: Common Python files needed by LEAP projects This package contains common Python functions that are needed for the LEAP Client project -- cgit v1.2.3 From 456ebfdd4b7a790a968bf9e41f1b2cc8bc2358ce Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Sat, 31 Oct 2015 11:39:11 -0400 Subject: remove dh-python so we can build on wheezy --- debian/changelog | 1 + debian/control | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index bcbc0bb..0ee4bb4 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,7 @@ leap-common (0.4.11) unstable; urgency=medium * Dummy release. This is exactly tag 0.4.4, to fix a former versioning error. Closes leap issue 7555. + * Remove dh-python so we can build on wheezy -- Ben Carrillo Thu, 29 Oct 2015 16:33:25 -0400 diff --git a/debian/control b/debian/control index b9033ad..f4ec038 100644 --- a/debian/control +++ b/debian/control @@ -3,7 +3,7 @@ Maintainer: Micah Anderson Uploaders: Ben Carrillo Section: python Priority: optional -Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 9), dh-python +Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 9) Standards-Version: 3.9.6 Package: python-leap-common -- cgit v1.2.3 From 010598fd91722474d1d8a216a20fa429a2615c3a Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 2 Nov 2015 11:56:51 -0400 Subject: [pkg] move python-service-identity to Recommends - Resolves: #7559 --- debian/changelog | 7 +++++++ debian/control | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index bcbc0bb..c03039a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +leap-common (0.4.11.1) unstable; urgency=medium + + * Move python-service-identity to Recommends. + Closes leap issue #7559 + + -- Ben Carrillo Mon, 02 Nov 2015 11:55:28 -0400 + leap-common (0.4.11) unstable; urgency=medium * Dummy release. This is exactly tag 0.4.4, to fix a former versioning diff --git a/debian/control b/debian/control index b9033ad..8f9e640 100644 --- a/debian/control +++ b/debian/control @@ -8,7 +8,8 @@ Standards-Version: 3.9.6 Package: python-leap-common Architecture: all -Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-dirspec, python-dateutil, python-openssl, python-zmq (>=14.4.1-1.1), python-txzmq (>= 0.7.3), python-twisted-web (>= 13.0.0), python-service-identity +Depends: ${misc:Depends}, ${python:Depends}, python-jsonschema, python-dirspec, python-dateutil, python-openssl, python-zmq (>=14.4.1-1.1), python-txzmq (>= 0.7.3), python-twisted-web (>= 13.0.0) +Recommends: python-service-identity Description: Common Python files needed by LEAP projects This package contains common Python functions that are needed for the LEAP Client project -- cgit v1.2.3