diff --git a/doc/source/command-objects/identity-provider.rst b/doc/source/command-objects/identity-provider.rst index 47e274dd56..90f0b4942c 100644 --- a/doc/source/command-objects/identity-provider.rst +++ b/doc/source/command-objects/identity-provider.rst @@ -15,10 +15,21 @@ Create new identity provider .. code:: bash os identity provider create + [--remote-id [...] | --remote-id-file ] [--description ] [--enable | --disable] +.. option:: --remote-id + + Remote IDs to associate with the Identity Provider (repeat to provide + multiple values) + +.. option:: --remote-id-file + + Name of a file that contains many remote IDs to associate with the identity + provider, one per line + .. option:: --description New identity provider description @@ -69,9 +80,20 @@ Set identity provider properties .. code:: bash os identity provider set + [--remote-id [...] | --remote-id-file ] [--enable | --disable] +.. option:: --remote-id + + Remote IDs to associate with the Identity Provider (repeat to provide + multiple values) + +.. option:: --remote-id-file + + Name of a file that contains many remote IDs to associate with the identity + provider, one per line + .. option:: --enable Enable the identity provider diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py index 691446da4d..8096580016 100644 --- a/openstackclient/identity/v3/identity_provider.py +++ b/openstackclient/identity/v3/identity_provider.py @@ -35,6 +35,20 @@ class CreateIdentityProvider(show.ShowOne): metavar='', help='New identity provider name (must be unique)' ) + identity_remote_id_provider = parser.add_mutually_exclusive_group() + identity_remote_id_provider.add_argument( + '--remote-id', + metavar='', + action='append', + help='Remote IDs to associate with the Identity Provider ' + '(repeat to provide multiple values)' + ) + identity_remote_id_provider.add_argument( + '--remote-id-file', + metavar='', + help='Name of a file that contains many remote IDs to associate ' + 'with the identity provider, one per line' + ) parser.add_argument( '--description', metavar='', @@ -59,8 +73,17 @@ class CreateIdentityProvider(show.ShowOne): def take_action(self, parsed_args): self.log.debug('take_action(%s)', parsed_args) identity_client = self.app.client_manager.identity + if parsed_args.remote_id_file: + file_content = utils.read_blob_file_contents( + parsed_args.remote_id_file) + remote_ids = file_content.splitlines() + remote_ids = list(map(str.strip, remote_ids)) + else: + remote_ids = (parsed_args.remote_id + if parsed_args.remote_id else None) idp = identity_client.federation.identity_providers.create( id=parsed_args.identity_provider_id, + remote_ids=remote_ids, description=parsed_args.description, enabled=parsed_args.enabled) @@ -119,6 +142,20 @@ class SetIdentityProvider(command.Command): metavar='', help='Identity provider to modify', ) + identity_remote_id_provider = parser.add_mutually_exclusive_group() + identity_remote_id_provider.add_argument( + '--remote-id', + metavar='', + action='append', + help='Remote IDs to associate with the Identity Provider ' + '(repeat to provide multiple values)' + ) + identity_remote_id_provider.add_argument( + '--remote-id-file', + metavar='', + help='Name of a file that contains many remote IDs to associate ' + 'with the identity provider, one per line' + ) enable_identity_provider = parser.add_mutually_exclusive_group() enable_identity_provider.add_argument( '--enable', @@ -136,16 +173,33 @@ class SetIdentityProvider(command.Command): self.log.debug('take_action(%s)', parsed_args) federation_client = self.app.client_manager.identity.federation - if parsed_args.enable is True: - enabled = True - elif parsed_args.disable is True: - enabled = False - else: - self.log.error("No changes requested") + # Basic argument checking + if (not parsed_args.enable and not parsed_args.disable and not + parsed_args.remote_id and not parsed_args.remote_id_file): + self.log.error('No changes requested') return (None, None) + # Always set remote_ids if either is passed in + if parsed_args.remote_id_file: + file_content = utils.read_blob_file_contents( + parsed_args.remote_id_file) + remote_ids = file_content.splitlines() + remote_ids = list(map(str.strip, remote_ids)) + elif parsed_args.remote_id: + remote_ids = parsed_args.remote_id + + # Setup keyword args for the client + kwargs = {} + if parsed_args.enable: + kwargs['enabled'] = True + if parsed_args.disable: + kwargs['enabled'] = False + if parsed_args.remote_id_file or parsed_args.remote_id: + kwargs['remote_ids'] = remote_ids + identity_provider = federation_client.identity_providers.update( - parsed_args.identity_provider, enabled=enabled) + parsed_args.identity_provider, **kwargs) + identity_provider._info.pop('links', None) return zip(*sorted(six.iteritems(identity_provider._info))) diff --git a/openstackclient/tests/identity/v3/fakes.py b/openstackclient/tests/identity/v3/fakes.py index c868401aa1..4c7ef6c16c 100644 --- a/openstackclient/tests/identity/v3/fakes.py +++ b/openstackclient/tests/identity/v3/fakes.py @@ -231,9 +231,11 @@ TOKEN_WITH_DOMAIN_ID = { idp_id = 'test_idp' idp_description = 'super exciting IdP description' +idp_remote_ids = ['entity1', 'entity2'] IDENTITY_PROVIDER = { 'id': idp_id, + 'remote_ids': idp_remote_ids, 'enabled': True, 'description': idp_description } diff --git a/openstackclient/tests/identity/v3/test_identity_provider.py b/openstackclient/tests/identity/v3/test_identity_provider.py index 527f01b581..cd328c1d4c 100644 --- a/openstackclient/tests/identity/v3/test_identity_provider.py +++ b/openstackclient/tests/identity/v3/test_identity_provider.py @@ -14,6 +14,8 @@ import copy +import mock + from openstackclient.identity.v3 import identity_provider from openstackclient.tests import fakes from openstackclient.tests.identity.v3 import fakes as identity_fakes @@ -51,6 +53,7 @@ class TestIdentityProviderCreate(TestIdentityProvider): # Set expected values kwargs = { + 'remote_ids': None, 'enabled': True, 'description': None, } @@ -60,12 +63,13 @@ class TestIdentityProviderCreate(TestIdentityProvider): **kwargs ) - collist = ('description', 'enabled', 'id') + collist = ('description', 'enabled', 'id', 'remote_ids') self.assertEqual(collist, columns) datalist = ( identity_fakes.idp_description, True, identity_fakes.idp_id, + identity_fakes.idp_remote_ids ) self.assertEqual(datalist, data) @@ -83,6 +87,7 @@ class TestIdentityProviderCreate(TestIdentityProvider): # Set expected values kwargs = { + 'remote_ids': None, 'description': identity_fakes.idp_description, 'enabled': True, } @@ -92,12 +97,121 @@ class TestIdentityProviderCreate(TestIdentityProvider): **kwargs ) - collist = ('description', 'enabled', 'id') + collist = ('description', 'enabled', 'id', 'remote_ids') self.assertEqual(collist, columns) datalist = ( identity_fakes.idp_description, True, identity_fakes.idp_id, + identity_fakes.idp_remote_ids + ) + self.assertEqual(datalist, data) + + def test_create_identity_provider_remote_id(self): + arglist = [ + identity_fakes.idp_id, + '--remote-id', identity_fakes.idp_remote_ids[0] + ] + verifylist = [ + ('identity_provider_id', identity_fakes.idp_id), + ('remote_id', identity_fakes.idp_remote_ids[:1]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'remote_ids': identity_fakes.idp_remote_ids[:1], + 'description': None, + 'enabled': True, + } + + self.identity_providers_mock.create.assert_called_with( + id=identity_fakes.idp_id, + **kwargs + ) + + collist = ('description', 'enabled', 'id', 'remote_ids') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.idp_description, + True, + identity_fakes.idp_id, + identity_fakes.idp_remote_ids + ) + self.assertEqual(datalist, data) + + def test_create_identity_provider_remote_ids_multiple(self): + arglist = [ + '--remote-id', identity_fakes.idp_remote_ids[0], + '--remote-id', identity_fakes.idp_remote_ids[1], + identity_fakes.idp_id + ] + verifylist = [ + ('identity_provider_id', identity_fakes.idp_id), + ('remote_id', identity_fakes.idp_remote_ids), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'remote_ids': identity_fakes.idp_remote_ids, + 'description': None, + 'enabled': True, + } + + self.identity_providers_mock.create.assert_called_with( + id=identity_fakes.idp_id, + **kwargs + ) + + collist = ('description', 'enabled', 'id', 'remote_ids') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.idp_description, + True, + identity_fakes.idp_id, + identity_fakes.idp_remote_ids + ) + self.assertEqual(datalist, data) + + def test_create_identity_provider_remote_ids_file(self): + arglist = [ + '--remote-id-file', '/tmp/file_name', + identity_fakes.idp_id, + ] + verifylist = [ + ('identity_provider_id', identity_fakes.idp_id), + ('remote_id_file', '/tmp/file_name'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + mocker = mock.Mock() + mocker.return_value = "\n".join(identity_fakes.idp_remote_ids) + with mock.patch("openstackclient.identity.v3.identity_provider." + "utils.read_blob_file_contents", mocker): + columns, data = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'remote_ids': identity_fakes.idp_remote_ids, + 'description': None, + 'enabled': True, + } + + self.identity_providers_mock.create.assert_called_with( + id=identity_fakes.idp_id, + **kwargs + ) + + collist = ('description', 'enabled', 'id', 'remote_ids') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.idp_description, + True, + identity_fakes.idp_id, + identity_fakes.idp_remote_ids ) self.assertEqual(datalist, data) @@ -123,6 +237,7 @@ class TestIdentityProviderCreate(TestIdentityProvider): # Set expected values kwargs = { + 'remote_ids': None, 'enabled': False, 'description': None, } @@ -132,12 +247,13 @@ class TestIdentityProviderCreate(TestIdentityProvider): **kwargs ) - collist = ('description', 'enabled', 'id') + collist = ('description', 'enabled', 'id', 'remote_ids') self.assertEqual(collist, columns) datalist = ( None, False, identity_fakes.idp_id, + identity_fakes.idp_remote_ids ) self.assertEqual(datalist, data) @@ -241,12 +357,13 @@ class TestIdentityProviderShow(TestIdentityProvider): identity_fakes.idp_id, ) - collist = ('description', 'enabled', 'id') + collist = ('description', 'enabled', 'id', 'remote_ids') self.assertEqual(collist, columns) datalist = ( identity_fakes.idp_description, True, identity_fakes.idp_id, + identity_fakes.idp_remote_ids ) self.assertEqual(datalist, data) @@ -276,11 +393,14 @@ class TestIdentityProviderSet(TestIdentityProvider): prepare(self) arglist = [ '--disable', identity_fakes.idp_id, + '--remote-id', identity_fakes.idp_remote_ids[0], + '--remote-id', identity_fakes.idp_remote_ids[1] ] verifylist = [ ('identity_provider', identity_fakes.idp_id), ('enable', False), ('disable', True), + ('remote_id', identity_fakes.idp_remote_ids) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -288,14 +408,16 @@ class TestIdentityProviderSet(TestIdentityProvider): self.identity_providers_mock.update.assert_called_with( identity_fakes.idp_id, enabled=False, + remote_ids=identity_fakes.idp_remote_ids ) - collist = ('description', 'enabled', 'id') + collist = ('description', 'enabled', 'id', 'remote_ids') self.assertEqual(collist, columns) datalist = ( identity_fakes.idp_description, False, identity_fakes.idp_id, + identity_fakes.idp_remote_ids ) self.assertEqual(datalist, data) @@ -316,23 +438,122 @@ class TestIdentityProviderSet(TestIdentityProvider): prepare(self) arglist = [ '--enable', identity_fakes.idp_id, + '--remote-id', identity_fakes.idp_remote_ids[0], + '--remote-id', identity_fakes.idp_remote_ids[1] ] verifylist = [ ('identity_provider', identity_fakes.idp_id), ('enable', True), ('disable', False), + ('remote_id', identity_fakes.idp_remote_ids) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.identity_providers_mock.update.assert_called_with( - identity_fakes.idp_id, enabled=True) - collist = ('description', 'enabled', 'id') + identity_fakes.idp_id, enabled=True, + remote_ids=identity_fakes.idp_remote_ids) + collist = ('description', 'enabled', 'id', 'remote_ids') self.assertEqual(collist, columns) datalist = ( identity_fakes.idp_description, True, identity_fakes.idp_id, + identity_fakes.idp_remote_ids + ) + self.assertEqual(datalist, data) + + def test_identity_provider_replace_remote_ids(self): + """Enable Identity Provider. + + Set Identity Provider's ``enabled`` attribute to True. + """ + def prepare(self): + """Prepare fake return objects before the test is executed""" + self.new_remote_id = 'new_entity' + + updated_idp = copy.deepcopy(identity_fakes.IDENTITY_PROVIDER) + updated_idp['remote_ids'] = [self.new_remote_id] + resources = fakes.FakeResource( + None, + updated_idp, + loaded=True + ) + self.identity_providers_mock.update.return_value = resources + + prepare(self) + arglist = [ + '--enable', identity_fakes.idp_id, + '--remote-id', self.new_remote_id + ] + verifylist = [ + ('identity_provider', identity_fakes.idp_id), + ('enable', True), + ('disable', False), + ('remote_id', [self.new_remote_id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.identity_providers_mock.update.assert_called_with( + identity_fakes.idp_id, enabled=True, + remote_ids=[self.new_remote_id]) + collist = ('description', 'enabled', 'id', 'remote_ids') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.idp_description, + True, + identity_fakes.idp_id, + [self.new_remote_id] + ) + self.assertEqual(datalist, data) + + def test_identity_provider_replace_remote_ids_file(self): + """Enable Identity Provider. + + Set Identity Provider's ``enabled`` attribute to True. + """ + def prepare(self): + """Prepare fake return objects before the test is executed""" + self.new_remote_id = 'new_entity' + + updated_idp = copy.deepcopy(identity_fakes.IDENTITY_PROVIDER) + updated_idp['remote_ids'] = [self.new_remote_id] + resources = fakes.FakeResource( + None, + updated_idp, + loaded=True + ) + self.identity_providers_mock.update.return_value = resources + + prepare(self) + arglist = [ + '--enable', identity_fakes.idp_id, + '--remote-id-file', self.new_remote_id, + ] + verifylist = [ + ('identity_provider', identity_fakes.idp_id), + ('enable', True), + ('disable', False), + ('remote_id_file', self.new_remote_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + mocker = mock.Mock() + mocker.return_value = self.new_remote_id + with mock.patch("openstackclient.identity.v3.identity_provider." + "utils.read_blob_file_contents", mocker): + columns, data = self.cmd.take_action(parsed_args) + self.identity_providers_mock.update.assert_called_with( + identity_fakes.idp_id, enabled=True, + remote_ids=[self.new_remote_id]) + collist = ('description', 'enabled', 'id', 'remote_ids') + self.assertEqual(collist, columns) + datalist = ( + identity_fakes.idp_description, + True, + identity_fakes.idp_id, + [self.new_remote_id] ) self.assertEqual(datalist, data) @@ -361,6 +582,7 @@ class TestIdentityProviderSet(TestIdentityProvider): ('identity_provider', identity_fakes.idp_id), ('enable', False), ('disable', False), + ('remote_id', None) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist)