Repository: softlayer/softlayer-python
Pull request #1818 (2023-01-10 21:36:54)

New Command: `slcli user grant-access`
Issue: https://github.com/softlayer/softlayer-python/issues/1816
Observations: `slcli user grant-access` command and unit tests were added
```
slcli user grant-access 8344222 --hardware 1532729 --virtual 132364928
Grant to access to hardware: 1532729
Grant to access to virtual guest: 132364928
```



Patch:
diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py
index 4acaea0a..cefb00df 100644
--- a/SoftLayer/CLI/routes.py
+++ b/SoftLayer/CLI/routes.py
@@ -376,6 +376,7 @@
     ('user:vpn-manual', 'SoftLayer.CLI.user.vpn_manual:cli'),
     ('user:vpn-subnet', 'SoftLayer.CLI.user.vpn_subnet:cli'),
     ('user:remove-access', 'SoftLayer.CLI.user.remove_access:cli'),
+    ('user:grant-access', 'SoftLayer.CLI.user.grant_access:cli'),
 
     ('vlan', 'SoftLayer.CLI.vlan'),
     ('vlan:create', 'SoftLayer.CLI.vlan.create:cli'),
diff --git a/SoftLayer/CLI/user/grant_access.py b/SoftLayer/CLI/user/grant_access.py
new file mode 100644
index 00000000..1913e4c2
--- /dev/null
+++ b/SoftLayer/CLI/user/grant_access.py
@@ -0,0 +1,45 @@
+"""Grants a user access to a given device"""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+
+from SoftLayer.CLI.command import SLCommand as SLCommand
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+
+
+@click.command(cls=SLCommand, )
+@click.argument('identifier')
+@click.option('--hardware', help="Hardware ID")
+@click.option('--virtual', help="Virtual Guest ID")
+@click.option('--dedicated', help="dedicated host ID")
+@environment.pass_env
+def cli(env, identifier, hardware, virtual, dedicated):
+    """Grants a user access to a given device.
+
+    Example: slcli user grant-access 123456 --hardware 123456789
+    """
+
+    mgr = SoftLayer.UserManager(env.client)
+    result = False
+    if hardware:
+        result = mgr.grant_hardware_access(identifier, hardware)
+        if result:
+            click.secho(f"User {identifier} has been given access to hardware {hardware}", fg='green')
+
+    if virtual:
+        result = mgr.grant_virtual_access(identifier, virtual)
+        if result:
+            click.secho(f"User {identifier} has been given access to hardware {virtual}", fg='green')
+
+    if dedicated:
+        result = mgr.grant_dedicated_access(identifier, dedicated)
+        if result:
+            click.secho(f"User {identifier} has been given access to hardware {dedicated}", fg='green')
+
+    if not result:
+        raise exceptions.CLIAbort('A device option is required.\n'
+                                  'E.g slcli user grant-access 123456 --hardware 91803794\n'
+                                  '    slcli user grant-access 123456 --dedicated 91803793\n'
+                                  '    slcli user grant-access 123456 --virtual 91803792')
diff --git a/SoftLayer/fixtures/SoftLayer_User_Customer.py b/SoftLayer/fixtures/SoftLayer_User_Customer.py
index fca4cfeb..8eb4285e 100644
--- a/SoftLayer/fixtures/SoftLayer_User_Customer.py
+++ b/SoftLayer/fixtures/SoftLayer_User_Customer.py
@@ -89,6 +89,9 @@
 removeDedicatedHostAccess = True
 removeHardwareAccess = True
 removeVirtualGuestAccess = True
