1#   Copyright 2013 Nebula Inc.
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
16from unittest import mock
17
18from keystoneauth1 import exceptions as ks_exc
19from osc_lib import exceptions
20from osc_lib import utils
21
22from openstackclient.identity.v2_0 import user
23from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes
24
25
26class TestUser(identity_fakes.TestIdentityv2):
27
28    fake_project = identity_fakes.FakeProject.create_one_project()
29    attr = {
30        'tenantId': fake_project.id,
31    }
32    fake_user = identity_fakes.FakeUser.create_one_user(attr)
33
34    def setUp(self):
35        super(TestUser, self).setUp()
36
37        # Get a shortcut to the TenantManager Mock
38        self.projects_mock = self.app.client_manager.identity.tenants
39        self.projects_mock.reset_mock()
40
41        # Get a shortcut to the UserManager Mock
42        self.users_mock = self.app.client_manager.identity.users
43        self.users_mock.reset_mock()
44
45
46class TestUserCreate(TestUser):
47
48    fake_project_c = identity_fakes.FakeProject.create_one_project()
49    attr = {
50        'tenantId': fake_project_c.id,
51    }
52    fake_user_c = identity_fakes.FakeUser.create_one_user(attr)
53
54    columns = (
55        'email',
56        'enabled',
57        'id',
58        'name',
59        'project_id',
60    )
61    datalist = (
62        fake_user_c.email,
63        True,
64        fake_user_c.id,
65        fake_user_c.name,
66        fake_project_c.id,
67    )
68
69    def setUp(self):
70        super(TestUserCreate, self).setUp()
71
72        self.projects_mock.get.return_value = self.fake_project_c
73
74        self.users_mock.create.return_value = self.fake_user_c
75
76        # Get the command object to test
77        self.cmd = user.CreateUser(self.app, None)
78
79    def test_user_create_no_options(self):
80        arglist = [
81            self.fake_user_c.name,
82        ]
83        verifylist = [
84            ('enable', False),
85            ('disable', False),
86            ('name', self.fake_user_c.name),
87        ]
88        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
89
90        # In base command class ShowOne in cliff, abstract method take_action()
91        # returns a two-part tuple with a tuple of column names and a tuple of
92        # data to be shown.
93        columns, data = self.cmd.take_action(parsed_args)
94
95        # Set expected values
96        kwargs = {
97            'enabled': True,
98            'tenant_id': None,
99        }
100        # UserManager.create(name, password, email, tenant_id=, enabled=)
101        self.users_mock.create.assert_called_with(
102            self.fake_user_c.name,
103            None,
104            None,
105            **kwargs
106        )
107
108        self.assertEqual(self.columns, columns)
109        self.assertEqual(self.datalist, data)
110
111    def test_user_create_password(self):
112        arglist = [
113            '--password', 'secret',
114            self.fake_user_c.name,
115        ]
116        verifylist = [
117            ('name', self.fake_user_c.name),
118            ('password_prompt', False),
119            ('password', 'secret')
120        ]
121        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
122
123        # In base command class ShowOne in cliff, abstract method take_action()
124        # returns a two-part tuple with a tuple of column names and a tuple of
125        # data to be shown.
126        columns, data = self.cmd.take_action(parsed_args)
127
128        # Set expected values
129        kwargs = {
130            'enabled': True,
131            'tenant_id': None,
132        }
133        # UserManager.create(name, password, email, tenant_id=, enabled=)
134        self.users_mock.create.assert_called_with(
135            self.fake_user_c.name,
136            'secret',
137            None,
138            **kwargs
139        )
140        self.assertEqual(self.columns, columns)
141        self.assertEqual(self.datalist, data)
142
143    def test_user_create_password_prompt(self):
144        arglist = [
145            '--password-prompt',
146            self.fake_user_c.name,
147        ]
148        verifylist = [
149            ('name', self.fake_user_c.name),
150            ('password_prompt', True)
151        ]
152        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
153
154        # In base command class ShowOne in cliff, abstract method take_action()
155        # returns a two-part tuple with a tuple of column names and a tuple of
156        # data to be shown.
157        mocker = mock.Mock()
158        mocker.return_value = 'abc123'
159        with mock.patch("osc_lib.utils.get_password", mocker):
160            columns, data = self.cmd.take_action(parsed_args)
161
162        # Set expected values
163        kwargs = {
164            'enabled': True,
165            'tenant_id': None,
166        }
167        # UserManager.create(name, password, email, tenant_id=, enabled=)
168        self.users_mock.create.assert_called_with(
169            self.fake_user_c.name,
170            'abc123',
171            None,
172            **kwargs
173        )
174
175        self.assertEqual(self.columns, columns)
176        self.assertEqual(self.datalist, data)
177
178    def test_user_create_email(self):
179        arglist = [
180            '--email', 'barney@example.com',
181            self.fake_user_c.name,
182        ]
183        verifylist = [
184            ('name', self.fake_user_c.name),
185            ('email', 'barney@example.com'),
186        ]
187        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
188
189        # In base command class ShowOne in cliff, abstract method take_action()
190        # returns a two-part tuple with a tuple of column names and a tuple of
191        # data to be shown.
192        columns, data = self.cmd.take_action(parsed_args)
193
194        # Set expected values
195        kwargs = {
196            'enabled': True,
197            'tenant_id': None,
198        }
199        # UserManager.create(name, password, email, tenant_id=, enabled=)
200        self.users_mock.create.assert_called_with(
201            self.fake_user_c.name,
202            None,
203            'barney@example.com',
204            **kwargs
205        )
206
207        self.assertEqual(self.columns, columns)
208        self.assertEqual(self.datalist, data)
209
210    def test_user_create_project(self):
211        # Return the new project
212        self.projects_mock.get.return_value = self.fake_project_c
213
214        # Set up to return an updated user
215        attr = {
216            'tenantId': self.fake_project_c.id,
217        }
218        user_2 = identity_fakes.FakeUser.create_one_user(attr)
219        self.users_mock.create.return_value = user_2
220
221        arglist = [
222            '--project', self.fake_project_c.name,
223            user_2.name,
224        ]
225        verifylist = [
226            ('name', user_2.name),
227            ('project', self.fake_project_c.name),
228        ]
229        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
230
231        # In base command class ShowOne in cliff, abstract method take_action()
232        # returns a two-part tuple with a tuple of column names and a tuple of
233        # data to be shown.
234        columns, data = self.cmd.take_action(parsed_args)
235
236        # Set expected values
237        kwargs = {
238            'enabled': True,
239            'tenant_id': self.fake_project_c.id,
240        }
241        # UserManager.create(name, password, email, tenant_id=, enabled=)
242        self.users_mock.create.assert_called_with(
243            user_2.name,
244            None,
245            None,
246            **kwargs
247        )
248
249        self.assertEqual(self.columns, columns)
250        datalist = (
251            user_2.email,
252            True,
253            user_2.id,
254            user_2.name,
255            self.fake_project_c.id,
256        )
257        self.assertEqual(datalist, data)
258
259    def test_user_create_enable(self):
260        arglist = [
261            '--enable',
262            self.fake_user_c.name,
263        ]
264        verifylist = [
265            ('name', self.fake_user_c.name),
266            ('enable', True),
267            ('disable', False),
268        ]
269        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
270
271        # In base command class ShowOne in cliff, abstract method take_action()
272        # returns a two-part tuple with a tuple of column names and a tuple of
273        # data to be shown.
274        columns, data = self.cmd.take_action(parsed_args)
275
276        # Set expected values
277        kwargs = {
278            'enabled': True,
279            'tenant_id': None,
280        }
281        # UserManager.create(name, password, email, tenant_id=, enabled=)
282        self.users_mock.create.assert_called_with(
283            self.fake_user_c.name,
284            None,
285            None,
286            **kwargs
287        )
288
289        self.assertEqual(self.columns, columns)
290        self.assertEqual(self.datalist, data)
291
292    def test_user_create_disable(self):
293        arglist = [
294            '--disable',
295            self.fake_user_c.name,
296        ]
297        verifylist = [
298            ('name', self.fake_user_c.name),
299            ('enable', False),
300            ('disable', True),
301        ]
302        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
303
304        # In base command class ShowOne in cliff, abstract method take_action()
305        # returns a two-part tuple with a tuple of column names and a tuple of
306        # data to be shown.
307        columns, data = self.cmd.take_action(parsed_args)
308
309        # Set expected values
310        kwargs = {
311            'enabled': False,
312            'tenant_id': None,
313        }
314        # UserManager.create(name, password, email, tenant_id=, enabled=)
315        self.users_mock.create.assert_called_with(
316            self.fake_user_c.name,
317            None,
318            None,
319            **kwargs
320        )
321
322        self.assertEqual(self.columns, columns)
323        self.assertEqual(self.datalist, data)
324
325    def test_user_create_or_show_exists(self):
326        def _raise_conflict(*args, **kwargs):
327            raise ks_exc.Conflict(None)
328
329        # need to make this throw an exception...
330        self.users_mock.create.side_effect = _raise_conflict
331
332        self.users_mock.get.return_value = self.fake_user_c
333
334        arglist = [
335            '--or-show',
336            self.fake_user_c.name,
337        ]
338        verifylist = [
339            ('name', self.fake_user_c.name),
340            ('or_show', True),
341        ]
342        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
343
344        # In base command class ShowOne in cliff, abstract method take_action()
345        # returns a two-part tuple with a tuple of column names and a tuple of
346        # data to be shown.
347        columns, data = self.cmd.take_action(parsed_args)
348
349        # UserManager.create(name, password, email, tenant_id=, enabled=)
350        self.users_mock.get.assert_called_with(self.fake_user_c.name)
351
352        self.assertEqual(self.columns, columns)
353        self.assertEqual(self.datalist, data)
354
355    def test_user_create_or_show_not_exists(self):
356        arglist = [
357            '--or-show',
358            self.fake_user_c.name,
359        ]
360        verifylist = [
361            ('name', self.fake_user_c.name),
362            ('or_show', True),
363        ]
364        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
365
366        # In base command class ShowOne in cliff, abstract method take_action()
367        # returns a two-part tuple with a tuple of column names and a tuple of
368        # data to be shown.
369        columns, data = self.cmd.take_action(parsed_args)
370
371        # Set expected values
372        kwargs = {
373            'enabled': True,
374            'tenant_id': None,
375        }
376        # UserManager.create(name, password, email, tenant_id=, enabled=)
377        self.users_mock.create.assert_called_with(
378            self.fake_user_c.name,
379            None,
380            None,
381            **kwargs
382        )
383        self.assertEqual(self.columns, columns)
384        self.assertEqual(self.datalist, data)
385
386
387class TestUserDelete(TestUser):
388
389    def setUp(self):
390        super(TestUserDelete, self).setUp()
391
392        # This is the return value for utils.find_resource()
393        self.users_mock.get.return_value = self.fake_user
394        self.users_mock.delete.return_value = None
395
396        # Get the command object to test
397        self.cmd = user.DeleteUser(self.app, None)
398
399    def test_user_delete_no_options(self):
400        arglist = [
401            self.fake_user.id,
402        ]
403        verifylist = [
404            ('users', [self.fake_user.id]),
405        ]
406        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
407
408        result = self.cmd.take_action(parsed_args)
409
410        self.users_mock.delete.assert_called_with(
411            self.fake_user.id,
412        )
413        self.assertIsNone(result)
414
415    @mock.patch.object(utils, 'find_resource')
416    def test_delete_multi_users_with_exception(self, find_mock):
417        find_mock.side_effect = [self.fake_user,
418                                 exceptions.CommandError]
419        arglist = [
420            self.fake_user.id,
421            'unexist_user',
422        ]
423        verifylist = [
424            ('users', arglist),
425        ]
426        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
427
428        try:
429            self.cmd.take_action(parsed_args)
430            self.fail('CommandError should be raised.')
431        except exceptions.CommandError as e:
432            self.assertEqual('1 of 2 users failed to delete.',
433                             str(e))
434
435        find_mock.assert_any_call(self.users_mock, self.fake_user.id)
436        find_mock.assert_any_call(self.users_mock, 'unexist_user')
437
438        self.assertEqual(2, find_mock.call_count)
439        self.users_mock.delete.assert_called_once_with(self.fake_user.id)
440
441
442class TestUserList(TestUser):
443
444    fake_project_l = identity_fakes.FakeProject.create_one_project()
445    attr = {
446        'tenantId': fake_project_l.id,
447    }
448    fake_user_l = identity_fakes.FakeUser.create_one_user(attr)
449
450    columns = (
451        'ID',
452        'Name',
453    )
454    datalist = (
455        (
456            fake_user_l.id,
457            fake_user_l.name,
458        ),
459    )
460
461    def setUp(self):
462        super(TestUserList, self).setUp()
463
464        self.projects_mock.get.return_value = self.fake_project_l
465        self.projects_mock.list.return_value = [self.fake_project_l]
466
467        self.users_mock.list.return_value = [self.fake_user_l]
468
469        # Get the command object to test
470        self.cmd = user.ListUser(self.app, None)
471
472    def test_user_list_no_options(self):
473        arglist = []
474        verifylist = []
475        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
476
477        # In base command class Lister in cliff, abstract method take_action()
478        # returns a tuple containing the column names and an iterable
479        # containing the data to be listed.
480        columns, data = self.cmd.take_action(parsed_args)
481
482        self.users_mock.list.assert_called_with(tenant_id=None)
483
484        self.assertEqual(self.columns, columns)
485        self.assertListItemEqual(self.datalist, tuple(data))
486
487    def test_user_list_project(self):
488        arglist = [
489            '--project', self.fake_project_l.id,
490        ]
491        verifylist = [
492            ('project', self.fake_project_l.id),
493        ]
494        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
495        project_id = self.fake_project_l.id
496
497        # In base command class Lister in cliff, abstract method take_action()
498        # returns a tuple containing the column names and an iterable
499        # containing the data to be listed.
500        columns, data = self.cmd.take_action(parsed_args)
501
502        self.users_mock.list.assert_called_with(tenant_id=project_id)
503
504        self.assertEqual(self.columns, columns)
505        self.assertListItemEqual(self.datalist, tuple(data))
506
507    def test_user_list_long(self):
508        arglist = [
509            '--long',
510        ]
511        verifylist = [
512            ('long', True),
513        ]
514        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
515
516        # In base command class Lister in cliff, abstract method take_action()
517        # returns a tuple containing the column names and an iterable
518        # containing the data to be listed.
519        columns, data = self.cmd.take_action(parsed_args)
520
521        self.users_mock.list.assert_called_with(tenant_id=None)
522
523        collist = ('ID', 'Name', 'Project', 'Email', 'Enabled')
524        self.assertEqual(collist, columns)
525        datalist = ((
526            self.fake_user_l.id,
527            self.fake_user_l.name,
528            user.ProjectColumn(
529                self.fake_project_l.id,
530                {self.fake_project_l.id: self.fake_project_l}),
531            self.fake_user_l.email,
532            True,
533        ), )
534        self.assertListItemEqual(datalist, tuple(data))
535
536
537class TestUserSet(TestUser):
538
539    def setUp(self):
540        super(TestUserSet, self).setUp()
541
542        self.projects_mock.get.return_value = self.fake_project
543        self.users_mock.get.return_value = self.fake_user
544
545        # Get the command object to test
546        self.cmd = user.SetUser(self.app, None)
547
548    def test_user_set_no_options(self):
549        arglist = [
550            self.fake_user.name,
551        ]
552        verifylist = [
553            ('name', None),
554            ('password', None),
555            ('email', None),
556            ('project', None),
557            ('enable', False),
558            ('disable', False),
559            ('user', self.fake_user.name),
560        ]
561        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
562
563        result = self.cmd.take_action(parsed_args)
564
565        self.assertIsNone(result)
566
567    def test_user_set_unexist_user(self):
568        arglist = [
569            "unexist-user",
570        ]
571        verifylist = [
572            ('name', None),
573            ('password', None),
574            ('email', None),
575            ('project', None),
576            ('enable', False),
577            ('disable', False),
578            ('user', "unexist-user"),
579        ]
580        self.users_mock.get.side_effect = exceptions.NotFound(None)
581        self.users_mock.find.side_effect = exceptions.NotFound(None)
582
583        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
584
585        self.assertRaises(
586            exceptions.CommandError, self.cmd.take_action, parsed_args)
587
588    def test_user_set_name(self):
589        arglist = [
590            '--name', 'qwerty',
591            self.fake_user.name,
592        ]
593        verifylist = [
594            ('name', 'qwerty'),
595            ('password', None),
596            ('email', None),
597            ('project', None),
598            ('enable', False),
599            ('disable', False),
600            ('user', self.fake_user.name),
601        ]
602        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
603
604        result = self.cmd.take_action(parsed_args)
605
606        # Set expected values
607        kwargs = {
608            'enabled': True,
609            'name': 'qwerty',
610        }
611        # UserManager.update(user, **kwargs)
612        self.users_mock.update.assert_called_with(
613            self.fake_user.id,
614            **kwargs
615        )
616        self.assertIsNone(result)
617
618    def test_user_set_password(self):
619        arglist = [
620            '--password', 'secret',
621            self.fake_user.name,
622        ]
623        verifylist = [
624            ('name', None),
625            ('password', 'secret'),
626            ('password_prompt', False),
627            ('email', None),
628            ('project', None),
629            ('enable', False),
630            ('disable', False),
631            ('user', self.fake_user.name),
632        ]
633        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
634
635        result = self.cmd.take_action(parsed_args)
636
637        # UserManager.update_password(user, password)
638        self.users_mock.update_password.assert_called_with(
639            self.fake_user.id,
640            'secret',
641        )
642        self.assertIsNone(result)
643
644    def test_user_set_password_prompt(self):
645        arglist = [
646            '--password-prompt',
647            self.fake_user.name,
648        ]
649        verifylist = [
650            ('name', None),
651            ('password', None),
652            ('password_prompt', True),
653            ('email', None),
654            ('project', None),
655            ('enable', False),
656            ('disable', False),
657            ('user', self.fake_user.name),
658        ]
659        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
660
661        mocker = mock.Mock()
662        mocker.return_value = 'abc123'
663        with mock.patch("osc_lib.utils.get_password", mocker):
664            result = self.cmd.take_action(parsed_args)
665
666        # UserManager.update_password(user, password)
667        self.users_mock.update_password.assert_called_with(
668            self.fake_user.id,
669            'abc123',
670        )
671        self.assertIsNone(result)
672
673    def test_user_set_email(self):
674        arglist = [
675            '--email', 'barney@example.com',
676            self.fake_user.name,
677        ]
678        verifylist = [
679            ('name', None),
680            ('password', None),
681            ('email', 'barney@example.com'),
682            ('project', None),
683            ('enable', False),
684            ('disable', False),
685            ('user', self.fake_user.name),
686        ]
687        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
688
689        result = self.cmd.take_action(parsed_args)
690
691        # Set expected values
692        kwargs = {
693            'email': 'barney@example.com',
694            'enabled': True,
695        }
696        # UserManager.update(user, **kwargs)
697        self.users_mock.update.assert_called_with(
698            self.fake_user.id,
699            **kwargs
700        )
701        self.assertIsNone(result)
702
703    def test_user_set_project(self):
704        arglist = [
705            '--project', self.fake_project.id,
706            self.fake_user.name,
707        ]
708        verifylist = [
709            ('name', None),
710            ('password', None),
711            ('email', None),
712            ('project', self.fake_project.id),
713            ('enable', False),
714            ('disable', False),
715            ('user', self.fake_user.name),
716        ]
717        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
718
719        result = self.cmd.take_action(parsed_args)
720
721        # UserManager.update_tenant(user, tenant)
722        self.users_mock.update_tenant.assert_called_with(
723            self.fake_user.id,
724            self.fake_project.id,
725        )
726        self.assertIsNone(result)
727
728    def test_user_set_enable(self):
729        arglist = [
730            '--enable',
731            self.fake_user.name,
732        ]
733        verifylist = [
734            ('name', None),
735            ('password', None),
736            ('email', None),
737            ('project', None),
738            ('enable', True),
739            ('disable', False),
740            ('user', self.fake_user.name),
741        ]
742        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
743
744        result = self.cmd.take_action(parsed_args)
745
746        # Set expected values
747        kwargs = {
748            'enabled': True,
749        }
750        # UserManager.update(user, **kwargs)
751        self.users_mock.update.assert_called_with(
752            self.fake_user.id,
753            **kwargs
754        )
755        self.assertIsNone(result)
756
757    def test_user_set_disable(self):
758        arglist = [
759            '--disable',
760            self.fake_user.name,
761        ]
762        verifylist = [
763            ('name', None),
764            ('password', None),
765            ('email', None),
766            ('project', None),
767            ('enable', False),
768            ('disable', True),
769            ('user', self.fake_user.name),
770        ]
771        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
772
773        result = self.cmd.take_action(parsed_args)
774
775        # Set expected values
776        kwargs = {
777            'enabled': False,
778        }
779        # UserManager.update(user, **kwargs)
780        self.users_mock.update.assert_called_with(
781            self.fake_user.id,
782            **kwargs
783        )
784        self.assertIsNone(result)
785
786
787class TestUserShow(TestUser):
788
789    def setUp(self):
790        super(TestUserShow, self).setUp()
791
792        self.users_mock.get.return_value = self.fake_user
793
794        # Get the command object to test
795        self.cmd = user.ShowUser(self.app, None)
796
797    def test_user_show(self):
798        arglist = [
799            self.fake_user.id,
800        ]
801        verifylist = [
802            ('user', self.fake_user.id),
803        ]
804        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
805
806        # In base command class ShowOne in cliff, abstract method take_action()
807        # returns a two-part tuple with a tuple of column names and a tuple of
808        # data to be shown.
809        columns, data = self.cmd.take_action(parsed_args)
810
811        self.users_mock.get.assert_called_with(self.fake_user.id)
812
813        collist = ('email', 'enabled', 'id', 'name', 'project_id')
814        self.assertEqual(collist, columns)
815        datalist = (
816            self.fake_user.email,
817            True,
818            self.fake_user.id,
819            self.fake_user.name,
820            self.fake_project.id,
821        )
822        self.assertItemEqual(datalist, data)
823