Changeset 11813
- Timestamp:
- Jul 27, 2012, 10:48:39 PM (11 years ago)
- Location:
- cryptoplugin/trunk
- Files:
-
- 5 added
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
cryptoplugin/trunk/COPYING
r11807 r11813 27 27 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 28 28 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30 ---- 31 32 For OpenPGP operation this code relies on python-gnupg, a wrapper for the 33 'gpg' command. So it's disclaimer follows here for completeness: 34 35 Portions of this module are derived from A.M. Kuchling's well-designed 36 GPG.py, using Richard Jones' updated version 1.3, which can be found 37 in the pycrypto CVS repository on Sourceforge: 38 39 http://pycrypto.cvs.sourceforge.net/viewvc/pycrypto/gpg/GPG.py 40 41 This module is *not* forward-compatible with amk's; some of the 42 old interface has changed. For instance, since I've added decrypt 43 functionality, I elected to initialize with a 'gnupghome' argument 44 instead of 'keyring', so that gpg can find both the public and secret 45 keyrings. I've also altered some of the returned objects in order for 46 the caller to not have to know as much about the internals of the 47 result classes. 48 49 While the rest of ISconf is released under the GPL, I am releasing 50 this single file under the same terms that A.M. Kuchling used for 51 pycrypto. 52 53 Steve Traugott, stevegt@terraluna.org 54 Thu Jun 23 21:27:20 PDT 2005 55 56 This version of the module has been modified from Steve Traugott's version 57 (see http://trac.t7a.org/isconf/browser/trunk/lib/python/isconf/GPG.py) by 58 Vinay Sajip to make use of the subprocess module (Steve's version uses 59 os.fork() and so does not work on Windows). Renamed to gnupg.py to avoid 60 confusion with the previous versions. 61 62 Modifications Copyright (C) 2008-2012 Vinay Sajip. All rights reserved. 63 64 ---- 65 66 Finally, the licensing terms of pycrypto are reproduced here for reference: 67 68 =================================================================== 69 Distribute and use freely; there are no restrictions on further 70 dissemination and usage except those imposed by the laws of your 71 country of residence. This software is provided "as is" without 72 warranty of fitness for use or suitability for any purpose, express 73 or implied. Use at your own risk or not at all. 74 =================================================================== 75 76 Incorporating the code into commercial products is permitted; you do 77 not have to make source available or contribute your changes back 78 (though that would be nice). 79 80 --amk (www.amk.ca) -
cryptoplugin/trunk/crypto/admin.py
r11811 r11813 7 7 # you should have received as part of this distribution. 8 8 9 import os 10 11 from datetime import datetime 12 9 13 from trac.admin import IAdminPanelProvider 14 from trac.config import Option 10 15 from trac.core import implements 11 16 from trac.perm import IPermissionRequestor 17 from trac.util.datefmt import to_timestamp, utc 18 from trac.web.chrome import add_notice, add_stylesheet, add_warning 12 19 13 from crypto.api import _, dgettext 20 from crypto.api import CryptoBase, _, dgettext 21 from crypto.compat import exception_to_unicode 14 22 from crypto.web_ui import CommonTemplateProvider 15 23 … … 22 30 # IPermissionRequestor method 23 31 def get_permission_actions(self): 24 actions = ['CRYPTO_ADMIN'] 32 action = ['CRYPTO_DELETE'] 33 actions = [('CRYPTO_ADMIN', action), action[0]] 25 34 return actions 26 35 … … 32 41 33 42 def render_admin_panel(self, req, cat, page, path_info): 43 if req.method == 'POST': 44 defaults = self.config.defaults().get('crypto') 45 ## Read admin form values. 46 # GnuPG settings 47 gpg_binary = req.args.get('gpg_binary') 48 gpg_home = req.args.get('gpg_home') 49 priv_key = req.args.get('priv_key') 50 # Key generation presets 51 expire_date = req.args.get('expire_date') 52 key_length = req.args.get('key_length') 53 key_type = req.args.get('key_type') 54 subkey_length = req.args.get('subkey_length') 55 subkey_type = req.args.get('subkey_type') 56 # Checkbox return value requires special parsing. 57 allow_usermod = bool(req.args.get('allow_usermod')) 58 59 # Overwrite deleted values with defaults. 60 if not gpg_binary and defaults: 61 gpg_binary = defaults['gpg_binary'] 62 self.config.set('crypto', 'gpg_binary', gpg_binary) 63 if not gpg_home and defaults: 64 gpg_home = defaults['gpg_home'] 65 self.config.set('crypto', 'gpg_home', gpg_home) 66 if not priv_key and defaults: 67 priv_key = defaults['private_key'] 68 self.config.set('crypto', 'gpg_private_key', priv_key) 69 70 self.config.set('crypto', 'gpg_keygen_allow_usermod', 71 allow_usermod) 72 if not expire_date and defaults: 73 expire_date = defaults['gpg_keygen_expire_date'] 74 self.config.set('crypto', 'gpg_keygen_expire_date', expire_date) 75 if not key_length and defaults: 76 key_length = defaults['gpg_keygen_key_length'] 77 self.config.set('crypto', 'gpg_keygen_key_length', key_length) 78 if not key_type and defaults: 79 key_type = defaults['gpg_keygen_key_type'] 80 self.config.set('crypto', 'gpg_keygen_key_type', key_type) 81 if not subkey_length and defaults: 82 subkey_length = defaults['gpg_keygen_subkey_length'] 83 self.config.set('crypto', 'gpg_keygen_subkey_length', 84 subkey_length) 85 if not subkey_type and defaults: 86 subkey_type = defaults['gpg_keygen_subkey_type'] 87 self.config.set('crypto', 'gpg_keygen_subkey_type', subkey_type) 88 89 # Save effective new configuration. 90 _save_config(self.config, req, self.log) 91 req.redirect(req.href.admin(cat, page)) 92 34 93 # Get current configuration. 35 if req.method == 'POST': 36 # Save configuration changes. 37 test = 'replace_with_real_code' 38 # Display current configuration. 39 data = {'_dgettext': dgettext} 94 gpg = { 95 'binary': self.config.get('crypto', 'gpg_binary'), 96 'home': self.config.get('crypto', 'gpg_home') 97 } 98 keygen = { 99 'allow_usermod': self.config.getbool('crypto', 100 'gpg_keygen_allow_usermod'), 101 'expire_date': self.config.get('crypto', 'gpg_keygen_expire_date'), 102 'key_length': self.config.getint('crypto', 103 'gpg_keygen_key_length'), 104 'key_type': self.config.get('crypto', 'gpg_keygen_key_type'), 105 'subkey_length': self.config.getint('crypto', 106 'gpg_keygen_subkey_length'), 107 'subkey_type': self.config.get('crypto', 'gpg_keygen_subkey_type') 108 } 109 now_ts = to_timestamp(datetime.now(utc)) 110 priv_key = self.config.get('crypto', 'gpg_private_key') 111 priv_keys = [dict(id='', label=_("(Select private key)"))] + [ 112 dict(id=key['keyid'], 113 label=' - '.join([key['keyid'], key.get('uids')[1]]), 114 disabled=key.get('expires') and \ 115 int(key.get('expires')) < now_ts, 116 selected=key['keyid'] == priv_key) 117 for key in CryptoBase(self.env).keys(private=True) 118 ] 119 120 data = { 121 '_dgettext': dgettext, 122 'env_dir': os.path.abspath(self.env.path), 123 'gpg': gpg, 124 'keygen': keygen, 125 'priv_keys': priv_keys 126 } 127 add_stylesheet(req, 'crypto/crypto.css') 40 128 return 'admin_crypto.html', data 129 130 131 def _save_config(config, req, log): 132 """Try to save the config, and display either a success notice or a 133 failure warning (code copied verbatim from Trac core). 134 """ 135 try: 136 config.save() 137 add_notice(req, _("Your changes have been saved.")) 138 except Exception, e: 139 log.error('Error writing to trac.ini: %s', exception_to_unicode(e)) 140 add_warning(req, _("""Error writing to trac.ini, make sure it is 141 writable by the web server. Your changes have not 142 been saved.""")) -
cryptoplugin/trunk/crypto/api.py
r11811 r11813 9 9 from pkg_resources import resource_filename 10 10 11 from trac.core import Component 11 from trac.core import Component, ExtensionPoint, Interface, implements 12 12 13 13 # Import standard i18n methods. … … 41 41 42 42 43 class IKeyVault(Interface): 44 """Defines common key store operations.""" 45 46 def keys(self, private=False, id_only=False): 47 """Returns the list of all available keys.""" 48 49 def has_key(self, key_id, private=False): 50 """Returns whether a key with the given ID is available or not.""" 51 52 def get_key(self, key_id, private=False): 53 """Returns a distinct key as dictionary of key properties.""" 54 55 def create_key(self, **kwargs): 56 """Generate a new key with specified properties.""" 57 58 def delete_key(self, key_id, perm): 59 """Delete the key specified by ID.""" 60 61 62 class ICipherModule(Interface): 63 """Defines common key operations.""" 64 65 66 class Credential(Component): 67 68 abstract = True 69 70 71 class GenericFactory(Component): 72 """Provides common key store operations.""" 73 74 abstract = True 75 implements(IKeyVault) 76 name = 'generic' 77 78 def keys(self, private=False, id_only=False): 79 """Returns the list of all available keys.""" 80 return [] 81 82 def has_key(self, key_id, private=False): 83 """Returns whether a key with the given ID is available or not.""" 84 if key_id in self.keys(private=private, id_only=True): 85 return True 86 return False 87 88 def get_key(self, key_id, private=False): 89 """Returns a distinct key as dictionary of key properties.""" 90 for key in self.keys(private=private): 91 if key_id == key['keyid']: 92 return key 93 return {} 94 95 def create_key(self, **kwargs): 96 """Generate a new key with specified properties.""" 97 return {} 98 99 def delete_key(self, key_id, perm): 100 """Delete the key specified by ID.""" 101 return False 102 103 43 104 class CryptoBase(Component): 44 105 """Cryptography foundation for Trac.""" 106 107 _key_stores = ExtensionPoint(IKeyVault) 45 108 46 109 def __init__(self): … … 48 111 locale_dir = resource_filename(__name__, 'locale') 49 112 add_domain(self.env.path, locale_dir) 113 114 def keys(self, private=False, id_only=False): 115 """Returns the list of all available keys.""" 116 for store in self._key_stores: 117 for key in store.keys(private=private, id_only=id_only): 118 yield key 119 120 def has_key(self, key_id, private=False): 121 """Returns whether a key with the given ID is available or not.""" 122 if key_id in self.keys(private=private, id_only=True): 123 return True 124 return False -
cryptoplugin/trunk/crypto/templates/admin_crypto.html
r11811 r11813 17 17 <body> 18 18 <h2>Cryptography: Configuration</h2> 19 <form class="mod" id="modcrypto" method="post" action=""> 20 <fieldset> 21 <legend>OpenPGP: GnuPG</legend> 22 <div class="field"> 23 <label>Binary:<br /> 24 <input type="text" id="gpg_binary" name="gpg_binary" size="48" 25 value="$gpg.binary"/> 26 </label> 27 <p class="help"> 28 GnuPG executable name, optionally including full path. For usual 29 installations that location is auto-detected by retaining the 30 default value <code>gpg</code>. 31 </p> 32 </div> 33 <div class="field"> 34 <label>Directory:<br /> 35 <input type="text" id="gpg_home" name="gpg_home" size="48" 36 value="$gpg.home"/> 37 </label> 38 <p class="help" i18n:msg="dir"> 39 This directory contains keyrings as well as other support files. 40 You could direct to an existing GnuPG home directory, or to a 41 new path so that it will be created upon next environment reload 42 and will be populated with new files including empty keyrings. 43 If you specify a relative path, that directory will reside inside 44 the project environment (<code>$env_dir</code>). 45 </p> 46 </div> 47 <div class="field"> 48 <label>Server Key:<br /> 49 <select id="priv_key" name="priv_key"> 50 <option py:for="key in priv_keys" value="${key.id}" 51 selected="${key.selected or None}" 52 disabled="${key.disabled or None}">${key.label}</option> 53 </select> 54 </label> 55 <p class="help"> 56 The selected key is used for signing auto-generated content. Such 57 unattended operation currently requires a blank private key 58 password. Directory and keyring file access must be narrowed down 59 carefully to limit exposure of this sensitive information. 60 </p> 61 </div> 62 63 <fieldset> 64 <legend>OpenPGP Key Generation Presets</legend> 65 <table> 66 <tr> 67 <th class="col1"> 68 <label for="key_type">Key Type:</label> 69 </th> 70 <td class="col1"> 71 <input type="text" id="key_type" name="key_type" 72 value="$keygen.key_type"/> 73 </td> 74 <th class="col2"> 75 <label for="key_length">Key Length:</label> 76 </th> 77 <td class="col2"> 78 <input type="text" id="key_length" name="key_length" 79 value="$keygen.key_length"/> 80 </td> 81 </tr> 82 <tr> 83 <th class="col1"> 84 <label for="subkey_type">Subkey Type:</label> 85 </th> 86 <td class="col1"> 87 <input type="text" id="subkey_type" name="subkey_type" 88 value="$keygen.subkey_type"/> 89 </td> 90 <th class="col2"> 91 <label for="subkey_length">Subkey Length:</label> 92 </th> 93 <td class="col2"> 94 <input type="text" id="subkey_length" name="subkey_length" 95 value="$keygen.subkey_length"/> 96 </td> 97 </tr> 98 <tr> 99 <th class="col1"> 100 <label for="expire_date">Expiration Date:</label> 101 </th> 102 <td class="col1"> 103 <input type="text" id="expire_date" name="expire_date" 104 value="$keygen.expire_date"/> 105 </td> 106 <th class="col2"> 107 <input type="checkbox" id="allow_usermod" name="allow_usermod" 108 checked="${keygen.allow_usermod and 'checked' or None}"/> 109 </th> 110 <td class="col2"> 111 <label for="allow_usermod"> 112 Allow users to overwrite key generation presets. 113 </label> 114 </td> 115 </tr> 116 </table> 117 </fieldset> 118 <div class="buttons"> 119 <input type="submit" value="${_('Apply changes')}"/> 120 </div> 121 </fieldset> 122 </form> 19 123 </body> 20 124 </html> -
cryptoplugin/trunk/crypto/tests/admin.py
r11810 r11813 32 32 def test_available_actions(self): 33 33 self.failIf('CRYPTO_ADMIN' not in self.perm.get_actions()) 34 self.failIf('CRYPTO_DELETE' not in self.perm.get_actions()) 34 35 35 36 def test_available_actions_no_perms(self): 36 37 self.perm.grant_permission('admin', 'authenticated') 37 38 self.assertFalse(self.perm.check_permission('CRYPTO_ADMIN', 'admin')) 39 self.assertFalse(self.perm.check_permission('CRYPTO_DELETE', 'admin')) 40 41 def test_available_actions_delete_only(self): 42 self.perm.grant_permission('admin', 'CRYPTO_DELETE') 43 self.assertFalse(self.perm.check_permission('CRYPTO_ADMIN', 'admin')) 44 self.assertTrue(self.perm.check_permission('CRYPTO_DELETE', 'admin')) 38 45 39 46 def test_available_actions_full_perms(self): 40 47 self.perm.grant_permission('admin', 'TRAC_ADMIN') 41 48 self.assertTrue(self.perm.check_permission('CRYPTO_ADMIN', 'admin')) 49 self.assertTrue(self.perm.check_permission('CRYPTO_DELETE', 'admin')) 42 50 43 51 -
cryptoplugin/trunk/crypto/tests/web_ui.py
r11812 r11813 23 23 self.env.path = tempfile.mkdtemp() 24 24 25 # CommonTemplateProvider is ratherabstract, test it using a subclass.25 # CommonTemplateProvider is abstract, test it using a subclass. 26 26 self.crypto_up = UserCryptoPreferences(self.env) 27 27 -
cryptoplugin/trunk/crypto/web_ui.py
r11812 r11813 21 21 implements(ITemplateProvider) 22 22 23 abstract = True 24 23 25 # ITemplateProvider methods 24 26 25 27 def get_htdocs_dirs(self): 26 return [ ]28 return [('crypto', resource_filename(__name__, 'htdocs'))] 27 29 28 30 def get_templates_dirs(self): 29 return [resource_filename( 'crypto', 'templates')]31 return [resource_filename(__name__, 'templates')] 30 32 31 33 class UserCryptoPreferences(CommonTemplateProvider): -
cryptoplugin/trunk/setup.py
r11811 r11813 53 53 'crypto.api = crypto.api', 54 54 'crypto.admin = crypto.admin', 55 'crypto.openpgp = crypto.openpgp', 55 56 'crypto.web_ui = crypto.web_ui' 56 57 ]
Note: See TracChangeset
for help on using the changeset viewer.