Skip to content

Commit 167cf11

Browse files
committed
Add authorization_ttl for identity providers
this is supported since Ussuri (Keystone API version 3.14) but was lacking from openstackclient. Change-Id: Ifac818b9a4eff66d9a68455ada1ddfe67cb46b3b
1 parent e49ad17 commit 167cf11

3 files changed

Lines changed: 223 additions & 1 deletion

File tree

openstackclient/identity/v3/identity_provider.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ def get_parser(self, prog_name):
6363
'specified, a domain will be created automatically. '
6464
'(Name or ID)'),
6565
)
66+
parser.add_argument(
67+
'--authorization-ttl',
68+
metavar='<authorization-ttl>',
69+
type=int,
70+
help=_('Time to keep the role assignments for users '
71+
'authenticating via this identity provider. '
72+
'When not provided, global default configured in the '
73+
'Identity service will be used. '
74+
'Available since Identity API version 3.14 (Ussuri).'),
75+
)
6676
enable_identity_provider = parser.add_mutually_exclusive_group()
6777
enable_identity_provider.add_argument(
6878
'--enable',
@@ -95,12 +105,23 @@ def take_action(self, parsed_args):
95105
domain_id = common.find_domain(identity_client,
96106
parsed_args.domain).id
97107

108+
# TODO(pas-ha) actually check for 3.14 microversion
109+
kwargs = {}
110+
auth_ttl = parsed_args.authorization_ttl
111+
if auth_ttl is not None:
112+
if auth_ttl < 0:
113+
msg = (_("%(param)s must be positive integer or zero."
114+
) % {"param": "authorization-ttl"})
115+
raise exceptions.CommandError(msg)
116+
kwargs['authorization_ttl'] = auth_ttl
117+
98118
idp = identity_client.federation.identity_providers.create(
99119
id=parsed_args.identity_provider_id,
100120
remote_ids=remote_ids,
101121
description=parsed_args.description,
102122
domain_id=domain_id,
103-
enabled=parsed_args.enabled)
123+
enabled=parsed_args.enabled,
124+
**kwargs)
104125

