1#   Copyright 2018 SUSE Linux GmbH
2#
3#   Licensed under the Apache License, Version 2.0 (the "License"); you may
4#   not use this file except in compliance with the License. You may obtain
5#   a copy of the License at
6#
7#        http://www.apache.org/licenses/LICENSE-2.0
8#
9#   Unless required by applicable law or agreed to in writing, software
10#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12#   License for the specific language governing permissions and limitations
13#   under the License.
14#
15
16import copy
17import json
18from unittest import mock
19
20from osc_lib import exceptions
21from osc_lib import utils
22
23from openstackclient.identity.v3 import application_credential
24from openstackclient.tests.unit import fakes
25from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
26
27
28class TestApplicationCredential(identity_fakes.TestIdentityv3):
29
30    def setUp(self):
31        super(TestApplicationCredential, self).setUp()
32
33        identity_manager = self.app.client_manager.identity
34        self.app_creds_mock = identity_manager.application_credentials
35        self.app_creds_mock.reset_mock()
36        self.roles_mock = identity_manager.roles
37        self.roles_mock.reset_mock()
38
39
40class TestApplicationCredentialCreate(TestApplicationCredential):
41
42    def setUp(self):
43        super(TestApplicationCredentialCreate, self).setUp()
44
45        self.roles_mock.get.return_value = fakes.FakeResource(
46            None,
47            copy.deepcopy(identity_fakes.ROLE),
48            loaded=True,
49        )
50
51        # Get the command object to test
52        self.cmd = application_credential.CreateApplicationCredential(
53            self.app, None)
54
55    def test_application_credential_create_basic(self):
56        self.app_creds_mock.create.return_value = fakes.FakeResource(
57            None,
58            copy.deepcopy(identity_fakes.APP_CRED_BASIC),
59            loaded=True,
60        )
61
62        name = identity_fakes.app_cred_name
63        arglist = [
64            name
65        ]
66        verifylist = [
67            ('name', identity_fakes.app_cred_name)
68        ]
69        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
70
71        # In base command class ShowOne in cliff, abstract method take_action()
72        # returns a two-part tuple with a tuple of column names and a tuple of
73        # data to be shown.
74        columns, data = self.cmd.take_action(parsed_args)
75
76        # Set expected values
77        kwargs = {
78            'secret': None,
79            'roles': [],
80            'expires_at': None,
81            'description': None,
82            'unrestricted': False,
83            'access_rules': None,
84        }
85        self.app_creds_mock.create.assert_called_with(
86            name,
87            **kwargs
88        )
89
90        collist = ('access_rules', 'description', 'expires_at', 'id', 'name',
91                   'project_id', 'roles', 'secret', 'unrestricted')
92        self.assertEqual(collist, columns)
93        datalist = (
94            None,
95            None,
96            None,
97            identity_fakes.app_cred_id,
98            identity_fakes.app_cred_name,
99            identity_fakes.project_id,
100            identity_fakes.role_name,
101            identity_fakes.app_cred_secret,
102            False,
103        )
104        self.assertEqual(datalist, data)
105
106    def test_application_credential_create_with_options(self):
107        name = identity_fakes.app_cred_name
108        self.app_creds_mock.create.return_value = fakes.FakeResource(
109            None,
110            copy.deepcopy(identity_fakes.APP_CRED_OPTIONS),
111            loaded=True,
112        )
113
114        arglist = [
115            name,
116            '--secret', 'moresecuresecret',
117            '--role', identity_fakes.role_id,
118            '--expiration', identity_fakes.app_cred_expires_str,
119            '--description', 'credential for testing'
120        ]
121        verifylist = [
122            ('name', identity_fakes.app_cred_name),
123            ('secret', 'moresecuresecret'),
124            ('role', [identity_fakes.role_id]),
125            ('expiration', identity_fakes.app_cred_expires_str),
126            ('description', 'credential for testing')
127        ]
128        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
129
130        # In base command class ShowOne in cliff, abstract method take_action()
131        # returns a two-part tuple with a tuple of column names and a tuple of
132        # data to be shown.
133        columns, data = self.cmd.take_action(parsed_args)
134
135        # Set expected values
136        kwargs = {
137            'secret': 'moresecuresecret',
138            'roles': [identity_fakes.role_id],
139            'expires_at': identity_fakes.app_cred_expires,
140            'description': 'credential for testing',
141            'unrestricted': False,
142            'access_rules': None,
143        }
144        self.app_creds_mock.create.assert_called_with(
145            name,
146            **kwargs
147        )
148
149        collist = ('access_rules', 'description', 'expires_at', 'id', 'name',
150                   'project_id', 'roles', 'secret', 'unrestricted')
151        self.assertEqual(collist, columns)
152        datalist = (
153            None,
154            identity_fakes.app_cred_description,
155            identity_fakes.app_cred_expires_str,
156            identity_fakes.app_cred_id,
157            identity_fakes.app_cred_name,
158            identity_fakes.project_id,
159            identity_fakes.role_name,
160            identity_fakes.app_cred_secret,
161            False,
162        )
163        self.assertEqual(datalist, data)
164
165    def test_application_credential_create_with_access_rules_string(self):
166        name = identity_fakes.app_cred_name
167        self.app_creds_mock.create.return_value = fakes.FakeResource(
168            None,
169            copy.deepcopy(identity_fakes.APP_CRED_ACCESS_RULES),
170            loaded=True,
171        )
172
173        arglist = [
174            name,
175            '--access-rules', identity_fakes.app_cred_access_rules,
176        ]
177        verifylist = [
178            ('name', identity_fakes.app_cred_name),
179            ('access_rules', identity_fakes.app_cred_access_rules),
180        ]
181        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
182
183        columns, data = self.cmd.take_action(parsed_args)
184
185        # Set expected values
186        kwargs = {
187            'secret': None,
188            'roles': [],
189            'expires_at': None,
190            'description': None,
191            'unrestricted': False,
192            'access_rules': json.loads(identity_fakes.app_cred_access_rules)
193        }
194        self.app_creds_mock.create.assert_called_with(
195            name,
196            **kwargs
197        )
198
199        collist = ('access_rules', 'description', 'expires_at', 'id', 'name',
200                   'project_id', 'roles', 'secret', 'unrestricted')
201        self.assertEqual(collist, columns)
202        datalist = (
203            identity_fakes.app_cred_access_rules,
204            None,
205            None,
206            identity_fakes.app_cred_id,
207            identity_fakes.app_cred_name,
208            identity_fakes.project_id,
209            identity_fakes.role_name,
210            identity_fakes.app_cred_secret,
211            False,
212        )
213        self.assertEqual(datalist, data)
214
215    @mock.patch('openstackclient.identity.v3.application_credential.json.load')
216    @mock.patch('openstackclient.identity.v3.application_credential.open')
217    def test_application_credential_create_with_access_rules_file(
218            self, _, mock_json_load):
219        mock_json_load.return_value = identity_fakes.app_cred_access_rules
220
221        name = identity_fakes.app_cred_name
222        self.app_creds_mock.create.return_value = fakes.FakeResource(
223            None,
224            copy.deepcopy(identity_fakes.APP_CRED_ACCESS_RULES),
225            loaded=True,
226        )
227
228        arglist = [
229            name,
230            '--access-rules', identity_fakes.app_cred_access_rules_path,
231        ]
232        verifylist = [
233            ('name', identity_fakes.app_cred_name),
234            ('access_rules', identity_fakes.app_cred_access_rules_path),
235        ]
236        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
237
238        columns, data = self.cmd.take_action(parsed_args)
239
240        # Set expected values
241        kwargs = {
242            'secret': None,
243            'roles': [],
244            'expires_at': None,
245            'description': None,
246            'unrestricted': False,
247            'access_rules': identity_fakes.app_cred_access_rules
248        }
249        self.app_creds_mock.create.assert_called_with(
250            name,
251            **kwargs
252        )
253
254        collist = ('access_rules', 'description', 'expires_at', 'id', 'name',
255                   'project_id', 'roles', 'secret', 'unrestricted')
256        self.assertEqual(collist, columns)
257        datalist = (
258            identity_fakes.app_cred_access_rules,
259            None,
260            None,
261            identity_fakes.app_cred_id,
262            identity_fakes.app_cred_name,
263            identity_fakes.project_id,
264            identity_fakes.role_name,
265            identity_fakes.app_cred_secret,
266            False,
267        )
268        self.assertEqual(datalist, data)
269
270
271class TestApplicationCredentialDelete(TestApplicationCredential):
272
273    def setUp(self):
274        super(TestApplicationCredentialDelete, self).setUp()
275
276        # This is the return value for utils.find_resource()
277        self.app_creds_mock.get.return_value = fakes.FakeResource(
278            None,
279            copy.deepcopy(identity_fakes.APP_CRED_BASIC),
280            loaded=True,
281        )
282        self.app_creds_mock.delete.return_value = None
283
284        # Get the command object to test
285        self.cmd = application_credential.DeleteApplicationCredential(
286            self.app, None)
287
288    def test_application_credential_delete(self):
289        arglist = [
290            identity_fakes.app_cred_id,
291        ]
292        verifylist = [
293            ('application_credential', [identity_fakes.app_cred_id])
294        ]
295        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
296
297        result = self.cmd.take_action(parsed_args)
298
299        self.app_creds_mock.delete.assert_called_with(
300            identity_fakes.app_cred_id,
301        )
302        self.assertIsNone(result)
303
304    @mock.patch.object(utils, 'find_resource')
305    def test_delete_multi_app_creds_with_exception(self, find_mock):
306        find_mock.side_effect = [self.app_creds_mock.get.return_value,
307                                 exceptions.CommandError]
308        arglist = [
309            identity_fakes.app_cred_id,
310            'nonexistent_app_cred',
311        ]
312        verifylist = [
313            ('application_credential', arglist),
314        ]
315        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
316
317        try:
318            self.cmd.take_action(parsed_args)
319            self.fail('CommandError should be raised.')
320        except exceptions.CommandError as e:
321            self.assertEqual('1 of 2 application credentials failed to'
322                             ' delete.', str(e))
323
324        find_mock.assert_any_call(self.app_creds_mock,
325                                  identity_fakes.app_cred_id)
326        find_mock.assert_any_call(self.app_creds_mock,
327                                  'nonexistent_app_cred')
328
329        self.assertEqual(2, find_mock.call_count)
330        self.app_creds_mock.delete.assert_called_once_with(
331            identity_fakes.app_cred_id)
332
333
334class TestApplicationCredentialList(TestApplicationCredential):
335
336    def setUp(self):
337        super(TestApplicationCredentialList, self).setUp()
338
339        self.app_creds_mock.list.return_value = [
340            fakes.FakeResource(
341                None,
342                copy.deepcopy(identity_fakes.APP_CRED_BASIC),
343                loaded=True,
344            ),
345        ]
346
347        # Get the command object to test
348        self.cmd = application_credential.ListApplicationCredential(self.app,
349                                                                    None)
350
351    def test_application_credential_list(self):
352        arglist = []
353        verifylist = []
354        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
355
356        # In base command class Lister in cliff, abstract method take_action()
357        # returns a tuple containing the column names and an iterable
358        # containing the data to be listed.
359        columns, data = self.cmd.take_action(parsed_args)
360
361        self.app_creds_mock.list.assert_called_with(user=None)
362
363        collist = ('ID', 'Name', 'Project ID', 'Description', 'Expires At')
364        self.assertEqual(collist, columns)
365        datalist = ((
366            identity_fakes.app_cred_id,
367            identity_fakes.app_cred_name,
368            identity_fakes.project_id,
369            None,
370            None
371        ), )
372        self.assertEqual(datalist, tuple(data))
373
374
375class TestApplicationCredentialShow(TestApplicationCredential):
376
377    def setUp(self):
378        super(TestApplicationCredentialShow, self).setUp()
379
380        self.app_creds_mock.get.return_value = fakes.FakeResource(
381            None,
382            copy.deepcopy(identity_fakes.APP_CRED_BASIC),
383            loaded=True,
384        )
385
386        # Get the command object to test
387        self.cmd = application_credential.ShowApplicationCredential(self.app,
388                                                                    None)
389
390    def test_application_credential_show(self):
391        arglist = [
392            identity_fakes.app_cred_id,
393        ]
394        verifylist = [
395            ('application_credential', identity_fakes.app_cred_id),
396        ]
397        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
398
399        # In base command class ShowOne in cliff, abstract method take_action()
400        # returns a two-part tuple with a tuple of column names and a tuple of
401        # data to be shown.
402        columns, data = self.cmd.take_action(parsed_args)
403
404        self.app_creds_mock.get.assert_called_with(identity_fakes.app_cred_id)
405
406        collist = ('access_rules', 'description', 'expires_at', 'id', 'name',
407                   'project_id', 'roles', 'secret', 'unrestricted')
408        self.assertEqual(collist, columns)
409        datalist = (
410            None,
411            None,
412            None,
413            identity_fakes.app_cred_id,
414            identity_fakes.app_cred_name,
415            identity_fakes.project_id,
416            identity_fakes.role_name,
417            identity_fakes.app_cred_secret,
418            False,
419        )
420        self.assertEqual(datalist, data)
421