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