Merge lp:~barry/ubuntu/natty/python-keyring/bug-686257 into lp:ubuntu/natty/python-keyring

Proposed by Barry Warsaw
Status: Merged
Merged at revision: 6
Proposed branch: lp:~barry/ubuntu/natty/python-keyring/bug-686257
Merge into: lp:ubuntu/natty/python-keyring
Diff against target: 1991 lines (+762/-609)
27 files modified
CHANGES.txt (+46/-0)
CONTRIBUTORS.txt (+11/-0)
PKG-INFO (+167/-118)
README.txt (+12/-9)
debian/changelog (+21/-0)
debian/control (+9/-33)
debian/copyright (+4/-2)
debian/python-keyring-gnome.install (+0/-1)
debian/python-keyring-kwallet.install (+0/-1)
debian/python-keyring.install (+0/-2)
debian/rules (+4/-10)
demo/keyring_demo.py (+17/-11)
extensions.py (+0/-83)
keyring/__init__.py (+1/-0)
keyring/backend.py (+258/-53)
keyring/backends/__init__.py (+1/-0)
keyring/backends/gnome_keyring.c (+0/-111)
keyring/backends/kde_kwallet.cpp (+0/-121)
keyring/backends/osx_keychain.c (+2/-2)
keyring/core.py (+20/-11)
keyring/getpassbackend.py (+13/-0)
keyring/tests/__init__.py (+1/-0)
keyring/tests/test_backend.py (+65/-25)
keyring/tests/test_core.py (+44/-13)
keyring/tests/test_util.py (+37/-0)
keyring/util/escape.py (+25/-0)
setup.py (+4/-3)
To merge this branch: bzr merge lp:~barry/ubuntu/natty/python-keyring/bug-686257
Reviewer Review Type Date Requested Status
Daniel Holbach (community) Approve
Ubuntu branches Pending
Review via email: mp+48967@code.launchpad.net

Description of the change

Update to latest upstream version.

To post a comment you must log in.
Revision history for this message
Daniel Holbach (dholbach) wrote :

I guess the tests don't need to be installed.

review: Needs Fixing
Revision history for this message
Barry Warsaw (barry) wrote :

Sorry, I don't quite understand the comment. Do you mean that the tests aren't being installed or shouldn't be installed? Because I think they are installed, and I kind of think they should be. :)

There was a debate recently on one of the Python mailing lists about this (I forget which one, but probably either distutils or the testing sig) and there were definitely opinions on both sides. I'm personally of the opinion that tests should be installed, for various reasons:

* They can serve as documentation to users
* They can be run by end users to verify a package works properly
* They rarely take up much additional space
* It's usually easier to include the tests than not when they are shipped upstream as a subpackage

Others disagree and it can be decided on a case-by-case basis, but in this case, I see no harm and some potential good, so I would continue to include the tests.

Revision history for this message
Daniel Holbach (dholbach) wrote :

