diff --git a/barbicanclient/barbican.py b/barbicanclient/barbican.py index eac42835..6ae37520 100644 --- a/barbicanclient/barbican.py +++ b/barbicanclient/barbican.py @@ -23,6 +23,7 @@ from cliff import app from cliff import commandmanager from keystoneclient.auth import identity from keystoneclient import session +import six from barbicanclient import client from barbicanclient import version @@ -110,6 +111,10 @@ class Barbican(app.App): metavar='', default=client.env('OS_PROJECT_DOMAIN_NAME'), help='Defaults to env[OS_PROJECT_DOMAIN_NAME].') + parser.add_argument('--os-auth-token', + metavar='', + default=client.env('OS_AUTH_TOKEN'), + help='Defaults to env[OS_AUTH_TOKEN].') parser.add_argument('--endpoint', '-E', metavar='', default=client.env('BARBICAN_ENDPOINT'), @@ -123,6 +128,63 @@ class Barbican(app.App): raise Exception("ERROR: argument --os-auth-url/-A: not allowed " "with argument --no-auth/-N") + def _check_auth_arguments(self, args, api_version=None, raise_exc=False): + """Verifies that we have the correct arguments for authentication + + Supported Keystone v3 combinations: + - Project Id + - Project Name + Project Domain Name + - Project Name + Project Domain Id + Support Keystone v2 combinations: + - Tenant Id + - Tenant Name + """ + successful = True + v3_arg_combinations = [ + args.os_project_id, + args.os_project_name and args.os_project_domain_name, + args.os_project_name and args.os_project_domain_id + ] + v2_arg_combinations = [args.os_tenant_id, args.os_tenant_name] + + # Keystone V3 + if not api_version or api_version == _DEFAULT_IDENTITY_API_VERSION: + if not any(v3_arg_combinations): + msg = ('ERROR: please specify the following --os-project-id or' + '--os-project-name and --os-project-domain-name or ' + '--os-project-name and --os-project-domain-id') + successful = False + # Keystone V2 + else: + if not any(v2_arg_combinations): + msg = ('ERROR: please specify --os-tenant-id or' + '--os-tenant-name') + successful = False + + if not successful and raise_exc: + raise Exception(msg) + + return successful + + def _build_kwargs_based_on_version(self, args, api_version=None): + if not api_version or api_version == _DEFAULT_IDENTITY_API_VERSION: + kwargs = { + 'project_id': args.os_project_id, + 'project_name': args.os_project_name, + 'user_domain_id': args.os_user_domain_id, + 'user_domain_name': args.os_user_domain_name, + 'project_domain_id': args.os_project_id, + 'project_domain_name': args.os_project_name + } + else: + kwargs = { + 'tenant_name': args.os_tenant_name, + 'tenant_id': args.os_tenant_id + } + + # Return a dictionary with only the populated (not None) values + return dict((k, v) for (k, v) in six.iteritems(kwargs) if v) + def initialize_app(self, argv): """Initializes the application. Checks if the minimal parameters are provided and creates the client @@ -132,6 +194,11 @@ class Barbican(app.App): args = self.options self._assert_no_auth_and_auth_url_mutually_exclusive(args.no_auth, args.os_auth_url) + + # Aliasing as we use this a number of times + api_version = args.os_identity_api_version + + # TODO(jmvrbanac): Split out these conditionals into discrete functions if args.no_auth: if not all([args.endpoint, args.os_tenant_id or args.os_project_id]): @@ -142,6 +209,31 @@ class Barbican(app.App): project_id=args.os_tenant_id or args.os_project_id, verify=not args.insecure) + # Token-based authentication + elif args.os_auth_token: + if not args.os_auth_url: + raise Exception('ERROR: please specify --os-auth-url') + + # Make sure we have the correct arguments to function + self._check_auth_arguments(args, api_version, raise_exc=True) + + kwargs = self._build_kwargs_based_on_version(args, api_version) + kwargs.update({ + 'auth_url': args.os_auth_url, + 'token': args.os_auth_token + }) + + if not api_version or api_version == _DEFAULT_IDENTITY_API_VERSION: + auth = identity.v3.Token(**kwargs) + else: + auth = identity.v2.Token(**kwargs) + + ks_session = session.Session(auth=auth, verify=not args.insecure) + self.client = client.Client( + session=ks_session, + endpoint=args.endpoint + ) + # Password-based authentication elif all([args.os_auth_url, args.os_user_id or args.os_username, args.os_password, args.os_tenant_name or args.os_tenant_id or args.os_project_name or args.os_project_id]): @@ -153,8 +245,6 @@ class Barbican(app.App): if args.os_username: kwargs['username'] = args.os_username - api_version = args.os_identity_api_version - if not api_version or api_version == _DEFAULT_IDENTITY_API_VERSION: if args.os_project_id: kwargs['project_id'] = args.os_project_id diff --git a/barbicanclient/tests/test_barbican.py b/barbicanclient/tests/test_barbican.py index f1b11b03..251d174b 100644 --- a/barbicanclient/tests/test_barbican.py +++ b/barbicanclient/tests/test_barbican.py @@ -133,3 +133,15 @@ class TestBarbicanWithKeystonePasswordAuth( '--os-username': 'some_user', '--os-password': 'some_pass', } + + +class TestBarbicanWithKeystoneTokenAuth( + keystone_client_fixtures.KeystoneClientFixture): + + def setUp(self): + super(TestBarbicanWithKeystoneTokenAuth, self).setUp() + + self.test_arguments = { + '--os-auth-token': 'some_token', + '--os-project-id': 'some_project_id', + } diff --git a/doc/source/authentication.rst b/doc/source/authentication.rst index f5a47dd5..d3752058 100644 --- a/doc/source/authentication.rst +++ b/doc/source/authentication.rst @@ -81,8 +81,7 @@ credentials can be passed to Barbican via arguments. $ barbican --os-auth-url --os-project-domain-id \ --os-user-domain-id --os-username \ - --os-password --os-project-name --endpoint \ - secret list + --os-password --os-project-name secret list This can become annoying and tedious, so authentication via Keystone can also be configured by setting environment variables. Barbican uses the same env @@ -127,6 +126,21 @@ each login: echo "source ~/clientrc" >> ~/.bashrc +Keystone Token Authentication +----------------------------- + +Barbican can be configured to use Keystone tokens for authentication. The +user's credentials can be passed to Barbican via arguments. + +.. code-block:: bash + + $ barbican --os-auth-url --os-auth-token \ + --os-project-id secret list + +Much like normal password authentication you can specify these values via +environmental variables. Refer to `Keystone V3 authentication`_ for more +information. + No Auth Mode ------------ diff --git a/doc/source/cli_usage.rst b/doc/source/cli_usage.rst index 98a177f4..3aa99300 100644 --- a/doc/source/cli_usage.rst +++ b/doc/source/cli_usage.rst @@ -1,5 +1,5 @@ -Usage -===== +CLI Usage +========= .. code-block:: bash @@ -15,6 +15,7 @@ Usage [--os-project-name ] [--os-project-domain-id ] [--os-project-domain-name ] + [--os-auth-token ] [--endpoint ] [--insecure] [--os-cacert ] [--os-cert ] [--os-key ] [--timeout ] @@ -168,4 +169,4 @@ Secret List | Secret href | Name | Created | Status | Content types | Algorithm | Bit length | Mode | Expiration | +-----------------------------------------------------------------------+------+----------------------------------+--------+-------------------------------------------+-----------+------------+------+------------+ | http://localhost:9311/v1/secrets/bb3d8c20-8ea5-4bfc-9645-c8da79c8b371 | None | 2015-04-15 20:37:37.501475+00:00 | ACTIVE | {u'default': u'application/octet-stream'} | aes | 256 | cbc | None | - +-----------------------------------------------------------------------+------+----------------------------------+--------+-------------------------------------------+-----------+------------+------+------------+ \ No newline at end of file + +-----------------------------------------------------------------------+------+----------------------------------+--------+-------------------------------------------+-----------+------------+------+------------+ diff --git a/doc/source/index.rst b/doc/source/index.rst index a6ee632d..0bc240cf 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -18,6 +18,7 @@ Contents: installation authentication usage + cli_usage reference contributing