+addDedicatedHostAccess = True
+addHardwareAccess = True
+addVirtualGuestAccess = True
 
 getHardware = [{
         "domain": "testedit.com",
diff --git a/SoftLayer/managers/user.py b/SoftLayer/managers/user.py
index e3d5e291..5f55f6b5 100644
--- a/SoftLayer/managers/user.py
+++ b/SoftLayer/managers/user.py
@@ -392,6 +392,36 @@ def get_overrides_list(self, user_id, subnet_ids):
 
         return overrides_list
 
+    def grant_hardware_access(self, user_id, hardware_id):
+        """Grants the user access to a single hardware device.
+
+        :param int user_id:
+        :param int hardware_id
+
+        :returns: true
+        """
+        return self.user_service.addHardwareAccess(hardware_id, id=user_id)
+
+    def grant_virtual_access(self, user_id, virtual_id):
+        """Grants the user access to a single VS device.
+
+        :param int user_id:
+        :param int virtual_id
+
+        :returns: true
+        """
+        return self.user_service.addVirtualGuestAccess(virtual_id, id=user_id)
+
+    def grant_dedicated_access(self, user_id, dedicated_id):
+        """Grants the user access to a single dedicated host device.
+
+        :param int user_id:
+        :param int dedicated_id
+
+        :returns: true
+        """
+        return self.user_service.addDedicatedHostAccess(dedicated_id, id=user_id)
+
     def remove_hardware_access(self, user_id, hardware_id):
         """Remove hardware from a portal user’s hardware access list.
 
diff --git a/docs/cli/users.rst b/docs/cli/users.rst
index 13e87764..55d0cf39 100644
--- a/docs/cli/users.rst
+++ b/docs/cli/users.rst
@@ -56,4 +56,8 @@ Version 5.6.0 introduces the ability to interact with user accounts from the cli
     :prog: user remove-access
     :show-nested:
 
+.. click:: SoftLayer.CLI.user.grant_access:cli
+    :prog: user grant-access
+    :show-nested:
+
 


Test patch:
diff --git a/tests/CLI/modules/user_tests.py b/tests/CLI/modules/user_tests.py
index 8823a987..bb4dd543 100644
--- a/tests/CLI/modules/user_tests.py
+++ b/tests/CLI/modules/user_tests.py
@@ -345,6 +345,23 @@ def test_devices_access(self):
         self.assert_called_with('SoftLayer_User_Customer', 'getDedicatedHosts')
         self.assert_called_with('SoftLayer_User_Customer', 'getVirtualGuests')
 
+    def test_grant_access_hardware(self):
+        result = self.run_command(['user', 'grant-access', '123456', '--hardware', '147258'])
+        self.assert_no_fail(result)
+
+    def test_grant_access_virtual(self):
+        result = self.run_command(['user', 'grant-access', '123456', '--virtual', '987456'])
+        self.assert_no_fail(result)
+
+    def test_grant_access_dedicated(self):
+        result = self.run_command(['user', 'grant-access', '123456', '--dedicated', '369852'])
+        self.assert_no_fail(result)
+
+    def test_grant_without_device(self):
+        result = self.run_command(['user', 'grant-access', '123456'])
+        self.assertEqual(2, result.exit_code)
+        self.assertIn('A device option is required.', result.exception.message)
+
     def test_remove_access_hardware(self):
         result = self.run_command(['user', 'remove-access', '123456', '--hardware', '147258'])
         self.assert_no_fail(result)
diff --git a/tests/managers/user_tests.py b/tests/managers/user_tests.py
index 8e910d15..a9ed5af3 100644
--- a/tests/managers/user_tests.py
+++ b/tests/managers/user_tests.py
@@ -307,6 +307,18 @@ def test_get_virtual(self):
         self.manager.get_user_virtuals(1234)
         self.assert_called_with('SoftLayer_User_Customer', 'getVirtualGuests')
 
+    def test_grant_hardware(self):
+        self.manager.grant_hardware_access(123456, 369852)
+        self.assert_called_with('SoftLayer_User_Customer', 'addHardwareAccess')
+
+    def test_grant_virtual(self):
+        self.manager.grant_virtual_access(123456, 369852)
+        self.assert_called_with('SoftLayer_User_Customer', 'addVirtualGuestAccess')
+
+    def test_grant_dedicated(self):
+        self.manager.grant_dedicated_access(123456, 369852)
+        self.assert_called_with('SoftLayer_User_Customer', 'addDedicatedHostAccess')
+
     def test_remove_hardware(self):
         self.manager.remove_hardware_access(123456, 369852)
         self.assert_called_with('SoftLayer_User_Customer', 'removeHardwareAccess')