Fine with me.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'CHANGES.txt'
2--- CHANGES.txt 1970-01-01 00:00:00 +0000
3+++ CHANGES.txt 2011-02-08 19:21:29 +0000
4@@ -0,0 +1,46 @@
5+-------
6+CHANGES
7+-------
8+
9+-----
10+0.5.1
11+-----
12+
13+* Remove a spurious KDE debug message when using KWallet
14+
15+* Fix a bug that caused an exception if the user canceled the KWallet dialog
16+ (https://bitbucket.org/kang/python-keyring-lib/issue/37/user-canceling-of-kde-wallet-dialogs).
17+
18+---
19+0.5
20+---
21+
22+* Now using the existing Gnome and KDE python libs instead of custom C++
23+ code.
24+
25+* Using the getpass module instead of custom code
26+
27+---
28+0.4
29+---
30+
31+* Fixed the setup script (some subdirs were not included in the release.)
32+
33+---
34+0.3
35+---
36+
37+* Fixed keyring.core when the user doesn't have a cfg, or is not
38+ properly configured.
39+
40+* Fixed escaping issues for usernames with non-ascii characters
41+
42+---
43+0.2
44+---
45+
46+* Add support for Python 2.4+
47+ http://bitbucket.org/kang/python-keyring-lib/issue/2
48+
49+* Fix the bug in KDE Kwallet extension compiling
50+ http://bitbucket.org/kang/python-keyring-lib/issue/3
51
52=== added file 'CONTRIBUTORS.txt'
53--- CONTRIBUTORS.txt 1970-01-01 00:00:00 +0000
54+++ CONTRIBUTORS.txt 2011-02-08 19:21:29 +0000
55@@ -0,0 +1,11 @@
56+------------
57+Contributors
58+------------
59+
60+* Kang Zhang
61+* Tarek Ziade
62+* Marcin Kasperski
63+* Steve Borho
64+* Michael Gruenewald
65+* Jason R. Coombs
66+* Benji York
67
68=== modified file 'PKG-INFO'
69--- PKG-INFO 2009-09-24 23:24:02 +0000
70+++ PKG-INFO 2011-02-08 19:21:29 +0000
71@@ -1,6 +1,6 @@
72 Metadata-Version: 1.0
73 Name: keyring
74-Version: 0.2
75+Version: 0.5.1
76 Summary: Store and access your passwords safely.
77 Home-page: http://home.python-keyring.org/
78 Author: Kang Zhang
79@@ -41,18 +41,18 @@
80
81 Run easy_install or pip::
82
83- $ easy_install keyring
84- $ pip install keyring
85+ $ easy_install keyring
86+ $ pip install keyring
87
88 Source installation
89 ===================
90
91 Download the source tarball, and uncompress it, then run the install command::
92
93- $ wget http://pypi.python.org/packages/source/k/keyring/keyring-0.2.tar.gz
94- $ tar -xzvf keyring-0.2.tar.gz
95- $ cd keyring-0.2
96- $ python setup.py install
97+ $ wget http://pypi.python.org/packages/source/k/keyring/keyring-0.3.tar.gz
98+ $ tar -xzvf keyring-0.3.tar.gz
99+ $ cd keyring-0.3
100+ $ python setup.py install
101
102
103 --------------------------
104@@ -96,9 +96,9 @@
105 Here's a sample config file(The full demo can be accessed in the ``demo/keyring.py``):
106 ::
107
108- [backend]
109- default-keyring=simplekeyring.SimpleKeyring
110- keyring-path=/home/kang/pyworkspace/python-keyring-lib/demo/
111+ [backend]
112+ default-keyring=simplekeyring.SimpleKeyring
113+ keyring-path=/home/kang/pyworkspace/python-keyring-lib/demo/
114
115
116 Write your own keyring backend
117@@ -112,44 +112,44 @@
118 The usage of the three functions:
119
120 * ``supported(self)`` : Return if this backend is supported in current
121- environment. The returned value can be **0**, **1** , or **-1**. **0** means
122- suitable; **1** means recommended and **-1** means this backend is not
123- available for current environment.
124+ environment. The returned value can be **0**, **1** , or **-1**. **0** means
125+ suitable; **1** means recommended and **-1** means this backend is not
126+ available for current environment.
127 * ``get_password(self, service, username)`` : Return the stored password for the
128- ``username`` of the ``service``.
129+ ``username`` of the ``service``.
130 * ``set_password(self, service, username, password)`` : Store the ``password``
131- for ``username`` of the ``service`` in the backend.
132+ for ``username`` of the ``service`` in the backend.
133
134 For an instance, there's the source code of the demo mentioned above. It's a
135 simple keyring which stores the password directly in memory.
136
137 ::
138
139- """
140- simplekeyring.py
141-
142- A simple keyring class for the keyring_demo.py
143-
144- Created by Kang Zhang on 2009-07-12
145- """
146- from keyring.backend import KeyringBackend
147-
148- class SimpleKeyring(KeyringBackend):
149- """Simple Keyring is a keyring which can store only one
150- password in memory.
151- """
152- def __init__(self):
153- self.password = ''
154-
155- def supported(self):
156- return 0
157-
158- def get_password(self, service, username):
159- return self.password
160-
161- def set_password(self, service, username, password):
162- self.password = password
163- return 0
164+ """
165+ simplekeyring.py
166+
167+ A simple keyring class for the keyring_demo.py
168+
169+ Created by Kang Zhang on 2009-07-12
170+ """
171+ from keyring.backend import KeyringBackend
172+
173+ class SimpleKeyring(KeyringBackend):
174+ """Simple Keyring is a keyring which can store only one
175+ password in memory.
176+ """
177+ def __init__(self):
178+ self.password = ''
179+
180+ def supported(self):
181+ return 0
182+
183+ def get_password(self, service, username):
184+ return self.password
185+
186+ def set_password(self, service, username, password):
187+ self.password = password
188+ return 0
189
190
191 Set the keyring in runtime
192@@ -163,24 +163,27 @@
193 ``set_keyring()``
194 ::
195
196- # define a new keyring class which extends the KeyringBackend
197- import keyring.backend
198- class TestKeyring(keyring.backend.KeyringBackend):
199- """A test keyring which always outputs same password
200- """
201- def supported(self): return 0
202- def set_password(self, servicename, username, password): return 0
203- def get_password(self, servicename, username):
204- return "password from TestKeyring"
205-
206- # set the keyring for keyring lib
207- import keyring
208- keyring.set_keyring(TestKeyring())
209-
210- # invoke the keyring lib
211- if keyring.set_password("demo-service", "tarek", "passexample") == 0:
212- print "password stored successful"
213- print "password", keyring.get_password("demo-service", "tarek")
214+ # define a new keyring class which extends the KeyringBackend
215+ import keyring.backend
216+ class TestKeyring(keyring.backend.KeyringBackend):
217+ """A test keyring which always outputs same password
218+ """
219+ def supported(self): return 0
220+ def set_password(self, servicename, username, password): return 0
221+ def get_password(self, servicename, username):
222+ return "password from TestKeyring"
223+
224+ # set the keyring for keyring lib
225+ import keyring
226+ keyring.set_keyring(TestKeyring())
227+
228+ # invoke the keyring lib
229+ try:
230+ keyring.set_password("demo-service", "tarek", "passexample")
231+ print "password stored sucessfully"
232+ except keyring.backend.PasswordError:
233+ print "failed to store password"
234+ print "password", keyring.get_password("demo-service", "tarek")
235
236
237 -----------------------------------------------
238@@ -193,9 +196,9 @@
239 The keyring lib has two functions:
240
241 * ``get_password(service, username)`` : Returns the password stored in keyring.
242- If the password does not exist, it will return None.
243+ If the password does not exist, it will return None.
244 * ``set_password(service, username, password)`` : Store the password in the
245- keyring.
246+ keyring.
247
248 Example
249 =======
250@@ -205,62 +208,62 @@
251 only returns true when the password equals to the username.
252 ::
253
254- """
255- auth_demo.py
256-
257- Created by Kang Zhang 2009-08-14
258- """
259-
260- import keyring
261- import getpass
262- import ConfigParser
263-
264- def auth(username, password):
265- """A faked authorization function.
266- """
267- return username == password
268-
269- def main():
270- """This scrip demos how to use keyring facilite the authorization. The
271- username is stored in a config named 'auth_demo.cfg'
272- """
273- # config file init
274- config_file = 'auth_demo.cfg'
275- config = ConfigParser.SafeConfigParser({
276- 'username':'',
277- })
278- config.read(config_file)
279- if not config.has_section('auth_demo_login'):
280- config.add_section('auth_demo_login')
281-
282- username = config.get('auth_demo_login','username')
283- password = None
284- if username != '':
285- password = keyring.get_password('auth_demo_login', username)
286-
287- if password == None or not auth(username, password):
288-
289- while 1:
290- username = raw_input("Username:\n")
291- password = getpass.getpass("Password:\n")
292-
293- if auth(username, password):
294- break
295- else:
296- print "Authorization failed."
297-
298- # store the username
299- config.set('auth_demo_login', 'username', username)
300- config.write(open(config_file, 'w'))
301-
302- # store the password
303- keyring.set_password('auth_demo_login', username, password)
304-
305- # the stuff that needs authorization here
306- print "Authorization successful."
307-
308- if __name__ == "__main__":
309- main()
310+ """
311+ auth_demo.py
312+
313+ Created by Kang Zhang 2009-08-14
314+ """
315+
316+ import keyring
317+ import getpass
318+ import ConfigParser
319+
320+ def auth(username, password):
321+ """A faked authorization function.
322+ """
323+ return username == password
324+
325+ def main():
326+ """This scrip demos how to use keyring facilite the authorization. The
327+ username is stored in a config named 'auth_demo.cfg'
328+ """
329+ # config file init
330+ config_file = 'auth_demo.cfg'
331+ config = ConfigParser.SafeConfigParser({
332+ 'username':'',
333+ })
334+ config.read(config_file)
335+ if not config.has_section('auth_demo_login'):
336+ config.add_section('auth_demo_login')
337+
338+ username = config.get('auth_demo_login','username')
339+ password = None
340+ if username != '':
341+ password = keyring.get_password('auth_demo_login', username)
342+
343+ if password == None or not auth(username, password):
344+
345+ while 1:
346+ username = raw_input("Username:\n")
347+ password = getpass.getpass("Password:\n")
348+
349+ if auth(username, password):
350+ break
351+ else:
352+ print "Authorization failed."
353+
354+ # store the username
355+ config.set('auth_demo_login', 'username', username)
356+ config.write(open(config_file, 'w'))
357+
358+ # store the password
359+ keyring.set_password('auth_demo_login', username, password)
360+
361+ # the stuff that needs authorization here
362+ print "Authorization successful."
363+
364+ if __name__ == "__main__":
365+ main()
366
367 ------------
368 Get involved
369@@ -284,8 +287,54 @@
370 .. _this post: http://tarekziade.wordpress.com/2009/03/27/pycon-hallway-session-1-a-keyring-library-for-python/
371 .. _Google Summer of Code: http://socghop.appspot.com/
372
373- * Kang Zhang
374- * Tarek Ziade
375+ See CONTRIBUTORS.txt for a complete list of contributors.
376+
377+ -------
378+ CHANGES
379+ -------
380+
381+ -----
382+ 0.5.1
383+ -----
384+
385+ * Remove a spurious KDE debug message when using KWallet
386+
387+ * Fix a bug that caused an exception if the user canceled the KWallet dialog
388+ (https://bitbucket.org/kang/python-keyring-lib/issue/37/user-canceling-of-kde-wallet-dialogs).
389+
390+ ---
391+ 0.5
392+ ---
393+
394+ * Now using the existing Gnome and KDE python libs instead of custom C++
395+ code.
396+
397+ * Using the getpass module instead of custom code
398+
399+ ---
400+ 0.4
401+ ---
402+
403+ * Fixed the setup script (some subdirs were not included in the release.)
404+
405+ ---
406+ 0.3
407+ ---
408+
409+ * Fixed keyring.core when the user doesn't have a cfg, or is not
410+ properly configured.
411+
412+ * Fixed escaping issues for usernames with non-ascii characters
413+
414+ ---
415+ 0.2
416+ ---
417+
418+ * Add support for Python 2.4+
419+ http://bitbucket.org/kang/python-keyring-lib/issue/2
420+
421+ * Fix the bug in KDE Kwallet extension compiling
422+ http://bitbucket.org/kang/python-keyring-lib/issue/3
423
424 Keywords: keyring Keychain GnomeKeyring Kwallet password storage
425 Platform: Many
426
427=== modified file 'README.txt'
428--- README.txt 2009-09-24 23:24:02 +0000
429+++ README.txt 2011-02-08 19:21:29 +0000
430@@ -41,9 +41,9 @@
431
432 Download the source tarball, and uncompress it, then run the install command::
433
434- $ wget http://pypi.python.org/packages/source/k/keyring/keyring-0.2.tar.gz
435- $ tar -xzvf keyring-0.2.tar.gz
436- $ cd keyring-0.2
437+ $ wget http://pypi.python.org/packages/source/k/keyring/keyring-0.3.tar.gz
438+ $ tar -xzvf keyring-0.3.tar.gz
439+ $ cd keyring-0.3
440 $ python setup.py install
441
442
443@@ -170,8 +170,11 @@
444 keyring.set_keyring(TestKeyring())
445
446 # invoke the keyring lib
447- if keyring.set_password("demo-service", "tarek", "passexample") == 0:
448- print "password stored successful"
449+ try:
450+ keyring.set_password("demo-service", "tarek", "passexample")
451+ print "password stored sucessfully"
452+ except keyring.backend.PasswordError:
453+ print "failed to store password"
454 print "password", keyring.get_password("demo-service", "tarek")
455
456
457@@ -269,12 +272,12 @@
458 Credits
459 -------
460
461-The project was based on Tarek Ziade's idea in `this post`_. Kang Zhang
462-initially carried it out as a `Google Summer of Code`_ project, and Tarek
463+The project was based on Tarek Ziade's idea in `this post`_. Kang Zhang
464+initially carried it out as a `Google Summer of Code`_ project, and Tarek
465 mentored Kang on this project.
466
467 .. _this post: http://tarekziade.wordpress.com/2009/03/27/pycon-hallway-session-1-a-keyring-library-for-python/
468 .. _Google Summer of Code: http://socghop.appspot.com/
469
470-* Kang Zhang
471-* Tarek Ziade
472+See CONTRIBUTORS.txt for a complete list of contributors.
473+
474
475=== modified file 'debian/changelog'
476--- debian/changelog 2010-12-03 00:11:03 +0000
477+++ debian/changelog 2011-02-08 19:21:29 +0000
478@@ -1,3 +1,24 @@
479+python-keyring (0.5.1-0ubuntu1~ppa0) natty; urgency=low
480+
481+ * New upstream release. (LP: #686257)
482+ * debian/control
483+ - The upstream package no longer builds extension modules for
484+ keyring-gnome or keyring-kwallet, so those binary packages are deleted.
485+ - Specify Python 2.6 as the minimum requirement and use
486+ X-Python-Version instead of XS-Python-Version (dh_python2).
487+ - Update Vcs-* headers to match what is given on the Cheeseshop page.
488+ - Update Standards-Version and Depends.
489+ - Suggests is no longer relevant, so it's removed.
490+ - Trim Build-Depends to only what's needed now.
491+ * debian/rules
492+ - Update to dh_python2 build system and simplify.
493+ * debian/python-keyring{,-gnome,-kwallet}.install
494+ - Removed as there is now only one binary package.
495+ * debian/copyright
496+ - Include my name since the packaging has changed quite a bit.
497+
498+ -- Barry Warsaw <barry@ubuntu.com> Wed, 02 Feb 2011 15:17:50 -0500
499+
500 python-keyring (0.2-3build1) natty; urgency=low
501
502 * Rebuild to add support for python 2.7.
503
504=== modified file 'debian/control'
505--- debian/control 2009-10-13 22:59:53 +0000
506+++ debian/control 2011-02-08 19:21:29 +0000
507@@ -1,45 +1,21 @@
508 Source: python-keyring
509 Priority: optional
510 Section: python
511-XS-Python-Version: >= 2.5
512-Maintainer: Carl Chenet <chaica@ohmytux.com>
513+X-Python-Version: >= 2.6
514+Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
515+XSBC-Original-Maintainer: Carl Chenet <chaica@ohmytux.com>
516 Uploaders: Debian Python Modules Team <python-modules-team@lists.alioth.debian.org>
517-Build-Depends: debhelper (>= 7.0.50~), python(>= 2.5), python-support, python-all-dev, pkg-config, libdbus-1-dev, libglib2.0-dev, libgnome-keyring-dev, kdelibs5-dev, kdelibs-bin, libqt4-dev
518-Standards-Version: 3.8.3
519-Homepage: http://pypi.python.org/pypi/keyring
520-Vcs-Svn: svn://svn.debian.org/svn/python-modules/packages/python-keyring/trunk/
521-Vcs-Browser: http://svn.debian.org/wsvn/python-modules/packages/python-keyring/trunk/
522+Build-Depends: debhelper (>= 7.0.50~), python-setuptools, python-all
523+Standards-Version: 3.9.1
524+Homepage: http://home.python-keyring.org/
525+Vcs-Hg: http://bitbucket.org/kang/python-keyring-lib/
526+Vcs-Browser: http://bitbucket.org/kang/python-keyring-lib/
527
528 Package: python-keyring
529 Architecture: all
530-Depends: ${python:Depends}, ${misc:Depends}, ${shlibs:Depends}
531+Depends: ${python:Depends}, ${misc:Depends}
532 Recommends: python-crypto
533-Suggests: python-keyring-gnome (>= ${source:Version}), python-keyring-kwallet (>= ${source:Version})
534 Description: store and access your passwords safely
535 The Python keyring library provides an easy way to access the system
536 keyring service (e.g Gnome-Keyring, KWallet) from Python.
537 it can be used in any application that needs safe password storage.
538- .
539- This package does not provides the Gnome-Keyring or KWallet backends.
540-
541-Package: python-keyring-gnome
542-Architecture: any
543-Depends: ${python:Depends}, ${misc:Depends}, ${shlibs:Depends}, python-keyring (= ${source:Version})
544-Provides: ${python:Provides}
545-Description: store and access your passwords safely - Gnome-Keyring backend
546- The Python keyring library provides an easy way to access the system
547- keyring service (e.g Gnome-Keyring, KWallet) from Python.
548- It can be used in any application that needs safe password storage.
549- .
550- This package only provides the Gnome-Keyring backend.
551-
552-Package: python-keyring-kwallet
553-Architecture: any
554-Depends: ${python:Depends}, ${misc:Depends}, ${shlibs:Depends}, python-keyring (= ${source:Version})
555-Provides: ${python:Provides}
556-Description: store and access your passwords safely - KWallet backend
557- The Python keyring library provides an easy way to access the system
558- keyring service (e.g Gnome-Keyring, KWallet) from Python.
559- it can be used in any application that needs safe password storage.
560- .
561- This package only provides the KWallet backend.
562
563=== modified file 'debian/copyright'
564--- debian/copyright 2009-09-24 23:24:02 +0000
565+++ debian/copyright 2011-02-08 19:21:29 +0000
566@@ -1,5 +1,6 @@
567-This package was debianized by Carl Chenet <chaica@ohmytux.com> on
568-Tue, 01 Sep 2009 00:22:25 +0200.
569+This package was originally debianized by Carl Chenet <chaica@ohmytux.com> on
570+Tue, 01 Sep 2009 00:22:25 +0200. The packaging was updated by Barry Warsaw
571+<barry@python.org> on 2011-02-02.
572
573 It was downloaded from http://pypi.python.org/pypi/keyring
574
575@@ -278,6 +279,7 @@
576 The Debian packaging is:
577
578 Copyright © 2009 Carl Chenet <chaica@ohmytux.com>
579+ Copyright © 2011 Barry Warsaw <barry@python.org>
580
581 and is licensed under the GPL version 3,
582 see `/usr/share/common-licenses/GPL-3'.
583
584=== removed file 'debian/python-keyring-gnome.install'
585--- debian/python-keyring-gnome.install 2009-09-24 23:24:02 +0000
586+++ debian/python-keyring-gnome.install 1970-01-01 00:00:00 +0000
587@@ -1,1 +0,0 @@
588-usr/lib/python*/*-packages/gnome_keyring.so
589
590=== removed file 'debian/python-keyring-kwallet.install'
591--- debian/python-keyring-kwallet.install 2009-09-24 23:24:02 +0000
592+++ debian/python-keyring-kwallet.install 1970-01-01 00:00:00 +0000
593@@ -1,1 +0,0 @@
594-usr/lib/python*/*-packages/kde_kwallet.so
595
596=== removed file 'debian/python-keyring.install'
597--- debian/python-keyring.install 2009-09-24 23:24:02 +0000
598+++ debian/python-keyring.install 1970-01-01 00:00:00 +0000
599@@ -1,2 +0,0 @@
600-usr/lib/python*/*-packages/keyring/*.py
601-usr/lib/python*/*-packages/keyring-*.egg-info
602
603=== modified file 'debian/rules'
604--- debian/rules 2009-10-13 22:59:53 +0000
605+++ debian/rules 2011-02-08 19:21:29 +0000
606@@ -1,13 +1,7 @@
607 #!/usr/bin/make -f
608-HOME=$(CURDIR)/debian/tmphome
609
610 %:
611- dh $@
612-
613-override_dh_auto_clean:
614- rm -rf $(HOME)
615- dh_auto_clean
616-
617-override_dh_auto_build:
618- mkdir -p $(HOME)
619- dh_auto_build
620+ dh $@ --with python2
621+
622+override_dh_installchangelogs:
623+ dh_installchangelogs -k CHANGES.txt
624
625=== modified file 'demo/keyring_demo.py'
626--- demo/keyring_demo.py 2009-09-24 23:24:02 +0000
627+++ demo/keyring_demo.py 2011-02-08 19:21:29 +0000
628@@ -19,7 +19,7 @@
629 config_file.writelines(["[backend]\n",
630 # the path for the user created keyring
631 "keyring-path= %s\n" % str(os.path.abspath(__file__))[:-16],
632- # the name of the keyring class
633+ # the name of the keyring class
634 "default-keyring=simplekeyring.SimpleKeyring\n" ])
635 config_file.close()
636
637@@ -28,14 +28,17 @@
638 import keyring
639
640 # invoke the keyring to store and fetch the password
641- if keyring.set_password("demo-service", "tarek", "passexample") == 0:
642- print "password stored sucessful"
643+ try:
644+ keyring.set_password("demo-service", "tarek", "passexample")
645+ print "password stored sucessfully"
646+ except keyring.backend.PasswordSetError:
647+ print "failed to store password"
648 print "password", keyring.get_password("demo-service", "tarek")
649
650 os.remove(KEYRINGRC)
651
652 def set_keyring_in_runtime():
653- """This function shows how to create a keyring manully and use it
654+ """This function shows how to create a keyring manully and use it
655 in runtime
656 """
657
658@@ -45,22 +48,25 @@
659 """A test keyring which always outputs same password
660 """
661 def supported(self): return 0
662- def set_password(self, servicename, username, password): return 0
663- def get_password(self, servicename, username):
664+ def set_password(self, servicename, username, password): return 0
665+ def get_password(self, servicename, username):
666 return "password from TestKeyring"
667-
668+
669 # set the keyring for keyring lib
670 import keyring
671 keyring.set_keyring(TestKeyring())
672
673 # invoke the keyring lib
674- if keyring.set_password("demo-service", "tarek", "passexample") == 0:
675- print "password stored successful"
676+ try:
677+ keyring.set_password("demo-service", "tarek", "passexample")
678+ print "password stored sucessfully"
679+ except keyring.backend.PasswordSetError:
680+ print "failed to store password"
681 print "password", keyring.get_password("demo-service", "tarek")
682
683 def main():
684- """This script shows how to enable the keyring using the config
685- file and in runtime.
686+ """This script shows how to enable the keyring using the config
687+ file and in runtime.
688 """
689
690 load_keyring_by_config()
691
692=== modified file 'extensions.py'
693--- extensions.py 2009-09-24 23:24:02 +0000
694+++ extensions.py 2011-02-08 19:21:29 +0000
695@@ -4,67 +4,10 @@
696 Created by Kang Zhang on 2009-08-07
697 """
698
699-import os
700 import sys
701-import commands
702
703 from distutils.core import Extension
704
705-def pkg_check(packages):
706- """Return false if not all packages has been installed properly.
707- """
708- status, output = commands.getstatusoutput("pkg-config --exists %s" %
709- ' '.join(packages))
710- return len(output) == 0 and status == 0
711-
712-def pkg_config(packages):
713- """Return the config parameters for all packages
714- """
715- keywords = {}
716- flag_map = {'-I':'include_dirs', '-L':'library_dirs', '-l':'libraries'}
717-
718- for token in commands.getoutput("pkg-config --libs --cflags %s" %
719- ' '.join(packages)).split():
720- try:
721- key = flag_map[token[:2]]
722- keywords.setdefault(key, []).append(token[2:])
723- except KeyError:
724- keywords.setdefault('extra_link_args', []).append(token)
725-
726- return keywords
727-
728-def kde_exec(cmd, option):
729- """Execute the kde-config command and get the output dirs
730- """
731- return commands.getoutput("%s %s --expandvars" % (cmd, option)).split(':')
732-
733-def kde_check(cmd, headfiles):
734- includes = kde_exec(cmd, '--install include')
735- for filename in headfiles:
736- # generate all possible paths for the headfile
737- paths = [os.path.join(dir, filename) for dir in includes]
738- # check if file exists on any path
739- exists = any([ os.path.exists(path) for path in paths])
740- if not exists:
741- return False
742- return True
743-
744-def kde_config(cmd, keywords):
745- """Add the compile parameters for kdelibs
746- """
747-
748- # KDE guys hate pkg-config, so we need due with it seperately. :-(
749- # See following link for more details
750- # http://lists.kde.org/?t=109647896600005&r=1&w=2
751-
752- keywords.setdefault('libraries', []).append('kdeui')
753-
754- libs = kde_exec(cmd, '--path lib')
755- includes = kde_exec(cmd, '--install include')
756-
757- keywords.setdefault('library_dirs', []).extend(libs)
758- keywords.setdefault('include_dirs', []).extend(includes)
759- return keywords
760
761 def get_extensions():
762 """Collect the extensions that can be installed.
763@@ -82,32 +25,6 @@
764 'CoreServices'])
765 exts.append(osx_keychain_module)
766
767- gnome_keyring_libs = ['dbus-1', 'glib-2.0', 'gnome-keyring-1']
768- if pkg_check(gnome_keyring_libs):
769- # gnome-keyring installed
770- gnome_keychain_module = Extension('gnome_keyring',
771- sources = ['keyring/backends/gnome_keyring.c'],
772- **pkg_config(gnome_keyring_libs)
773- )
774- exts.append(gnome_keychain_module)
775-
776- # check for KWallet heardfiles
777- kde_kwallet_headfiles = ['kwallet.h']
778- kde_cmd = None
779- for cmd in ('kde-config','kde4-config'):
780- if kde_check(cmd, kde_kwallet_headfiles):
781- kde_cmd = cmd
782-
783- # check for Kwallet related libs
784- kde_kwallet_libs = ['dbus-1', 'glib-2.0', 'QtGui']
785- if pkg_check(kde_kwallet_libs) and kde_cmd is not None:
786- # KDE Kwallet is installed.
787- kde_kwallet_module = Extension('kde_kwallet',
788- sources = ['keyring/backends/kde_kwallet.cpp'],
789- **kde_config(kde_cmd, pkg_config(kde_kwallet_libs))
790- )
791- exts.append(kde_kwallet_module)
792-
793 if platform in ['win32'] and sys.getwindowsversion()[-2] == 2:
794 # windows 2k+
795 win32_crypto_module = Extension('win32_crypto',
796
797=== modified file 'keyring/__init__.py'
798--- keyring/__init__.py 2009-09-24 23:24:02 +0000
799+++ keyring/__init__.py 2011-02-08 19:21:29 +0000
800@@ -7,3 +7,4 @@
801 logger = logging.getLogger('keyring')
802
803 from core import set_keyring, get_keyring, set_password, get_password
804+from keyring.getpassbackend import get_password as get_pass_get_password
805
806=== modified file 'keyring/backend.py'
807--- keyring/backend.py 2009-09-24 23:24:02 +0000
808+++ keyring/backend.py 2011-02-08 19:21:29 +0000
809@@ -6,9 +6,10 @@
810
811 import os
812 import sys
813-import getpass
814 import ConfigParser
815
816+from keyring.util.escape import escape as escape_for_ini
817+
818 try:
819 from abc import ABCMeta, abstractmethod
820 except ImportError:
821@@ -19,12 +20,20 @@
822 def abstractmethod(funcobj):
823 return funcobj
824
825+try:
826+ import gnomekeyring
827+except ImportError:
828+ pass
829
830 _KEYRING_SETTING = 'keyring-setting'
831 _CRYPTED_PASSWORD = 'crypted-password'
832 _BLOCK_SIZE = 32
833 _PADDING = '0'
834
835+class PasswordSetError(Exception):
836+ """Raised when the password can't be set.
837+ """
838+
839 class KeyringBackend(object):
840 """The abstract base class of the keyring, every backend must implement
841 this interface.
842@@ -44,13 +53,13 @@
843 def get_password(self, service, username):
844 """Get password of the username for the service
845 """
846- pass
847+ return None
848
849 @abstractmethod
850 def set_password(self, service, username, password):
851 """Set password for the username of the service
852 """
853- return -1
854+ raise PasswordSetError()
855
856 class _ExtensionKeyring(KeyringBackend):
857 """_ExtensionKeyring is a adaptor class for the platform related keyring
858@@ -94,11 +103,13 @@
859 def set_password(self, service, username, password):
860 """Overide the set_password() in KeyringBackend.
861 """
862- return self.keyring_impl.password_set(service, username, password)
863+ try:
864+ self.keyring_impl.password_set(service, username, password)
865+ except OSError:
866+ raise PasswordSetError()
867
868 class OSXKeychain(_ExtensionKeyring):
869- """The keyring backend based on Keychain Service of the Mac OSX
870- """
871+ """Mac OSX Keychain"""
872 def _init_backend(self):
873 """Return the handler: osx_keychain
874 """
875@@ -110,36 +121,100 @@
876 """
877 return sys.platform == 'darwin'
878
879-class GnomeKeyring(_ExtensionKeyring):
880- """The keyring backend using Gnome Keyring.
881- """
882- def _init_backend(self):
883- """Return the gnome_keyring handler.
884- """
885- import gnome_keyring
886- return gnome_keyring
887-
888- def _recommend(self):
889- """Recommend this keyring when Gnome is running.
890- """
891- # Gnome is running
892- return os.getenv("GNOME_DESKTOP_SESSION_ID") is not None
893-
894-
895-class KDEKWallet(_ExtensionKeyring):
896- """The keyring backend based on KDE KWallet
897- """
898- def _init_backend(self):
899- """Return the kde_kwallet handler.
900- """
901- import kde_kwallet
902- return kde_kwallet
903-
904- def _recommend(self):
905- """Recommend this keyring backend when KDE is running.
906- """
907- # KDE is running
908- return os.getenv("KDE_FULL_SESSION") == "true"
909+class GnomeKeyring(KeyringBackend):
910+ """Gnome Keyring"""
911+
912+ def supported(self):
913+ try:
914+ import gnomekeyring
915+ except ImportError:
916+ return -1
917+ else:
918+ return 1
919+
920+ def get_password(self, service, username):
921+ """Get password of the username for the service
922+ """
923+ try:
924+ items = gnomekeyring.find_network_password_sync(username, service)
925+ except gnomekeyring.NoMatchError:
926+ return None
927+ except gnomekeyring.CancelledError:
928+ # The user pressed "Cancel" when prompted to unlock their keyring.
929+ return None
930+
931+ assert len(items) == 1, 'no more than one entry should ever match'
932+ return items[0]['password']
933+
934+ def set_password(self, service, username, password):
935+ """Set password for the username of the service
936+ """
937+ try:
938+ gnomekeyring.set_network_password_sync(None, username, service,
939+ None, None, None, None, 0, password)
940+ except gnomekeyring.CancelledError:
941+ # The user pressed "Cancel" when prompted to unlock their keyring.
942+ raise PasswordSetError()
943+
944+def open_kwallet(kwallet_module=None, qt_module=None):
945+ # Allow for the injection of module-like objects for testing purposes.
946+ if kwallet_module is None:
947+ kwallet_module = KWallet.Wallet
948+ if qt_module is None:
949+ qt_module = QtGui
950+
951+ # KDE wants us to instantiate an application object.
952+ app = qt_module.QApplication([])
953+ try:
954+ window = QtGui.QWidget()
955+ kwallet = kwallet_module.openWallet(
956+ kwallet_module.NetworkWallet(),
957+ window.winId(),
958+ kwallet_module.Synchronous)
959+ if kwallet is not None:
960+ if not kwallet.hasFolder('Python'):
961+ kwallet.createFolder('Python')
962+ kwallet.setFolder('Python')
963+ finally:
964+ app.exit()
965+
966+
967+kwallet = None
968+try:
969+ from PyKDE4.kdeui import KWallet
970+ from PyQt4 import QtCore, QtGui
971+except ImportError:
972+ kwallet = None
973+else:
974+ kwallet = open_kwallet()
975+
976+
977+class KDEKWallet(KeyringBackend):
978+ """KDE KWallet"""
979+
980+ def supported(self):
981+ if kwallet is None:
982+ return -1
983+ else:
984+ return 1
985+
986+ def get_password(self, service, username):
987+ """Get password of the username for the service
988+ """
989+ key = username + '@' + service
990+ network = KWallet.Wallet.NetworkWallet()
991+ if kwallet.keyDoesNotExist(network, 'Python', key):
992+ return None
993+
994+ result = kwallet.readPassword(key)[1]
995+ # The string will be a PyQt4.QtCore.QString, so turn it into a unicode
996+ # object.
997+ return unicode(result)
998+
999+ def set_password(self, service, username, password):
1000+ """Set password for the username of the service
1001+ """
1002+ kwallet.writePassword(username+'@'+service, password)
1003
1004 class BasicFileKeyring(KeyringBackend):
1005 """BasicFileKeyring is a filebased implementation of keyring.
1006@@ -173,6 +248,9 @@
1007 def get_password(self, service, username):
1008 """Read the password from the file.
1009 """
1010+ service = escape_for_ini(service)
1011+ username = escape_for_ini(username)
1012+
1013 # load the passwords from the file
1014 config = ConfigParser.RawConfigParser()
1015 if os.path.exists(self.file_path):
1016@@ -192,6 +270,9 @@
1017 def set_password(self, service, username, password):
1018 """Write the password in the file.
1019 """
1020+ service = escape_for_ini(service)
1021+ username = escape_for_ini(username)
1022+
1023 # encrypt the password
1024 password_encrypted = self.encrypt(password)
1025 # load the password from the disk
1026@@ -208,11 +289,8 @@
1027 config_file = open(self.file_path,'w')
1028 config.write(config_file)
1029
1030- return 0
1031-
1032 class UncryptedFileKeyring(BasicFileKeyring):
1033- """A simple filekeyring which dose not encrypt the password.
1034- """
1035+ """Uncrypted File Keyring"""
1036 def filename(self):
1037 """Return the filename of the password file. It should be
1038 "keyring_pass.cfg" .
1039@@ -235,8 +313,7 @@
1040 return 0
1041
1042 class CryptedFileKeyring(BasicFileKeyring):
1043- """CryptedFileKeyring is a keyring using lib pycryto to encrypt the password
1044- """
1045+ """PyCrypto File Keyring"""
1046 def __init__(self):
1047 super(CryptedFileKeyring, self).__init__()
1048 self.crypted_password = None
1049@@ -264,6 +341,7 @@
1050 password = None
1051 while 1:
1052 if not password:
1053+ import getpass
1054 password = getpass.getpass()
1055 password2 = getpass.getpass('Password (again): ')
1056 if password != password2:
1057@@ -325,6 +403,7 @@
1058 self._init_file()
1059
1060 print "Please input your password for the keyring"
1061+ import getpass
1062 password = getpass.getpass()
1063
1064 if not self._auth(password):
1065@@ -352,9 +431,7 @@
1066
1067
1068 class Win32CryptoKeyring(BasicFileKeyring):
1069- """Win32CryptoKeyring is a keyring which use Windows CryptAPI to encrypt
1070- the user's passwords and store them in a file.
1071- """
1072+ """Win32 Cryptography Keyring"""
1073 def __init__(self):
1074 super(Win32CryptoKeyring, self).__init__()
1075
1076@@ -370,14 +447,15 @@
1077 return "wincrypto_pass.cfg"
1078
1079 def supported(self):
1080- """Recommend for all Windows is higher than Windows 2000.
1081+ """Recommend when other Windows backends are unavailable
1082 """
1083- if self.crypt_handler is not None and sys.platform == 'win32':
1084- major, minor, build, platform, text = sys.getwindowsversion()
1085- if platform == 2:
1086- # recommend for windows 2k+
1087- return 1
1088- return -1
1089+ recommended = select_windows_backend()
1090+ if recommended == None:
1091+ return -1
1092+ elif recommended == 'file':
1093+ return 1
1094+ else:
1095+ return 0
1096
1097 def encrypt(self, password):
1098 """Encrypt the password using the CryptAPI.
1099@@ -390,6 +468,132 @@
1100 return self.crypt_handler.decrypt(password_encrypted)
1101
1102
1103+class WinVaultKeyring(KeyringBackend):
1104+ def __init__(self):
1105+ super(WinVaultKeyring, self).__init__()
1106+ try:
1107+ import pywintypes, win32cred
1108+ self.win32cred = win32cred
1109+ self.pywintypes = pywintypes
1110+ except ImportError:
1111+ self.win32cred = None
1112+
1113+ def supported(self):
1114+ '''Default Windows backend, when it is available
1115+ '''
1116+ recommended = select_windows_backend()
1117+ if recommended == None:
1118+ return -1
1119+ elif recommended == 'cred':
1120+ return 1
1121+ else:
1122+ return 0
1123+
1124+ def get_password(self, service, username):
1125+ try:
1126+ blob = self.win32cred.CredRead(Type=self.win32cred.CRED_TYPE_GENERIC,
1127+ TargetName=service)['CredentialBlob']
1128+ except self.pywintypes.error, e:
1129+ if e[:2] == (1168, 'CredRead'):
1130+ return None
1131+ raise
1132+ return blob.decode("utf16")
1133+
1134+ def set_password(self, service, username, password):
1135+ credential = dict(Type=self.win32cred.CRED_TYPE_GENERIC,
1136+ TargetName=service,
1137+ UserName=username,
1138+ CredentialBlob=unicode(password),
1139+ Comment="Stored using python-keyring",
1140+ Persist=self.win32cred.CRED_PERSIST_ENTERPRISE)
1141+ self.win32cred.CredWrite(credential, 0)
1142+
1143+
1144+class Win32CryptoRegistry(KeyringBackend):
1145+ """Win32CryptoRegistry is a keyring which use Windows CryptAPI to encrypt
1146+ the user's passwords and store them under registry keys
1147+ """
1148+ def __init__(self):
1149+ super(Win32CryptoRegistry, self).__init__()
1150+
1151+ try:
1152+ import win32_crypto
1153+ import _winreg
1154+ self.crypt_handler = win32_crypto
1155+ except ImportError:
1156+ self.crypt_handler = None
1157+
1158+ def supported(self):
1159+ """Return if this keyring supports current enviroment.
1160+ -1: not applicable
1161+ 0: suitable
1162+ 1: recommended
1163+ """
1164+ recommended = select_windows_backend()
1165+ if recommended == None:
1166+ return -1
1167+ elif recommended == 'reg':
1168+ return 1
1169+ else:
1170+ return 0
1171+
1172+ def get_password(self, service, username):
1173+ """Get password of the username for the service
1174+ """
1175+ from _winreg import HKEY_CURRENT_USER, OpenKey, QueryValueEx
1176+ try:
1177+ # fetch the password
1178+ key = r'Software\%s\Keyring' % service
1179+ hkey = OpenKey(HKEY_CURRENT_USER, key)
1180+ password_base64 = QueryValueEx(hkey, username)[0]
1181+ # decode with base64
1182+ password_encrypted = password_base64.decode("base64")
1183+ # decrypted the password
1184+ password = self.crypt_handler.decrypt(password_encrypted)
1185+ except EnvironmentError:
1186+ password = None
1187+ return password
1188+
1189+
1190+ def set_password(self, service, username, password):
1191+ """Write the password to the registry
1192+ """
1193+ # encrypt the password
1194+ password_encrypted = self.crypt_handler.encrypt(password)
1195+ # encode with base64
1196+ password_base64 = password_encrypted.encode("base64")
1197+
1198+ # store the password
1199+ from _winreg import HKEY_CURRENT_USER, CreateKey, SetValueEx, REG_SZ
1200+ hkey = CreateKey(HKEY_CURRENT_USER, r'Software\%s\Keyring' % service)
1201+ SetValueEx(hkey, username, 0, REG_SZ, password_base64)
1202+
1203+def select_windows_backend():
1204+ if os.name != 'nt':
1205+ return None
1206+ major, minor, build, platform, text = sys.getwindowsversion()
1207+ try:
1208+ import pywintypes, win32cred
1209+ if (major, minor) >= (5, 1):
1210+ # recommend for windows xp+
1211+ return 'cred'
1212+ except ImportError:
1213+ pass
1214+ try:
1215+ import win32_crypto, _winreg
1216+ if (major, minor) >= (5, 0):
1217+ # recommend for windows 2k+
1218+ return 'reg'
1219+ except ImportError:
1220+ pass
1221+ try:
1222+ import win32_crypto
1223+ return 'file'
1224+ except ImportError:
1225+ pass
1226+ return None
1227+
1228+
1229 _all_keyring = None
1230
1231 def get_all_keyring():
1232@@ -399,6 +603,7 @@
1233 if _all_keyring is None:
1234 _all_keyring = [ OSXKeychain(), GnomeKeyring(), KDEKWallet(),
1235 CryptedFileKeyring(), UncryptedFileKeyring(),
1236- Win32CryptoKeyring()]
1237+ Win32CryptoKeyring(), Win32CryptoRegistry(),
1238+ WinVaultKeyring()]
1239 return _all_keyring
1240
1241
1242=== added file 'keyring/backends/__init__.py'
1243--- keyring/backends/__init__.py 1970-01-01 00:00:00 +0000
1244+++ keyring/backends/__init__.py 2011-02-08 19:21:29 +0000
1245@@ -0,0 +1,1 @@
1246+#
1247
1248=== removed file 'keyring/backends/gnome_keyring.c'
1249--- keyring/backends/gnome_keyring.c 2009-09-24 23:24:02 +0000
1250+++ keyring/backends/gnome_keyring.c 1970-01-01 00:00:00 +0000
1251@@ -1,111 +0,0 @@
1252-/*
1253- * Comment by Kang. This lib dose not support non_interactive mode
1254- */
1255-#include <glib.h>
1256-#include <dbus/dbus.h>
1257-#include <gnome-keyring.h>
1258-
1259-#include "Python.h"
1260-#include "keyring_util.h"
1261-
1262-static PyObject*
1263-gnome_keyring_password_get(PyObject *self, PyObject *args)
1264-{
1265- const char *realmstring;
1266- const char *username;
1267- const char *password;
1268-
1269- if (!PyArg_ParseTuple(args, "ss", &realmstring, &username)){
1270- PyErr_Clear();
1271- PyErr_SetString(PyExc_TypeError,
1272- "password_get() must be called as (servicename,username)");
1273- return NULL;
1274- }
1275- if ((! dbus_bus_get(DBUS_BUS_SESSION,NULL)) ||
1276- (!gnome_keyring_is_available())){
1277- PyErr_Clear();
1278- PyErr_SetString(PyExc_OSError, "Can's access the keyring now");
1279- return NULL;
1280- }
1281-
1282- GnomeKeyringResult result;
1283- GList *items;
1284-
1285- result = gnome_keyring_find_network_password_sync(username, realmstring,
1286- NULL, NULL, NULL, NULL, 0, &items);
1287-
1288- int status = 0;
1289- if (result == GNOME_KEYRING_RESULT_OK){
1290- if (items && items->data){
1291- GnomeKeyringNetworkPasswordData *item;
1292- item = (GnomeKeyringNetworkPasswordData *)items->data;
1293- if (item->password){
1294- size_t len = strlen(item->password);
1295- password = string_dump(item->password, len);
1296- status = 1;
1297- }
1298- gnome_keyring_network_password_list_free(items);
1299- }
1300- }
1301-
1302- if (!status){
1303- PyErr_Clear();
1304- PyErr_SetString(PyExc_OSError, "Can't fech password from system");
1305- return NULL;
1306- }
1307-
1308- return Py_BuildValue("s",password);
1309-}
1310-static PyObject*
1311-gnome_keyring_password_set(PyObject *self, PyObject *args)
1312-{
1313- const char *realmstring;
1314- const char *username;
1315- const char *password;
1316-
1317- if (!PyArg_ParseTuple(args, "sss", &realmstring, &username, &password)){
1318- PyErr_Clear();
1319- PyErr_SetString(PyExc_TypeError,
1320- "password_set() must be called as (servicename,username,password)");
1321- return NULL;
1322- }
1323- if ((! dbus_bus_get(DBUS_BUS_SESSION,NULL)) ||
1324- (!gnome_keyring_is_available())){
1325- PyErr_Clear();
1326- PyErr_SetString(PyExc_OSError,
1327- "Can's access the keyring now");
1328- return NULL;
1329- }
1330-
1331-
1332- GnomeKeyringResult result;
1333- guint32 item_id;
1334-
1335- result = gnome_keyring_set_network_password_sync(NULL, username, realmstring,
1336- NULL, NULL, NULL, NULL, 0, password, &item_id);
1337-
1338- return Py_BuildValue("i", (result!=GNOME_KEYRING_RESULT_OK));
1339-}
1340-
1341-
1342-static struct PyMethodDef gnome_keyring_methods[] = {
1343- {"password_set", gnome_keyring_password_set, METH_VARARGS},
1344- {"password_get", gnome_keyring_password_get, METH_VARARGS},
1345- {} /* Sentinel */
1346-};
1347-
1348-static void
1349-init_application_name(void)
1350-{
1351- const char *application_name = NULL;
1352- application_name = g_get_application_name();
1353- if (!application_name)
1354- g_set_application_name("Python");
1355-}
1356-
1357-PyMODINIT_FUNC
1358-initgnome_keyring(void)
1359-{
1360- init_application_name();
1361- Py_InitModule("gnome_keyring", gnome_keyring_methods);
1362-}
1363
1364=== removed file 'keyring/backends/kde_kwallet.cpp'
1365--- keyring/backends/kde_kwallet.cpp 2009-09-24 23:24:02 +0000
1366+++ keyring/backends/kde_kwallet.cpp 1970-01-01 00:00:00 +0000
1367@@ -1,121 +0,0 @@
1368-#include <dbus/dbus.h>
1369-#include <QtCore/QCoreApplication>
1370-#include <QtCore/QString>
1371-
1372-#include <kwallet.h>
1373-
1374-#include "Python.h"
1375-#include "keyring_util.h"
1376-
1377-static PyObject *
1378-kde_kwallet_password_get(PyObject *self, PyObject *args)
1379-{
1380- const char *realmstring;
1381- const char *username;
1382- const char *password;
1383-
1384- if (!PyArg_ParseTuple(args, "ss", &realmstring, &username)){
1385- PyErr_Clear();
1386- PyErr_SetString(PyExc_TypeError,
1387- "password_get() must be called as (service,username)");
1388- return NULL;
1389- }
1390-
1391- if (!dbus_bus_get(DBUS_BUS_SESSION,NULL)){
1392- PyErr_Clear();
1393- PyErr_SetString(PyExc_OSError,"can't get access to dbus");
1394- return NULL;
1395- }
1396-
1397- QCoreApplication *app;
1398- if (!qApp) {
1399- int argc = 1;
1400- app = new QCoreApplication(argc, (char *[1]){ (char*) "python"});
1401- }
1402- QString folder = QString::fromUtf8("Python");
1403- QString key = QString::fromUtf8(username) + "@" + QString::fromUtf8(realmstring);
1404- QString wallet_name = KWallet::Wallet::NetworkWallet();
1405- bool fetch_success = false;
1406- if (!KWallet::Wallet::keyDoesNotExist(wallet_name, folder, key)){
1407- KWallet::Wallet *wallet = KWallet::Wallet::openWallet(wallet_name, -1,
1408- KWallet::Wallet::Synchronous);
1409- if (wallet){
1410- if (wallet->setFolder(folder)){
1411- QString q_password;
1412- if (wallet->readPassword(key, q_password) == 0){
1413- password = string_dump(q_password.toUtf8().data(), q_password.size());
1414- fetch_success = true;
1415- }
1416- }
1417- }
1418- }
1419- if (!fetch_success){
1420- PyErr_Clear();
1421- PyErr_SetString(PyExc_OSError, "Can't access the password from the system");
1422- return NULL;
1423- }
1424- return Py_BuildValue("s",password);
1425-}
1426-
1427-static PyObject *
1428-kde_kwallet_password_set(PyObject *self, PyObject *args)
1429-{
1430- const char *realmstring;
1431- const char *username;
1432- const char *password;
1433-
1434- if (!PyArg_ParseTuple(args,"sss", &realmstring, &username, &password)){
1435- PyErr_Clear();
1436- PyErr_SetString(PyExc_TypeError,
1437- "password_set() must be called as (service,username,password)");
1438- return NULL;
1439- }
1440- if (!dbus_bus_get(DBUS_BUS_SESSION,NULL)){
1441- PyErr_Clear();
1442- PyErr_SetString(PyExc_OSError, "can't get access to dbus");
1443- return NULL;
1444- }
1445- QCoreApplication *app;
1446- if (! qApp){
1447- int argc = 1;
1448- app = new QCoreApplication(argc,(char *[1]) {(char*) "Python"});
1449- }
1450-
1451- bool write_success = false;
1452- QString q_password = QString::fromUtf8(password);
1453- QString wallet_name = KWallet::Wallet::NetworkWallet();
1454- QString folder = QString::fromUtf8("Python");
1455- KWallet::Wallet *wallet = KWallet::Wallet::openWallet(wallet_name, -1,
1456- KWallet::Wallet::Synchronous);
1457- if (wallet){
1458- if (!wallet->hasFolder(folder)){
1459- wallet->createFolder(folder);
1460- }
1461- if (wallet->setFolder(folder)){
1462- QString key = QString::fromUtf8(username) + "@" + QString::fromUtf8(realmstring);
1463- if (wallet->writePassword(key, q_password) == 0){
1464- write_success = true;
1465- }
1466- }
1467- }
1468-
1469- if (!write_success){
1470- PyErr_Clear();
1471- PyErr_SetString(PyExc_OSError, "Can't write the password in the system");
1472- return NULL;
1473- }
1474- return Py_BuildValue("i", write_success != true);
1475-}
1476-
1477-
1478-static struct PyMethodDef kde_kwallet_methods[] = {
1479- {"password_set", kde_kwallet_password_set, METH_VARARGS},
1480- {"password_get", kde_kwallet_password_get, METH_VARARGS},
1481- {NULL,NULL}/* Sentinel */
1482-};
1483-
1484-PyMODINIT_FUNC
1485-initkde_kwallet(void)
1486-{
1487- Py_InitModule("kde_kwallet", kde_kwallet_methods);
1488-}
1489
1490=== modified file 'keyring/backends/osx_keychain.c'
1491--- keyring/backends/osx_keychain.c 2009-09-24 23:24:02 +0000
1492+++ keyring/backends/osx_keychain.c 2011-02-08 19:21:29 +0000
1493@@ -53,8 +53,8 @@
1494 PyErr_SetString(PyExc_OSError, "Can't store password in Keychain");
1495 return NULL;
1496 }
1497-
1498- return Py_BuildValue("i",(status != 0));
1499+
1500+ Py_RETURN_NONE;
1501 }
1502
1503
1504
1505=== modified file 'keyring/core.py'
1506--- keyring/core.py 2009-09-24 23:24:02 +0000
1507+++ keyring/core.py 2011-02-08 19:21:29 +0000
1508@@ -7,7 +7,9 @@
1509 import ConfigParser
1510 import imp
1511 import sys
1512-import backend
1513+
1514+from keyring import logger
1515+from keyring import backend
1516
1517 def set_keyring(keyring):
1518 """Set current keyring backend.
1519@@ -30,31 +32,31 @@
1520 def set_password(service_name, username, password):
1521 """Set password for the user in the spcified service
1522 """
1523- return _keyring_backend.set_password(service_name, username, password)
1524+ _keyring_backend.set_password(service_name, username, password)
1525
1526 def init_backend():
1527 """first try to load the keyring in the config file, if it has not
1528 been decleared, assign a defult keyring according to the platform.
1529 """
1530 #select a backend according to the config file
1531- keyring_impl = load_config()
1532+ keyring = load_config()
1533
1534 # if the user dose not specify a keyring, we apply a default one
1535- if keyring_impl is None:
1536+ if keyring is None:
1537
1538 keyrings = backend.get_all_keyring()
1539 # rank according the supported
1540 keyrings.sort(lambda x, y: y.supported() - x.supported())
1541 # get the most recommend one
1542- keyring_impl = keyrings[0]
1543+ keyring = keyrings[0]
1544
1545- set_keyring(keyring_impl)
1546+ set_keyring(keyring)
1547
1548 def load_config():
1549 """load a keyring using the config file, the config file can be
1550 in the current working directory, or in the user's home directory.
1551 """
1552- keyring_impl = None
1553+ keyring = None
1554
1555 # search from current working directory and the home folder
1556 keyring_cfg_list = [os.path.join(os.getcwd(), "keyringrc.cfg"),
1557@@ -71,12 +73,19 @@
1558 config.read(keyring_cfg)
1559 # load the keyring-path option
1560 try:
1561- keyring_path = config.get("backend", "keyring-path").strip()
1562+ if config.has_section("backend"):
1563+ keyring_path = config.get("backend", "keyring-path").strip()
1564+ else:
1565+ keyring_path = None
1566 except ConfigParser.NoOptionError:
1567 keyring_path = None
1568+
1569 # load the keyring class name, and load it
1570 try:
1571- keyring_name = config.get("backend", "default-keyring").strip()
1572+ if config.has_section("backend"):
1573+ keyring_name = config.get("backend", "default-keyring").strip()
1574+ else:
1575+ raise ConfigParser.NoOptionError('backend', 'default-keyring')
1576
1577 def load_module(name, path):
1578 """Load the specified module from the disk.
1579@@ -111,12 +120,12 @@
1580 keyring_class = keyring_name.split('.')[-1].strip()
1581 exec "keyring_temp = module." + keyring_class + "() " in locals()
1582
1583- keyring_impl = keyring_temp
1584+ keyring = keyring_temp
1585 except (ConfigParser.NoOptionError, ImportError):
1586 logger.warning("Keyring Config file does not write correctly.\n" + \
1587 "Config file: %s" % keyring_cfg)
1588
1589- return keyring_impl
1590+ return keyring
1591
1592 # init the _keyring_backend
1593 init_backend()
1594
1595=== added file 'keyring/getpassbackend.py'
1596--- keyring/getpassbackend.py 1970-01-01 00:00:00 +0000
1597+++ keyring/getpassbackend.py 2011-02-08 19:21:29 +0000
1598@@ -0,0 +1,13 @@
1599+"""Specific support for getpass."""
1600+import os
1601+import getpass
1602+
1603+from keyring.core import get_password as original_get_password
1604+
1605+get_default_user = getpass.getuser
1606+
1607+def get_password(prompt='Password: ', stream=None,
1608+ service_name='Python',
1609+ username=get_default_user()):
1610+ return original_get_password(service_name, username)
1611+
1612
1613=== added file 'keyring/tests/__init__.py'
1614--- keyring/tests/__init__.py 1970-01-01 00:00:00 +0000
1615+++ keyring/tests/__init__.py 2011-02-08 19:21:29 +0000
1616@@ -0,0 +1,1 @@
1617+#
1618
1619=== modified file 'keyring/tests/test_backend.py'
1620--- keyring/tests/test_backend.py 2009-09-24 23:24:02 +0000
1621+++ keyring/tests/test_backend.py 2011-02-08 19:21:29 +0000
1622@@ -15,14 +15,17 @@
1623 import commands
1624 import keyring.backend
1625
1626+from keyring.backend import PasswordSetError
1627+
1628 ALPHABET = string.ascii_letters + string.digits
1629+DIFFICULT_CHARS = string.whitespace + string.punctuation
1630
1631-def random_string(k):
1632+def random_string(k, source = ALPHABET):
1633 """Generate a random string with length <i>k</i>
1634 """
1635 result = ''
1636 for i in range(0, k):
1637- result += random.choice(ALPHABET)
1638+ result += random.choice(source)
1639 return result
1640
1641 def backup(file):
1642@@ -41,16 +44,10 @@
1643
1644 __test__ = False
1645
1646- def init_keyring(self):
1647- return None
1648-
1649 def setUp(self):
1650 self.keyring = self.init_keyring()
1651
1652- def test_password_set_get(self):
1653- password = random_string(20)
1654- username = random_string(20)
1655- service = random_string(20)
1656+ def check_set_get(self, service, username, password):
1657 keyring = self.keyring
1658
1659 if self.supported() == -1: # skip the unsupported keyring
1660@@ -60,13 +57,25 @@
1661 self.assertEqual(keyring.get_password(service, username), None)
1662
1663 # common usage
1664- self.assertEqual(keyring.set_password(service, username, password), 0)
1665+ keyring.set_password(service, username, password)
1666 self.assertEqual(keyring.get_password(service, username), password)
1667
1668 # for the empty password
1669- self.assertEqual(keyring.set_password(service, username, ""), 0)
1670+ keyring.set_password(service, username, "")
1671 self.assertEqual(keyring.get_password(service, username), "")
1672
1673+ def test_password_set_get(self):
1674+ password = random_string(20)
1675+ username = random_string(20)
1676+ service = random_string(20)
1677+ self.check_set_get(service, username, password)
1678+
1679+ def test_difficult_chars(self):
1680+ password = random_string(20, DIFFICULT_CHARS)
1681+ username = random_string(20, DIFFICULT_CHARS)
1682+ service = random_string(20, DIFFICULT_CHARS)
1683+ self.check_set_get(service, username, password)
1684+
1685 def supported(self):
1686 """Return the correct value for supported.
1687 """
1688@@ -81,6 +90,7 @@
1689 __test__ = True
1690
1691 def init_keyring(self):
1692+ print >> sys.stderr, "Testing OSXKeychain, following password prompts are for this keyring"
1693 return keyring.backend.OSXKeychain()
1694
1695 def supported(self):
1696@@ -92,31 +102,58 @@
1697 __test__ = True
1698
1699 def init_keyring(self):
1700+ print >> sys.stderr, "Testing GnomeKeyring, following password prompts are for this keyring"
1701 return keyring.backend.GnomeKeyring()
1702
1703 def supported(self):
1704- try:
1705- import gnome_keyring
1706- except ImportError:
1707- return -1
1708- if os.getenv("GNOME_DESKTOP_SESSION_ID") is not None:
1709- return 1
1710- return 0
1711+ return self.keyring.supported()
1712
1713 class KDEKWalletTestCase(BackendBasicTestCase):
1714 __test__ = True
1715
1716 def init_keyring(self):
1717+ print >> sys.stderr, "Testing KDEKWallet, following password prompts are for this keyring"
1718 return keyring.backend.KDEKWallet()
1719
1720 def supported(self):
1721- try:
1722- import kde_kwallet
1723- except ImportError:
1724- return -1
1725- if os.getenv("KDE_FULL_SESSION") == "true":
1726- return 1
1727- return 0
1728+ return self.keyring.supported()
1729+
1730+
1731+class UnOpenableKWallet(object):
1732+ """A module-like object used to test KDE wallet fall-back."""
1733+
1734+ Synchronous = None
1735+
1736+ def openWallet(self, *args):
1737+ return None
1738+
1739+ def NetworkWallet(self):
1740+ return None
1741+
1742+
1743+class FauxQtGui(object):
1744+ """A fake module-like object used in testing the open_kwallet function."""
1745+
1746+ class QApplication(object):
1747+ def __init__(self, *args):
1748+ pass
1749+
1750+ def exit(self):
1751+ pass
1752+
1753+
1754+class KDEWalletCanceledTestCase(unittest.TestCase):
1755+
1756+ def test_user_canceled(self):
1757+ # If the user cancels either the "enter your password to unlock the
1758+ # keyring" dialog or clicks "deny" on the "can this application access
1759+ # the wallet" dialog then openWallet() will return None. The
1760+ # open_wallet() function should handle that eventuality by returning
1761+ # None to signify that the KWallet backend is not available.
1762+ self.assertEqual(
1763+ keyring.backend.open_kwallet(UnOpenableKWallet(), FauxQtGui()),
1764+ None)
1765+
1766
1767 class FileKeyringTestCase(BackendBasicTestCase):
1768 __test__ = False
1769@@ -148,6 +185,7 @@
1770 __test__ = True
1771
1772 def init_keyring(self):
1773+ print >> sys.stderr, "Testing UnecryptedFile, following password prompts are for this keyring"
1774 return keyring.backend.UncryptedFileKeyring()
1775
1776 def supported(self):
1777@@ -157,6 +195,7 @@
1778 __test__ = True
1779
1780 def init_keyring(self):
1781+ print >> sys.stderr, "Testing CryptedFile, following password prompts are for this keyring"
1782 return keyring.backend.CryptedFileKeyring()
1783
1784 def supported(self):
1785@@ -171,6 +210,7 @@
1786 __test__ = True
1787
1788 def init_keyring(self):
1789+ print >> sys.stderr, "Testing Win32, following password prompts are for this keyring"
1790 return keyring.backend.Win32CryptoKeyring()
1791
1792 def supported(self):
1793
1794=== modified file 'keyring/tests/test_core.py'
1795--- keyring/tests/test_core.py 2009-09-24 23:24:02 +0000
1796+++ keyring/tests/test_core.py 2011-02-08 19:21:29 +0000
1797@@ -3,10 +3,12 @@
1798
1799 Created by Kang Zhang on 2009-08-09
1800 """
1801-
1802 import unittest
1803 import os
1804 import sys
1805+import tempfile
1806+import shutil
1807+
1808 import keyring.backend
1809 import keyring.core
1810
1811@@ -36,17 +38,15 @@
1812 def test_set_get_password(self):
1813 """Test the basic function of the keyring.
1814 """
1815- self.assertEqual(keyring.core.set_password("test", "user", "passtest"),
1816- 0)
1817+ keyring.core.set_password("test", "user", "passtest")
1818 self.assertEqual(keyring.core.get_password("test", "user"), "passtest")
1819-
1820+
1821 def test_set_keyring_in_runtime(self):
1822 """Test the function of set keyring in runtime.
1823 """
1824 keyring.core.set_keyring(TestKeyring())
1825
1826- self.assertEqual(keyring.core.set_password("test", "user", "password"),
1827- 0)
1828+ keyring.core.set_password("test", "user", "password")
1829 self.assertEqual(keyring.core.get_password("test", "user"),
1830 PASSWORD_TEXT)
1831
1832@@ -56,23 +56,54 @@
1833 # create the config file
1834 config_file = open(KEYRINGRC,'w')
1835 config_file.writelines(["[backend]\n",
1836- # the path for the user created keyring
1837- "keyring-path= %s\n" % str(os.path.abspath(__file__))[:-16],
1838- # the name of the keyring class
1839- "default-keyring=test_core.TestKeyring2\n" ])
1840+ # the path for the user created keyring
1841+ "keyring-path= %s\n" % os.path.dirname(os.path.abspath(__file__)),
1842+ # the name of the keyring class
1843+ "default-keyring=test_core.TestKeyring2\n" ])
1844 config_file.close()
1845
1846 # init the keyring lib, the lib will automaticlly load the
1847 # config file and load the user defined module
1848 keyring.core.init_backend()
1849
1850- self.assertEqual(keyring.core.set_password("test", "user", "password"),
1851- 0)
1852+ keyring.core.set_password("test", "user", "password")
1853 self.assertEqual(keyring.core.get_password("test", "user"),
1854- PASSWORD_TEXT_2)
1855+ PASSWORD_TEXT_2)
1856
1857 os.remove(KEYRINGRC)
1858
1859+ def test_load_config(self):
1860+ tempdir = tempfile.mkdtemp()
1861+ old_location = os.getcwd()
1862+ os.chdir(tempdir)
1863+ personal_cfg = os.path.join(os.path.expanduser("~"), "keyringrc.cfg")
1864+ if os.path.exists(personal_cfg):
1865+ os.rename(personal_cfg, personal_cfg+'.old')
1866+ personal_renamed = True
1867+ else:
1868+ personal_renamed = False
1869+
1870+ # loading with an empty environment
1871+ keyring.core.load_config()
1872+
1873+ # loading with a file that doesn't have a backend section
1874+ cfg = os.path.join(tempdir, "keyringrc.cfg")
1875+ f = open(cfg, 'w')
1876+ f.write('[keyring]')
1877+ f.close()
1878+ keyring.core.load_config()
1879+
1880+ # loading with a file that doesn't have a default-keyring value
1881+ cfg = os.path.join(tempdir, "keyringrc.cfg")
1882+ f = open(cfg, 'w')
1883+ f.write('[backend]')
1884+ f.close()
1885+ keyring.core.load_config()
1886+
1887+ os.chdir(old_location)
1888+ shutil.rmtree(tempdir)
1889+ if personal_renamed:
1890+ os.rename(personal_cfg+'.old', personal_cfg)
1891
1892 def test_suite():
1893 suite = unittest.TestSuite()
1894
1895=== added file 'keyring/tests/test_util.py'
1896--- keyring/tests/test_util.py 1970-01-01 00:00:00 +0000
1897+++ keyring/tests/test_util.py 2011-02-08 19:21:29 +0000
1898@@ -0,0 +1,37 @@
1899+# -*- coding: utf-8 -*-
1900+
1901+"""
1902+Test for simple escape/unescape routine
1903+"""
1904+
1905+
1906+import unittest
1907+import os
1908+import sys
1909+import tempfile
1910+import shutil
1911+
1912+from keyring.util import escape
1913+
1914+class EscapeTestCase(unittest.TestCase):
1915+
1916+ def check_escape_unescape(self, initial):
1917+ escaped = escape.escape(initial)
1918+ self.assertTrue(all( c in (escape.LEGAL_CHARS + escape.ESCAPE_CHAR)
1919+ for c in escaped))
1920+ unescaped = escape.unescape(escaped)
1921+ self.assertEqual(initial, unescaped)
1922+
1923+ def test_escape_unescape(self):
1924+ self.check_escape_unescape("aaaa")
1925+ self.check_escape_unescape("aaaa bbbb cccc")
1926+ self.check_escape_unescape(u"Zażółć gęślą jaźń".encode("utf-8"))
1927+ self.check_escape_unescape("(((P{{{{'''---; ;; '\"|%^")
1928+
1929+def test_suite():
1930+ suite = unittest.TestSuite()
1931+ suite.addTest(unittest.makeSuite(EscapeTestCase))
1932+ return suite
1933+
1934+if __name__ == "__main__":
1935+ unittest.main(defaultTest="test_suite")
1936
1937=== added directory 'keyring/util'
1938=== added file 'keyring/util/__init__.py'
1939=== added file 'keyring/util/escape.py'
1940--- keyring/util/escape.py 1970-01-01 00:00:00 +0000
1941+++ keyring/util/escape.py 2011-02-08 19:21:29 +0000
1942@@ -0,0 +1,25 @@
1943+"""
1944+escape/unescape routines available for backends which need
1945+alphanumeric usernames, services, or other values
1946+"""
1947+
1948+import string, re
1949+
1950+LEGAL_CHARS = string.letters + string.digits
1951+ESCAPE_CHAR = "_"
1952+
1953+def escape(value):
1954+ """Escapes given value so the result consists of alphanumeric chars and underscore
1955+ only, and alphanumeric chars are preserved"""
1956+ def escape_char(c, legal = LEGAL_CHARS):
1957+ # Single char escape. Either normal char, or _<hexcode>
1958+ if c in legal:
1959+ return c
1960+ else:
1961+ return "%s%X" % (ESCAPE_CHAR, ord(c))
1962+ return "".join( escape_char(c) for c in value )
1963+
1964+def unescape(value):
1965+ """Reverts escape"""
1966+ re_esc = re.compile("_([0-9A-F]{2})")
1967+ return re_esc.sub(lambda i: chr(int(i.group(1),16)), value)
1968
1969=== modified file 'setup.py'
1970--- setup.py 2009-09-24 23:24:02 +0000
1971+++ setup.py 2011-02-08 19:21:29 +0000
1972@@ -12,16 +12,17 @@
1973 from extensions import get_extensions
1974
1975 setup(name = 'keyring',
1976- version = "0.2",
1977+ version = "0.5.1",
1978 description = "Store and access your passwords safely.",
1979 url = "http://home.python-keyring.org/",
1980 keywords = "keyring Keychain GnomeKeyring Kwallet password storage",
1981 maintainer = "Kang Zhang",
1982 maintainer_email = "jobo.zh@gmail.com",
1983 license="PSF",
1984- long_description = open('README.txt').read(),
1985+ long_description = open('README.txt').read() + open('CHANGES.txt').read(),
1986 platforms = ["Many"],
1987- packages = ['keyring'],
1988+ packages = ['keyring', 'keyring.tests', 'keyring.util',
1989+ 'keyring.backends'],
1990 ext_modules = get_extensions()
1991 )
1992

Subscribers

People subscribed via source and target branches

to all changes: