Keyring support for openstackclient.
Bug: 1030440 If password is defined in keyring, use it; otherwise, prompt for the password. Keying is configured using command line switch, --os-use-keyring or env(OS_USE_KEYRING). * openstackclient/common/openstackkeyring.py The abstract class for keyring, specifically for openstack. The class is used to store encrypted password in keyring, without prompting for keyring password. The encrypted password is stored in ~/.openstack-keyring.cfg file. * openstack-common.py Update openstackkeyring library from openstack.common. * openstackclient/shell.py OpenStackClient.build_option_parser(): New boolean argument, --os-use-keyring, default to env(OS_USE_KEYRING). OpenStackClient.authenticate_user(): Get password from keyring, if it is defined; otherwise, prompt for the password. If user enter a password and keyring is enabled, store it in keyring. OpenStackClient.init_keyring_backend(): New method to define openstack backend for keyring. OpenStackClient.get_password_from_keyring(): New method to get password from keyring. OpenStackClient.set_password_in_keyring(): New method go set password in keyring. * toos/pip-requires Define keyring and pycrypto as one of dependent. Change-Id: I36d3a63054658c0ef0553d68b38fefbc236930ef
This commit is contained in:
		| @@ -1,7 +1,7 @@ | ||||
| [DEFAULT] | ||||
|  | ||||
| # The list of modules to copy from openstack-common | ||||
| modules=setup | ||||
| modules=setup,openstackkeyring | ||||
|  | ||||
| # The base module to hold the copy of openstack.common | ||||
| base=openstackclient | ||||
|   | ||||
							
								
								
									
										65
									
								
								openstackclient/common/openstackkeyring.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								openstackclient/common/openstackkeyring.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| # Copyright 2011 OpenStack LLC. | ||||
| # All Rights Reserved | ||||
| # | ||||
| #    Licensed under the Apache License, Version 2.0 (the "License"); you may | ||||
| #    not use this file except in compliance with the License. You may obtain | ||||
| #    a copy of the License at | ||||
| # | ||||
| #         http://www.apache.org/licenses/LICENSE-2.0 | ||||
| # | ||||
| #    Unless required by applicable law or agreed to in writing, software | ||||
| #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
| #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
| #    License for the specific language governing permissions and limitations | ||||
| #    under the License. | ||||
| # | ||||
| # vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||||
|  | ||||
| """ | ||||
| Keyring backend for Openstack, to store encrypted password in a file. | ||||
| """ | ||||
|  | ||||
| from Crypto.Cipher import AES | ||||
|  | ||||
| import crypt | ||||
| import keyring | ||||
| import os | ||||
|  | ||||
| KEYRING_FILE = os.path.join(os.path.expanduser('~'), '.openstack-keyring.cfg') | ||||
|  | ||||
|  | ||||
| class OpenstackKeyring(keyring.backend.BasicFileKeyring): | ||||
|     """ Openstack Keyring to store encrypted password """ | ||||
|  | ||||
|     filename = KEYRING_FILE | ||||
|  | ||||
|     def supported(self): | ||||
|         """ applicable for all platforms, but not recommend """ | ||||
|         pass | ||||
|  | ||||
|     def _init_crypter(self): | ||||
|         """ initialize the crypter using the class name """ | ||||
|         block_size = 32 | ||||
|         padding = '0' | ||||
|  | ||||
|         # init the cipher with the class name, upto block_size | ||||
|         password = __name__[block_size:] | ||||
|         password = password + (block_size - len(password) % \ | ||||
|                               block_size) * padding | ||||
|         return AES.new(password, AES.MODE_CFB) | ||||
|  | ||||
|     def encrypt(self, password): | ||||
|         """ encrypt the given password """ | ||||
|         crypter = self._init_crypter() | ||||
|         return crypter.encrypt(password) | ||||
|  | ||||
|     def decrypt(self, password_encrypted): | ||||
|         """ decrypt the given password """ | ||||
|         crypter = self._init_crypter() | ||||
|         return crypter.decrypt(password_encrypted) | ||||
|  | ||||
|  | ||||
| def os_keyring(): | ||||
|     """ initialize the openstack keyring """ | ||||
|     return keyring.core.load_keyring(None, | ||||
|              'openstackclient.common.openstackkeyring.OpenstackKeyring') | ||||
| @@ -29,10 +29,12 @@ from cliff.commandmanager import CommandManager | ||||
|  | ||||
| from openstackclient.common import clientmanager | ||||
| from openstackclient.common import exceptions as exc | ||||
| from openstackclient.common import openstackkeyring | ||||
| from openstackclient.common import utils | ||||
|  | ||||
|  | ||||
| VERSION = '0.1' | ||||
| KEYRING_SERVICE = 'openstack' | ||||
|  | ||||
|  | ||||
| def env(*vars, **kwargs): | ||||
| @@ -123,6 +125,18 @@ class OpenStackShell(App): | ||||
|             default=env('OS_URL'), | ||||
|             help='Defaults to env[OS_URL]') | ||||
|  | ||||
|         env_os_keyring = env('OS_USE_KEYRING', default=False) | ||||
|         if type(env_os_keyring) == str: | ||||
|             if env_os_keyring.lower() in ['true', '1']: | ||||
|                 env_os_keyring = True | ||||
|             else: | ||||
|                 env_os_keyring = False | ||||
|         parser.add_argument('--os-use-keyring', | ||||
|                             default=env_os_keyring, | ||||
|                             action='store_true', | ||||
|                             help='Use keyring to store password, ' | ||||
|                                  'default=False (Env: OS_USE_KEYRING)') | ||||
|  | ||||
|         return parser | ||||
|  | ||||
|     def authenticate_user(self): | ||||
| @@ -149,12 +163,14 @@ class OpenStackShell(App): | ||||
|                     "You must provide a username via" | ||||
|                     " either --os-username or env[OS_USERNAME]") | ||||
|  | ||||
|             self.get_password_from_keyring() | ||||
|             if not self.options.os_password: | ||||
|                 # No password, if we've got a tty, try prompting for it | ||||
|                 if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty(): | ||||
|                     # Check for Ctl-D | ||||
|                     try: | ||||
|                         self.options.os_password = getpass.getpass() | ||||
|                         self.set_password_in_keyring() | ||||
|                     except EOFError: | ||||
|                         pass | ||||
|                 # No password because we did't have a tty or the | ||||
| @@ -188,6 +204,34 @@ class OpenStackShell(App): | ||||
|             ) | ||||
|         return | ||||
|  | ||||
|     def init_keyring_backend(self): | ||||
|         """Initialize openstack backend to use for keyring""" | ||||
|         return openstackkeyring.os_keyring() | ||||
|  | ||||
|     def get_password_from_keyring(self): | ||||
|         """Get password from keyring, if it's set""" | ||||
|         if self.options.os_use_keyring: | ||||
|             service = KEYRING_SERVICE | ||||
|             backend = self.init_keyring_backend() | ||||
|             if not self.options.os_password: | ||||
|                 password = backend.get_password(service, | ||||
|                                                 self.options.os_username) | ||||
|                 self.options.os_password = password | ||||
|  | ||||
|     def set_password_in_keyring(self): | ||||
|         """Set password in keyring for this user""" | ||||
|         if self.options.os_use_keyring: | ||||
|             service = KEYRING_SERVICE | ||||
|             backend = self.init_keyring_backend() | ||||
|             if self.options.os_password: | ||||
|                 password = backend.get_password(service, | ||||
|                                                 self.options.os_username) | ||||
|                 # either password is not set in keyring, or it is different | ||||
|                 if password != self.options.os_password: | ||||
|                     backend.set_password(service, | ||||
|                                          self.options.os_username, | ||||
|                                          self.options.os_password) | ||||
|  | ||||
|     def initialize_app(self, argv): | ||||
|         """Global app init bits: | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,9 @@ | ||||
| cliff | ||||
| argparse | ||||
| httplib2 | ||||
| keyring | ||||
| prettytable | ||||
| pycrypto | ||||
| python-keystoneclient>=0.1,<0.2 | ||||
| python-novaclient>=2,<3 | ||||
| simplejson | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Bhuvan Arumugam
					Bhuvan Arumugam