105126
idp._info.pop('links', None)
106127
remote_ids = format_columns.ListColumn(idp._info.pop('remote_ids', []))
@@ -205,6 +226,14 @@ def get_parser(self, prog_name):
205226
help=_('Name of a file that contains many remote IDs to associate '
206227
'with the identity provider, one per line'),
207228
)
229+
parser.add_argument(
230+
'--authorization-ttl',
231+
metavar='<authorization-ttl>',
232+
type=int,
233+
help=_('Time to keep the role assignments for users '
234+
'authenticating via this identity provider. '
235+
'Available since Identity API version 3.14 (Ussuri).'),
236+
)
208237
enable_identity_provider = parser.add_mutually_exclusive_group()
209238
enable_identity_provider.add_argument(
210239
'--enable',
@@ -241,6 +270,20 @@ def take_action(self, parsed_args):
241270
if parsed_args.remote_id_file or parsed_args.remote_id:
242271
kwargs['remote_ids'] = remote_ids
243272

273+
# TODO(pas-ha) actually check for 3.14 microversion
274+
# TODO(pas-ha) make it possible to reset authorization_ttl
275+
# back to None value.
276+
# Currently not possible as filter_kwargs decorator in
277+
# keystoneclient/base.py explicitly drops the None-valued keys
278+
# from kwargs, and 'update' method is wrapped in this decorator.
279+
auth_ttl = parsed_args.authorization_ttl
280+
if auth_ttl is not None:
281+
if auth_ttl < 0:
282+
msg = (_("%(param)s must be positive integer or zero."
283+
) % {"param": "authorization-ttl"})
284+
raise exceptions.CommandError(msg)
285+
kwargs['authorization_ttl'] = auth_ttl
286+
244287
federation_client.identity_providers.update(
245288
parsed_args.identity_provider,
246289
**kwargs

openstackclient/tests/unit/identity/v3/test_identity_provider.py

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
1515
import copy
1616
from unittest import mock
1717

18+
from osc_lib import exceptions
19+
1820
from openstackclient.identity.v3 import identity_provider
1921
from openstackclient.tests.unit import fakes
2022
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
23+
from openstackclient.tests.unit import utils as test_utils
2124

2225

2326
class TestIdentityProvider(identity_fakes.TestFederatedIdentity):
@@ -308,6 +311,86 @@ def test_create_identity_provider_domain_id(self):
308311
self.assertEqual(self.columns, columns)
309312
self.assertCountEqual(self.datalist, data)
310313

314+
def test_create_identity_provider_authttl_positive(self):
315+
arglist = [
316+
'--authorization-ttl', '60',
317+
identity_fakes.idp_id,
318+
]
319+
verifylist = [
320+
('identity_provider_id', identity_fakes.idp_id),
321+
('authorization_ttl', 60),
322+
]
323+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
324+
columns, data = self.cmd.take_action(parsed_args)
325+
326+
# Set expected values
327+
kwargs = {
328+
'remote_ids': None,
329+
'description': None,
330+
'domain_id': None,
331+
'enabled': True,
332+
'authorization_ttl': 60,
333+
}
334+
335+
self.identity_providers_mock.create.assert_called_with(
336+
id=identity_fakes.idp_id,
337+
**kwargs
338+
)
339+
340+
self.assertEqual(self.columns, columns)
341+
self.assertCountEqual(self.datalist, data)
342+
343+
def test_create_identity_provider_authttl_zero(self):
344+
arglist = [
345+
'--authorization-ttl', '0',
346+
identity_fakes.idp_id,
347+
]
348+
verifylist = [
349+
('identity_provider_id', identity_fakes.idp_id),
350+
('authorization_ttl', 0),
351+
]
352+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
353+
columns, data = self.cmd.take_action(parsed_args)
354+
355+
# Set expected values
356+
kwargs = {
357+
'remote_ids': None,
358+
'description': None,
359+
'domain_id': None,
360+
'enabled': True,
361+
'authorization_ttl': 0,
362+
}
363+
364+
self.identity_providers_mock.create.assert_called_with(
365+
id=identity_fakes.idp_id,
366+
**kwargs
367+
)
368+
369+
self.assertEqual(self.columns, columns)
370+
self.assertCountEqual(self.datalist, data)
371+
372+
def test_create_identity_provider_authttl_negative(self):
373+
arglist = [
374+
'--authorization-ttl', '-60',
375+
identity_fakes.idp_id,
376+
]
377+
verifylist = [
378+
('identity_provider_id', identity_fakes.idp_id),
379+
('authorization_ttl', -60),
380+
]
381+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
382+
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
383+
parsed_args)
384+
385+
def test_create_identity_provider_authttl_not_int(self):
386+
arglist = [
387+
'--authorization-ttl', 'spam',
388+
identity_fakes.idp_id,
389+
]
390+
verifylist = []
391+
self.assertRaises(test_utils.ParserException, self.check_parser,
392+
self.cmd, arglist, verifylist)
393+
311394

312395
class TestIdentityProviderDelete(TestIdentityProvider):
313396

@@ -678,6 +761,93 @@ def prepare(self):
678761

679762
self.cmd.take_action(parsed_args)
680763

764+
def test_identity_provider_set_authttl_positive(self):
765+
def prepare(self):
766+
"""Prepare fake return objects before the test is executed"""
767+
updated_idp = copy.deepcopy(identity_fakes.IDENTITY_PROVIDER)
768+
updated_idp['authorization_ttl'] = 60
769+
resources = fakes.FakeResource(
770+
None,
771+
updated_idp,
772+
loaded=True
773+
)
774+
self.identity_providers_mock.update.return_value = resources
775+
776+
prepare(self)
777+
arglist = [
778+
'--authorization-ttl', '60',
779+
identity_fakes.idp_id
780+
]
781+
verifylist = [
782+
('identity_provider', identity_fakes.idp_id),
783+
('enable', False),
784+
('disable', False),
785+
('remote_id', None),
786+
('authorization_ttl', 60),
787+
]
788+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
789+
self.cmd.take_action(parsed_args)
790+
self.identity_providers_mock.update.assert_called_with(
791+
identity_fakes.idp_id,
792+
authorization_ttl=60,
793+
)
794+
795+
def test_identity_provider_set_authttl_zero(self):
796+
def prepare(self):
797+
"""Prepare fake return objects before the test is executed"""
798+
updated_idp = copy.deepcopy(identity_fakes.IDENTITY_PROVIDER)
799+
updated_idp['authorization_ttl'] = 0
800+
resources = fakes.FakeResource(
801+
None,
802+
updated_idp,
803+
loaded=True
804+
)
805+
self.identity_providers_mock.update.return_value = resources
806+
807+
prepare(self)
808+
arglist = [
809+
'--authorization-ttl', '0',
810+
identity_fakes.idp_id
811+
]
812+
verifylist = [
813+
('identity_provider', identity_fakes.idp_id),
814+
('enable', False),
815+
('disable', False),
816+
('remote_id', None),
817+
('authorization_ttl', 0),
818+
]
819+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
820+
self.cmd.take_action(parsed_args)
821+
self.identity_providers_mock.update.assert_called_with(
822+
identity_fakes.idp_id,
823+
authorization_ttl=0,
824+
)
825+
826+
def test_identity_provider_set_authttl_negative(self):
827+
arglist = [
828+
'--authorization-ttl', '-1',
829+
identity_fakes.idp_id
830+
]
831+
verifylist = [
832+
('identity_provider', identity_fakes.idp_id),
833+
('enable', False),
834+
('disable', False),
835+
('remote_id', None),
836+
('authorization_ttl', -1),
837+
]
838+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
839+
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
840+
parsed_args)
841+
842+
def test_identity_provider_set_authttl_not_int(self):
843+
arglist = [
844+
'--authorization-ttl', 'spam',
845+
identity_fakes.idp_id
846+
]
847+
verifylist = []
848+
self.assertRaises(test_utils.ParserException, self.check_parser,
849+
self.cmd, arglist, verifylist)
850+
681851

682852
class TestIdentityProviderShow(TestIdentityProvider):
683853

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
features:
3+
- |
4+
``identity provider create`` and ``identity provider set`` commands now
5+
accept the ``--authorization-ttl <VALUE>`` argument, with ``<VALUE>``
6+
being a non-negative integer.
7+
8+
See `note <https://docs.openstack.org/keystone/latest/admin/federation/configure_federation.html#create-a-mapping>`_
9+
in Keystone documentations for more details on the meaning of this option.

0 commit comments

Comments
 (0)